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.

blog comments powered by Disqus