So I needed to paginate large collection of data in a new app I am working on. will_paginate is a good drop in replacement for the the default rails paginator. But I am also using acts_as_taggable_on_steroids (for tagging) and acts_as_ferret (for searching), so i needed special pagination for those scenarios.
acts_as_ferret
A quick Google search led me to this for paginating acts_as_ferret search results. I modified the offset calculation and ended up with this:
module ActsAsFerret
module ClassMethods
def paginate_search(query, options = {})
options, page, per_page = wp_parse_options!(options)
offset = (page.to_i - 1) * per_page
options.merge!(:offset => offset, :limit => per_page)
result = result = find_by_contents(query, options)
returning WillPaginate::Collection.new(page, per_page, result.total_hits) do |pager|
pager.replace result
end
end
end
end
Drop that in a file lib/ferret_pagination.rb, require it in you environment.rb, and you can now do this in your controller:
@entries = Entry.paginate_search params[:query],
:page => params[:page],
:per_page => 20
acts_as_taggable (on steroids)
So with that out of the way, I was now ready to tackle paginating entries tagged with a certain tag. Another quick google search turned up some ideas in the will_paginate comments. I used this one as a starting point and this is what I ended up with:
module ActiveRecord
module Acts #:nodoc:
module Taggable #:nodoc:
module SingletonMethods
# Return the number of time this class has been tagged with this tag
def tagging_counts(tag)
count_by_sql("select count(*) FROM tags, taggings WHERE " + sanitize_sql(['tags.name = ? AND tags.id = taggings.tag_id AND taggings.taggable_type = ?', tag, name]))
end
# paginate a call to find_tagged_with
# tag is the tag to find
# options is the option to use for pagination (:page, :per_page) and for find_tagged_with
def paginate_by_tag(tag, options = {})
options, page, per_page = wp_parse_options!(options)
offset = (page.to_i - 1) * per_page
options.merge!(:offset => offset, :limit => per_page.to_i)
items = find_tagged_with(tag, options)
count = tagging_counts(tag)
returning WillPaginate::Collection.new(page, per_page, count) do |p|
p.replace items
end
end
end
end
end
end
Again, drop that in a file lib/taggable_pagination.rb, require it in you environment.rb, and you can now do this in your controller:
@entries = Entry.paginate_by_tag @tag.name,
:order => 'entries.created_at DESC',
:page => params[:page],
:per_page => 20
Thanks
Thanks to Brandon for posting the ferret pagination code, Jim for the acts as taggable pagination code, and PJ for the will_paginate code.
UPDATED: Corrected problem noted in comments