ActiveRecord associations are magical, and if you embrace the sundry offerings of finder plugins the world of parameterized queries becomes a vast and wondrous landscape indeed. So how do Rails developers typically organize and build dynamic queries without drenching their code in switch blocks, multiplying filter methods on the model, or contriving vestigial routes for search- and report- like GETs?
My sense from reading various blog posts and git repos is that most people factor such logic out of the controller, which at least consolidates the complexity further upstream, and then resort to a variety of Ruby syntax and Rails convenience methods as required by the query. This is the approach I’ve always taken, and because I don’t think there is a better way to generalize the solution that isn’t already represented in the framework, I’m posting an example finder method as a sort of syntax reference.
What follows is a pretty typical case. This method is using searchlogic, geokit, and acts-as-taggable-on, as well as named_scope and some basic pagination. Ruby’s Hash is the real hero here and gives us most of the tools needed to keep such methods readable.
class << self
def find_with_options(opt={})
opt = opt.slice(:tags, :search, :sw_lat, :sw_lng, :ne_lat, :ne_lng, :limit, :offset)
conditions = {}
unless opt[:sw_lat].blank? || opt[:sw_lng].blank? || opt[:ne_lat].blank? || opt[:ne_lng].blank?
sw_point = GeoKit::LatLng.new(opt[:sw_lat],opt[:sw_lng])
ne_point = GeoKit::LatLng.new(opt[:ne_lat],opt[:ne_lng])
conditions[:bounds] = [sw_point,ne_point]
end
conditions[:limit] = opt[:limit] ||= 50
conditions[:offset] = opt[:offset] ||= 0
scope = MyModel.include_an_associated_model
scope = scope. associated_model _name_like(opt[:search]) unless opt[:search].blank?
scope = scope.tagged_with(opt[:tags], :on => : tags) unless opt[:tags].blank?
scope.all conditions
end
end
