Using Service Classes to improve code-quality

ServiceClasses are nothing but plain old ruby classes. But they go a long way to keep your controllers thin and models thin. To show what I mean by this, lets look at some code. This is the code in controller to download data following some set of rules.

class ImportExportsController < ApplicationController

  def multi_column_download_template
    rows = []
    header = ['Spec Number', 'place', 'measurement',
              'measurement number']

    file_name = "multi-column-template" + "_#{Time.now.to_i}.xlsx"
    file_path = "#{Rails.root}/tmp/#{file_name}"

    specs = @inspection_master.row_no_wise_specs
    specs.each do |spec|
      header << spec.id.to_s + "#" + spec.characteristic
    end
    rows << header

    feature_repeat = specs.collect(&:feature_repeats).max
    measurement    = specs.collect(&:minimum_readings_required).max

    inspection = Inspections::Inspection.new(
                   :feature_repeats => feature_repeat, 
                   :minimum_readings_required => measurement)
    place_cols = inspection.calculate_records

    @inspection_master.inspection_serial_numbers.each_with_index do |d, index|
      rows << [index+1, d.place, d.measurement, d.serial_number]      
    end
  
    generate_and_download_file(rows, file_path, file_name)
  end

  def generate_and_download_file(rows, file_path, file_name)
    #code which generate a xls file and send it as response
  end

end

This is bad. The controller should not look like this. It is not thin. It is difficult to understand. And the bigger problem which is difficult to see the moment we write code is that things will change. Believe me change is the only constant. And the cost of those changes will be much more higher. So comes in Service classes.

The ideology behind service classes is that it should have only one purpose to serve and nothing else.

And I can clearly see a purpose which our new service class should serve and that is to provide the data for our template download and nothing else. I keep the service classes under app/services folder. Also I do not think I need to iterate much on the importance of the name of the service class you choose. It goes without saying that the name should tell the purpose it is going to serve. I will name my service class MultiColumnDownload and I put it under app/services folder. This is how the service class looks like:


class MultiColumnDownload

  def initialize(inspection_master_id)
    @inspection_master = InspectionMaster.find inspection_master_id
    @file_name = "Multi-column-template-" + "_#{Time.now.to_i}.xlsx"
  end

  def file_name
    @file_name
  end

  def file_path
    "#{Rails.root}/tmp/#{@file_name}"
  end

  def run!
    initialize_header
    find_specs
    insert_data
    @rows
  end

  private

  def initialize_header
    header = ['Spec Number', 'place', 'measurement', 'measurement number']
    specs = @inspection_master.row_no_wise_specs
    specs.each do |spec|
      header << spec.id.to_s + "#" + spec.characteristic
    end
    @rows = [header]
  end

  def find_specs
    @specs = @inspection_master.specifications
  end

  def find_feature_repeats
    @specs.collect(&:feature_repeats).max
  end

  def find_minimum_readings_required
    @specs.collect(&:minimum_readings_required).max
  end

  def insert_data
    @inspection_master.inspection_serial_numbers.each_with_index do |d, index|
      @rows << [index+1, d.place, 
                d.measurement, d.serial_number]
    end
  end

end

And now our controller looks like this:


class ImportExportsController < ApplicationController

  def multi_column_download_template
    download_object = MultiColumnDownload.new(@inspection_master.id)
    rows = download_object.run!
    generate_and_download_file(rows, download_object.file_path, download_object.file_name)
  end

  def generate_and_download_file(rows, file_path, file_name)
    #code which generate a xls file and send it as response
  end
end

In the controller, I just have to instantiate an object for our newly created service class and we call this object service object. And call the run! method. Keeping our controller code very thin just the way it should be.

Now this is the code I like and proud of. Not the earlier one. Now in future if I want to change the formatting of this template or add or remove some data, I know where to look for the code and where to make changes. Also I do not have to change anything in my controller code as long as the service class returns data.

Have we reduced the number of lines of code? NO. In fact we have increased the lines of code by at least one and half times.

Have we reduced the complexity of code? YES. Now the code is easier to understand and maintain over long term.

I can only hope that you find this useful and make service objects without any fear of increasing code complexity or number of lines of code. I certainly do..!!!

(p.s. I have reduced the complexity of the controller to not deviate away from what we are trying to understand here.)

Whats new in Rails 5 ?

