I’ve tried to implement conditional chaining and this is what I got:
Controller index action code:
@range_start = params[:range_start]
@range_stop = params[:range_stop]
Contract.within_range(@range_start, @range_stop)
Model code:
def self.within_range(range_start = Date.today - 1.month, range_stop = nil)
self.started_after(range_start).started_before(range_stop)
end
def self.started_after(range_start)
if range_start.blank?
self
else
self.where('start_date >=?', range_start)
end
end
def self.started_before(range_stop)
if range_stop.blank?
self
else
self.where('start_date<=?', range_stop)
end
end
It works, but does not looks good. I tried to improve it a bit using tap, without success. How this code can be improved?
UPDATE: In can be converted to inline conditional, but maybe something else can be improved?
range_start.blank? ? self : self.where('start_date >=?', range_start)
UPDATE2: If range_stop is not set, this code is not really works, started_after condition does not apply.
What I have to return from started_before to do not loose first condition?
I want try to clarify the purpose of doing conditional chaining this way: the idea is to hide condition inside some method, and chain methods, so resulting method will be simple.
It is possible, but child of ActiveRecord::Base class can’t be chained itself. Only relation can be chained.
So instead of doing this:
one should do this:
Only one change:
selfwas replaced withscoped, and now method always returns a scope, and can be chained.Thank to this article for hint: http://blog.mitchcrowe.com/blog/2012/04/14/10-most-underused-activerecord-relation-methods/