Refactoring: Implementing filtering June 16th, 2008
For a project at Openminds we needed an implementation for filtering some data from a list with the aid of checkboxes. Our first solution looked like this:
def index session[:order_filter] = params[:filter] if params[:filter] session[:order_filter] ||= { :active => '1', :inactive => '1' } if session[:order_filter][:active] == '0' && session[:order_filter][:inactive] == '0' @orders = [] elsif session[:order_filter][:active] == '0' @orders = @user.orders.find(:all, :conditions => {:state => 'active'}) elsif session[:order_filter][:inactive] == '0' @orders = @user.orders.find(:all, :conditions => {:state => 'inactive'}) else @orders = @user.orders.find(:all) end end
So this code takes the filter-parameters from params, or initializes them from defaults, and then does the correct finds.
In the next iteration, I stumbled onto this code and thought to myself “This can be done better”. After making sure the index action was sufficiently covered with tests, I had a go at it, this is what I came up with:
def list if filter.active? && filter.inactive? @orders = @user.orders.all elsif filter.active? @orders = @user.orders.active elsif filter.inactive? @orders = @user.orders.inactive else @orders = [] end end protected def filter return @filter if @filter session[:order_filter] = params[:filter] if params[:filter] session[:order_filter] ||= { :active => '1', :inactive => '1' } @filter = SearchFilter.new("Order", session[:order_filter]) end helper_method :filter # search_filter.rb class SearchFilter attr_reader :name def initialize name, hash @name = name hash.each do |key, value| instance_eval <<METHOD def #{key}? #{value} == 1 end METHOD end end end
This code is more readable in my opinion. I extracted the session-storing and parameter-juggling to the filter-method, which then returns a SearchFilter object. Thanks to SearchFilter I can now query filter for set values in a nice way. I can also call filter in my views, which is nicer then calling session[:order_filter][:active]. I also used named_scope instead of doing the finds manually, which improves readability a lot too!
How would you refactor this?
l 2 comments »