I watched the live stream of DHH’s Railsconf 2015 keynote yesterday and he was talking about Rails 5. Here is a quick look at the new things I found out thats coming in Rails 5.

1. Turbolinks 3

Consider you have a simple blogging application where you can add posts and comments. So there are basically 2 important sections of a blog page. One the blog post self and second are the comments. Section one is not going to change frequently. But section two which is comments will keep changing. So in turbolinks 3, you can tell the server to just update the comment section and keep the rest of the page as it is, as there is no need to refresh the whole page. This is an attempt to reduce the page load time even further and improve the experience for the user.

You can specify that this part of the page will not change by mentioning something like this “turbolink-permanent” in HTML.

2. ActionCable

This sounds interesting. This is an attempt to implement WebSockets the Rails way. Making it super easy to subscribe to a channel, sending and receiving messages over the channel.

Screen Shot 2015-04-21 at 8.06.09 pm

Screen Shot 2015-04-21 at 8.06.58 pm

The code looks like a controller code, so I think before_filters should also work with classes inheriting from ActionCable::Channel::Base.

P.S.
1. Code snippets are screenshots from DHH’s keynote live streaming.
2. This is just glimpse of what I took away from his keynote. I did not see the whole thing from the start, so I am not sure what else he talked about.

At a happy place

For the past 3 months I have been working on a side project. It is called ‘At a happy place‘. Here is the link to it, athappyplace.

The story behind this project goes on to my love for travelling and strangely more so of reading about travelling. I wanted to build a platform where folks who like writing can share their stories and who like reading can enjoy these stories.

But then there are already tons of blogs out there with this information. Yes, there are. And that is the problem. All those blogs and ’10 places to see in India’ kind-ish posts are not enjoyable to read.

I want people to write about the places they loved, the one they never wanted to leave. I want people to write about their ‘Happy Places’ which genuinely made them happy, and not because it was one of the must visit place. And such posts make for a good reading too.

I wanted to build a product which is obviously dead simple to use, but also which reflects all those things I have mentioned and I think I have achieved that with athappyplace.

The project is open-source. Here is the codebase.
Would love to hear your thoughts…!!!

Happiness quite unshared can scarcely be called happiness; it has no taste.
― Charlotte Brontë

Nested module definitions

When we define nested modules like this:

module A
  X = 1
  module B
    X = 2
    module C
      def self.print_const
        puts X
      end
    end
  end
end
  A::B::C.print_const  => 2

and when we do like this:

module A
  X = 1
  module B
    X = 2
  end
end

module A
  module B::C
    def self.print_const
      puts X
    end
  end
end
  A::B::C.print_const  => 1

Even though both method calls to print_const looks same, and should return same result. But it returns different values of the constant.

Reason:
When you do this in first case:

module A
  X = 1
  module B
    X = 2
    module C
      puts Module.nesting
    end
  end
end

and this gives:

[A::B::C, A::B, A]

So ruby looks for the constant X in the order as A::B::C, A::B, A. and it loads wherever it finds first, and as in our scenario, under module B.

And in second scenario:

module A
  X = 1
  module B
    X = 2
  end
end

module A
  module B::C
    puts Module.nesting
  end
end

and this gives:

[A::B::C, A]

Here since the nesting of the module is different, it only search in A::B::C and A, skipping A::B. So it finds constant X under module A.

Blocks, Procs & Lambdas revisited

Blocks, Procs & Lambdas are one of the most fun things in ruby but also very difficult to understand. This is my attempt to make these things a little simple for you.

What are blocks?

A piece of ruby code within a do..end or within curly braces {} is a block. For ex:

[1,2,3,'a'].select do |d|
  d.is_a?Integer
end

or

[1,2,3,'a'].select {|d| d.is_a?Integer}

We should also notice here that blocks breaks one of the most basic and yet powerful fundamentals of ruby that everything in ruby is an object. Well blocks are not objects. They are just piece of ruby code.

Now imagine when we have more than 1 array to select only the integer values. Ex:

[1, 2, 3, 'a'].select {|d| d.is_a?Integer}
['b', 'c', 5].select {|d| d.is_a?Integer}
['a', nil, 4 ].select {|d| d.is_a?Integer}

This is not very ruby like. We clearly breaks the DRY(Do not repeat yourself) rule. We are repeatedly writing the same block of code again and again. Now only if we could somehow save the block of code in a variable. Enter Proc.

