I got tired of seeing the deprecation warnings about the old Rails pagination helpers, so I decided to seek out a better approach, but of course I wasn’t going to implement it myself. With all of the skilled Rails pros out there, I had every intent of leveraging someone’s else’s success.
I succeeded, thanks to Ilya Grigorik and Alex Wolfe. Ilya wrote “Faster Pagination in Rails” which leverages Alex’ paginating_find plugin to provide limited result sets at the database level. Ilya’s post includes code he wrote to provide a neat and tidy presentation of the pagination links.
It only took me a few hours to get it working. It should not have taken this long, but I’m notorious for making things harder than they need to be. In this case, after I grabbed the code from Ilya’s post and installed the paginating_find plugin for my application, I modified my view to use Ilya’s helper. This is where it all went wrong. I was about to do something like this
<%= render :partial => ‘shared/pagination’ %>
but instead did this
<%= windowed_pagination_links(@results) %>
As it turns out neither one of these is correct, but the second one is even more incorrect than the first and led me down a bad path for a couple hours (just what every developer hates). Since I don’t ever want to repeat that mistake again, and perhaps someone else out there is having this same trouble, I’m writing about it.
What I saw in my functional tests after modifying my view as above (and the associated controller) was
ActionView::TemplateError: undefined method `page’ for #
On line #22 of app/views/sell/list.rhtml
This frustrated me for hours because I couldn’t figure out why it didn’t recognize the page method. I checked the code, double-checked Ilya’s blog (and many comments) and checked my code to confirm that I was specifying the :page option to the find method. Sure enough. Here’s what my controller had
@stuff = Listing.find_by_all_user(session[:user_id],
:page => {:size => 10, :current => params[:page]})
Now if you know about paginating_find, you may notice a problem with the code above. But since paginating_find was brand new to me, I didn’t notice it. I’ll explain the problem here shortly. First let me explain the problem with my view.
What I really needed to have is
<%= render(:partial => ‘shared/paginate’, :locals => {:collection => @stuff}) -%>
So it turns out that I started out on the right path, but left off a critical piece, which is the passing of the controller-returned collection @stuff to the partial so it can do it’s rendering. If I had even once executed the code using this render(:partial) approach, I’d have been at least on the right path.
Once I got this figured out, I still had problems. My controller test was still failing with an error like this
ActionView::TemplateError: undefined method `page_count’ for #
On line #2 of app/views/shared/_paginate.rhtml
I returned to Ilya’s blog and read more comments, re-reviewed the entry itself and didn’t see what I was doing wrong. Then I looked more closely and noticed that all of the references to the use of the paginating_find plugin were using the find() method of any given model. So I thought perhaps it’s called paginating_find for a reason, that being that it works only with the find() method, not with the Rails dynamic methods find_by_XXX and find_all_by_XXX. But I was using find_all_by_user (where user is the property of my model that I wanted to find stuff by.
So a quick update to my code fixed the problem and all tests are passing.
@stuff = Stuff.find(:all, :conditions => ['user=?', session[:user_id]],
:page => {:size => results_per_page, :current => params[:page], :first => 1})
Now I’m a happy camper on the Rails.