I've been playing around with sorting and scoping to neatly and easily sort and filter an index page. I'm finally happy with a generic set of scoping/sorting methods I can quickly apply to a new resource. It uses the autoscope plugin I discussed earlier, as well as the smart column sorting I played with.
Scope up
To start with, drop some scoping into your model object - and add the three required methods below (with suitably appropriate modifications for your purposes).
auto_scope \
:active => {:find => {:conditions => ['destroyed_at IS NULL']}},
:archived => {:find => {:conditions => ['destroyed_at IS NOT NULL']}}
# Available scopes - shouldn't this be available via autoscope?
def self.scopes
[:all, :active, :archived]
end
# Defaults (put into the model object)
def self.default_scope
:active
end
def self.default_sort
'(destroyed_at IS NULL) DESC, login ASC'
end
Next, drop these methods into your application.rb
# Generate a quick-and-dirty description of the chosen sort order (for displaying in the template)
def sort_desc
"#{params[:dir] == 'down' ? 'descending' : ''} by #{params[:col_name] || params[:col]}"
end
# Generates a SQL order-by snippet based on requested sort criteria (or given default).
#
# Adapted from the following blog post:
# http://garbageburrito.com/blog/entry/447/rails-super-cool-simple-column-sorting
def sort_order(model, default)
orderby = "#{params[:col]} #{params[:dir] == 'down' ? 'DESC' : 'ASC'}"
return sort_desc, orderby
end
# Uses model scoping to generate a scoped subset of the required objects
# for use in the index view.
# Returns a sorted list of the object .
# Assumes existance of three methods on the model:
# scopes:: returns an array of acceptable scopes for this model
# default_scope:: returns the scope to use when none has been selected
# default_sort:: represents an SQL-appropriate string for this model
# representing the default way of sorting this model object
def scoped_search(model, scope)
scope = model.default_scope unless model.scopes.include?(scope.to_sym)
sort_desc, orderby = sort_order(model, model.default_sort)
# a description of the search/sort to display in the view
filter_desc = "#{scope.to_s} #{model.name.pluralize.downcase} #{sort_desc}"
return filter_desc, model.find(:all, :order => orderby) if scope.to_sym == :all
return filter_desc, model.send(scope.to_sym).find(:all, :order => orderby)
end
Using it is now easy. Drop something like this into your controller and call on it for all your collection-based actions.
# applies user-specified filters and sorting to the specified collection
def filter_users
scope = params[:user_scope].to_sym if params[:user_scope]
@filter_desc, @users = scoped_search(User,scope)
end
If you need to paginate, you can always paginate-collection, or add the code into the scoped_search function.
No comments:
Post a Comment