Monday, March 24th, 2008


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.

I need to be able to assert that a response from a Rails controller doesn’t contain certain content, for example when the same action will respond to both GET and POST methods.  In my case I’m writing a register method in a LoginController and I want it to render the same view, but if the registration was successful I do not want the registration form to be displayed again and instead want some text that answers the question, “Ok, I’ve registered, what next?”

Turns out this is easy (I should have known).  I found the answer on this Cheat Sheet.  The assert_select test helper accepts an equality? parameter, so give it false and it ensures that the content does not contain the value specified in the selector parameter.

The Bible is an incredible book. No other book is like it, period. It stands to reason that no other book would or even could be like it, since it has a very unique characteristic, one that no other book has: it is written by God who created the heavens, the earth and the sea and all things that are therein (Neh.9:6 et al).

The Bible treats of nearly every topic known to man, and I never cease to be amazed at just how much it really does teach us on so many topics, even things most people would never imagine it would. Of course for thousands of years people have disagreed about what the scriptures teach. When Jesus was preaching, he often caused divisions among the people.

One such case is recorded in John 7:40-43.

40 Many of the people therefore, when they heard this saying, said, Of a truth this is the Prophet.

41 Others said, This is the Christ. But some said, Shall Christ come out of Galilee?

42 Hath not the scripture said, That Christ cometh of the seed of David, and out of the town of Bethlehem, where David was?

43 So there was a division among the people because of him.

Jesus’ teaching had caused some to say that he is the Christ, while others knew that he came out of Galilee and the scripture taught that the Christ would come out of Bethlehem, the city of David. Clearly he could not have come out of Galilee and out of Bethlehem, right? Wrong. Jesus was born in Bethlehem, his parents having gone there to be taxed (Lk.2:4,11), but they were from Nazareth, a city of Galilee, and returned there with him after he was born.

Why did God have it that Jesus would come both out of Bethlehem and Galilee?  Did he do it to confuse people?  Does it surprise you that Jesus would cause this kind of division?  It shouldn’t.  Listen to the words of Jesus himself (Lk.12:51-53).

51 Suppose ye that I am come to give peace on earth? I tell you, Nay; but rather division:

52 For from henceforth there shall be five in one house divided, three against two, and two against three.

53 The father shall be divided against the son, and the son against the father; the mother against the daughter, and the daughter against the mother; the mother in law against her daughter in law, and the daughter in law against her mother in law.

If you don’t like that, keep in mind that it’s Jesus with whom you have a problem, not me.  God puts things in his word, like the fact that Jesus both came out of Galilee and out of Bethlehem specifically for the purpose of causing division, of dividing sheep from the goats (see Isa.28:9-13)  How then are we to understand the scriptures with these kinds of snares in them? We must rightly divide the word of truth (2Ti.2:15, Isa.28:9-13), something that requires much diligent study.  If we want to know the truth, we must study the word of God in the way that he teaches us to do (see Jn.7:17) and we must search it out as one searches for gold or silver or rubies or anything else of value (Pro.2:1-5).

Searching for gold is not an easy task (especially in our day).   It requires hard work, sweat and tears, joy and pain.  Gold, silver, rubies and any other worldly treasure are of little worth compared to the true riches which are in Christ (Job 28:8; Pr.3:15,8:11,20:15, Eph.3:8, Php.4:19).