How to use will_paginate with non-ActiveRecord collection/array
will_paginate is very well designed plugin. Besides ActiveRecord object integration, it can integrate with array and any collection that you may have. The README.rdoc (in version 2.2.2) and wiki clearly and concisely document how to use it with ActiveRecord objects. I recently needed to use it for a collection outside of activerecord and here is how I did it.
My input params supplied the current page and the per_page. So, I had
current_page = params[:page] per_page = params[:per_page] # could be configurable or fixed in your app
1) Fetch your collection:
In case of ActiveRecord object, we would do:
@posts = Post.paginate :page => params[:page], :per_page => 50
In case of non-ActiveRecord, the onus is still on us to fetch just the records that we need. If we can't and we need to work with full collection, will_paginate doesn't mind. So, here we go...
# Non-ActiveRecord Post @posts = Post.perform_search_and_obtain_collection(params[:criteria])
2) IMP*: Create an instance of WillPaginate::Collection class.
@page_results = WillPaginate::Collection.create(current_page, per_page, @results.total_results) do |pager| pager.replace(@posts.to_array) end
WillPaginate::Collection extends Array class and added properties like @current_page, @per_page, @total_entries etc. We pass the current_page, per_page properties in constructor and we replace the array contents by converting our @posts to an array (@posts.to_array). This is assuming that @posts contains only the collection for current-page. If @post contains _all_ results, then we can do following:
@page_results = WillPaginate::Collection.create(current_page, per_page, @results.total_results) do |pager| start = (current_page-1)*per_page # assuming current_page is 1 based. pager.replace(@posts.to_array[start, per_page]) end
If our search results were already an array, then it is even easier. All we do is:
@page_results = @posts.paginate(params[:current_page], params[:per_page])
Yes, the plugin adds a method "paginate" to Array class.
Once we have an instance of WillPaginate::Collection object, it behaves exactly same as the one we normally obtain from ActiveRecord.paginate() function. So, we can continue to do this in viewer:
<ol> <% for post in @page_results -%> <li>Render `results` in some nice way.</li> <% end -%> </ol> <p>Now let's render us some pagination!</p> <%= will_paginate @page_results %>
Also, I encourage you to peek into the source code. I found paginated_section that renders pagination stuff at the top/bottom of page.
<% paginated_section @posts do %> <ol id="posts"> <% for post in @posts %> <li> ... </li> <% end %> </ol> <% end %>
Even the rendering is done using a pluggable class, incase if you want to render page links differently.