WideFix tech post

Returning method

Have you ever write code on Ruby On Rails with this use cases?

  1. Initialize object which will contain result of the method (the resulting object)
  2. Change the resulting object with some operations and the last operation might be nil
  3. Retun the resulting object from the first step. You should do it because you have to return not result of last operation and object which you manipulated with, ie the resulting object

There are a lot of ‘the resulting object’ references. Don’t they?

If your answer is yes this post is especially for you.

#How to use clean up the code?

Assume that you have method like I have already written above:

def my_action
  res = []
  res << 'item 1'
  res << 'item 2'
  #…some code like two lines above
  res << 'item n'
  res
end

The is a perfect method in Ruby On Rails - returning. According rdoc we can clean up our code like this:

def my_action
  returning res = [] do
    res << 'item 1'
    res << 'item 2'
    #…some code like two lines above
    res << 'item n'
  end
end

or like this:

def my_action
  returning [] do |res|
    res << 'item 1'
    res << 'item 2'
    #…some code like two lines above
    res << 'item n'
  end
end

The result of method my_action will be modified array. It doesn’t matter which approach to use: first or second. Choose which one you would like. So this code looks a little bit better then repetitive code which was before.

Fail

But there is a one pitfall. Let’s try to use it like this:

class Order < ActiveRecord::Base
  # ...
  def self.search(params)
    returning orders = self do
      orders = orders.scoped(:conditions => {:service_type => params[:service_type]}) if params[:service_type].present?
      orders = orders.scoped(:conditions => ['orders.created_at >= ?', params[:date_range_start]]) if params[:date_range_start].present?
      orders = orders.scoped(:conditions => ['orders.created_at <= ?', params[:date_range_end]]) if params[:date_range_end].present?
    end
  end
  # ...
end

You will wonder that it doesn’t work as code like in my_action! We have changed reference for local object orders. That’s why we will have unexpected result: self instead of scope object (assume we passed :service_type parameter for instance, ie we called method: Order.search(:service_type => ‘test’)). We can use method returning here, so right code should be this:

# ...
def self.search(params)
  orders = self
  orders = orders.scoped(:conditions => {:service_type => params[:service_type]}) if params[:service_type].present?
  orders = orders.scoped(:conditions => ['orders.created_at >= ?', params[:date_range_start]]) if params[:date_range_start].present?
  orders = orders.scoped(:conditions => ['orders.created_at <= ?', params[:date_range_end]]) if params[:date_range_end].present?
  orders
end
# ...

Conclusion

So the conclusion of this post is to use method returning if you have initialized one object and do some changes and then return it. But be attentive: If you change reference for returning object to new object you will have result with old one.

Attention! Method returning provided ONLY Ruby On Rails, but NOT Ruby!

Are you seeking assistance with Ruby on Rails development?

Read also