Have you ever write code on Ruby On Rails with this use cases?
There are a lot of 'the resulting object' references. Don't they?
If your answer is yes this post is especially for you.
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.
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
# ...
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.
Andrey Koleshko 11 April 2012Attention! Method returning provided ONLY Ruby On Rails, but NOT Ruby!