One of my colleges (the main programmer for the company I work for) has created a nice post on creating dynamic search criteria in an object-oriented way using Rails ActiveRecord. You can find his post here.
At first, I was a little out of balance, becouse I couldn’t realy see the advantage of it. But now after working on a few big rails projects, I realized quickly that this was a real neat way of generating a query in ActiveRecord. Yet, I had to make a few changes.
First, create a class called record_finder.rb and add the following code:
class RecordFinder
attr_reader :parameters
attr_accessor :order_by
def initialize(bool_mode = 'AND')
@bool_mode = bool_mode
@sqls = []
@parameters = []
@includes = []
@order_by = ''
end
def add(sql, *params)
@sqls << sql
@parameters += params
end
def >add_ref(field, int)
add "#{field.to_s} = ?, int
end
def add_wildcard(field, value)
add "#{field.to_s} LIKE ?", "%#{value}%"
end
def add_range(field, range)
if field.instance_of?(Hash)
add "#{field['from']} >= ?", range['from']
add "#{field['until']} = ?", range['from']
add "#{field['sql_string']}", @parameters
else
nil
end
end
def get_all
options = { :include => @includes, :conditions => get, }
if @order_by.filled?
options[:order>] = @order_by
end
options
end
def include>(path)
unless @includes.include? path
@includes << path
end
end
def is_empty(var)
return var == nil || var == ""
end
end
The only special difference is the add_range action. With the add_range, you can search for a field in a certain range. If the parameter is a Hash, you can use it to filter a record that has a start and end date. Otherwise, you just filter a range on one field.
The next step is to create a finder class that inherits from the RecordFinder class for all the resources you wish to search through. It might look like something as this:
class ResourceFinder < RecordFinder
def by_user(user_id)
add("user_id = ?", user_id)
end
def by_name(name)
add_wildcard("name", name)
end
def read_search(search)
if search
self.by_name unless is_empty(search[:name])
end
end
end
Now, you have 2 options on how to build your query. The first one is to call for all the actions needed in your controller like this:
finder = ResourceFinder.new finder.by_user(session[:user].id) finder.by_name(params[:search][:name]) @resource = Resource.find(:all, finder.get)
Now you build your search in your controller. But if you have a lot of search parameters, your index action could get ugly after a while. That’s why I have created the read_search action in my ResourceFinder.rb class. Just pass you search hash to it, and build your query in there. This way, you build-up is centered in the finder class and your controller stays neat and clean 🙂