Archive for October 2008
On a recent project I came up with a cool way to do paginating find with my resources. I was able to combine resource_controller with acts_as_xapian for very DRY searching.
I simply override the 'collection' method in the index view and I can then display a list of requests, eighteen per page, that are sorted by spec.
class RequestsController < ResourceController::Base
include SearchableResource
...
private
def collection
search_collection(:spec, {}, size=18)
end
end
The bulk of the interesting code is in the SearchableResource module that is included by all of the controllers that need searching.
# Adds a collection method to a resource controller that
# is suitable for collections that can be optionally searched
module SearchableResource
def self.included(base)
base.class_eval {
def clazz
object_name.camelize.constantize
end
def filter_criteria
return "" unless @search_filter
return " #{@search_filter.query}"
end
def search_collection(order_by, find_options = {}, page_size=6)
@search = params[:q] ||= ""
@query = @search + filter_criteria
if @query.blank?
session[:query] = nil
options = {:order => order_by, :page => {:size => page_size, :current =>params[:p], :first => 1}}
options.merge!(find_options)
@collection ||= end_of_association_chain.find(:all, options)
@collection.sort
else
session[:query] = @query
current_page = params[:p].to_i if params[:p]
current_page ||= 1
hits = ActsAsXapian::Search.new [clazz], @query, :limit => 1
@collection = PagingEnumerator.new(page_size, hits.matches_estimated, false, current_page, 1) do |page|
offset = (current_page -1) * page_size
hits = ActsAsXapian::Search.new clazz, @query, :limit => page_size, :offset => offset
# pull the models out of the results
collection = []
hits.results.each do |hit|
collection << hit[:model]
end
collection.sort
end
end
end
}
end
end
The only other trick is to modify resource_controller so that you can use the url helpers with parameters. This allows a single partial for the pagination that takes the search query as a parameter. But that's a subject for another post.