What is a Proc?
In most simple way, Procs are named blocks. For ex:

p = Proc.new {|d| d.is_a?Integer}
puts p.class => Proc

So p is an object of class Proc unlike blocks.
Now we know how to save a block of code in a variable, lets use that knowledge to make our code dry.

[1, 2, 3, 'a'].select(&p)
['b', 'c', 5].select(&p)
['a', nil, 4 ].select(&p)

Then, what about lambda.

What is a lambda?
Lambda is just like a proc with very subtle differences between them. But before we get there, lets see how do we declare a lambda and how to use it. Ex:

p = lambda {|d| d.is_a?Integer}
puts p.class => Proc
[1, 2, 3, 'a'].select(&p)
['b', 'c', 5].select(&p)
['a', nil, 4 ].select(&p)

Yes, lambda and proc both belong to class Proc.

Then, is there any difference between them?

Difference between proc and lambda:

1. Proc do not care about the number of arguments passed. It assigns nil to any missing argument.
Ex:

p = Proc.new {|d| p d.class}
p.call => NilClass
p.call(3) => Fixnum

But had it been a lambda,

p = lambda {|d| p d.class}
p.call would raise
ArgumentError: wrong number of arguments (0 for 1)

, as it cares about its arguments unlike Procs.

2. when lambda is called, it returns control back to the calling method. Whereas Proc does not go back to the calling method. Ex:

def test_proc
  p = Proc.new {return 'In proc'}
  p.call
  'In test_proc method'
end
test_proc=> 'In proc'
def test_lambda
  p = lambda {return 'In lambda'}
  p.call
  'In test_lambda method'
end
test_lambda=> 'In test_lambda method'

I hope this would make understanding these concepts a little bit easier.

Recap of 2014

As I am writing this, MS Dhoni fans all over are still recovering from his sudden but timely announcement from test cricket. It has not been good few years for MS test cricket wise. But 2014 has been kind to me if not the best ever.

On professional front, I have spoken at 4 conferences. These conferences took me to Goa, Pune, Sau Paulo, Rio De Janeiro, Chicago and San Francisco. I almost made it to Argentina also, but only the sloppiness of the conference organisers stopped that from happening. But never the less, it is a decent start for someone who has never spoken at conference before. I truly realised the value of open source software. My work involves around open source, but I never appreciated the efforts of all those developers so much before. I do now. And not only this, I made it to the list of Rails contributors. I have also contributed to few other projects. And it was not just about work at office. We have had some fun moments here. Also, not a big deal, but I am the FIFA champion until the next tournament happens at Josh(yeahh… I have the bragging rights until then).

josh

On personal front, It has been full of ups and downs. I got a chance to stay with my sister for almost 2 months. That was the most special moment for me in 2014. We have not lived that long together for almost 8 years. Also had super fun with my brother-in-law. All of these made my parents really happy. I must admit, I had never thought highly of Chicago. But I like Chicago now, though San Francisco is still my favourite.

IMG_0342

As a child I always wanted to see the Golden Gate. I went to San Francisco this year. So there was no way I could have missed the golden opportunity. And I was so amazed by the beauty of it that I spent good 3-4 hours there.

IMG_20140916_145146

So all in all, 2014 was a good year. Travelled, made some friends along the way. But there is a lot that I am looking forward to in 2015. Looking forward to a new special member joining the family, sticking to my new year resolutions, and a very successful year for India.

A very Happy New Year to everyone 🙂

Cheers.!!

My New Year Resolutions in November.

It is just November. And I want to make my new year resolutions today. And I do not want to wait for new years to do things I want to do. And I want to stick to them. So here goes:

1. Understand computer networks.
2. Stick to one side-project.
3. Do more open-source contributions.
4. Read at least 4 books and at max 6 books(has to be non-technincal) until my next birthday (i.e Nov 15, 2015).

I will spend the next few days to research about the best books to read for understanding computer networks. I will also find out the first book(1st of the 4) which I want to start with. And will also decide on a side project. I already have an idea, I just have to find out if it will really solve a problem. Lets just say it is in stealth mode.
And for open-source contributions, I already know few projects I want to contribute to.
If you have any suggestions for books to read, please mention them in comments. I will be grateful.

This all sounds too much to pull-off, but if I could, it will turn out to be an awesome year.