Aron posted this about 1 year ago | 3 comments
Sure, will_paginate can be modified and customized pretty much any way you want, and we've done so many, many times. But for me, it always felt a bit too abstracted, a bit too opaque.
Enter Paged Scopes, a clever little gem that makes pagination a whole lot cleaner and clearer in my opinion.
What the gem does is very simple: It extends ActiveRecord's already incredibly powerful named scopes by adding pagination-specific objects, attributes and methods. Instead of returning a collection of ActiveRecord objects directly, paged scope figures out just which records are needed for the page requested and returns them lovingly swaddled inside a page object.
So, for example, in this blog, I have a named scope that tells me which posts I have flagged as ready to publish. In my Post model:
1 2 |
named_scope :published, :conditions => {:published => true } |
That allows me to simply call Post.published, and gives me back only those posts where the published flag is true. But what if I only want three posts to appear on any given page? Easy. In my controller:
1 2 3 |
posts = Post.published posts.per_page = 3 @page = posts.pages.first |
Instead of returning a collection of ActiveRecord objects, paged scope tucks them inside of a lager page object, which, in this case, I am passing to the view inside the @page variable. This confused me at first, but once you understand what's going on, it's really very simple.
Check out Matt's blog post for more, but fetching posts this way becomes easy as pie because the page object, as I said, provides a number of powerful pagination-specific methods to make things easy.
For example, to get the last page of posts, I would simply call posts.pages.last. To get a specific page of posts -- say posts four through six -- I would call posts.pages.find(2).
How easy is that?
To get at the posts collection is also easy: it will be an attribute of the page object with the same name of the parent class. For our example, the page object contains a collection of Post objects which is accessible by calling posts.pages.find(2).posts. If they were instances of the Donkey model, you would call posts.pages.find(2).donkeys.
Nice.
Unlike will_paginate, paged scopes provides no out-of-the-box helper to generate pagination links in your view comparable to <%= will_paginate @posts %>. Here it is strictly DIY. But, that doesn't turn out to be such a bad thing, really, since most of our sites require extensive customization anyway.
Generating pagination links requires a bit more work, but not a lot. And paged scopes makes customization a whole lot easier.
As I said at the top, the page object provides a number of pagination-specific methods, including "next" and "previous" which do precisely what you think they would. To generate next/previous links for my blog, I defined a page parameter in my controller, created a new route and generated the appropriate links in my view. For example:
1 2 3 4 |
link_to('Previous', :controller => 'posts', :action => 'index', :page => @page.previous.to_param) |
That creates a nice, clean link that looks like: /posts/page/2, then passes the number of previous page of posts to the controller as part of the params hash, which can then be passed to the pages.find method and off you go again.
Although I've been playing with it for literally a matter of hours, there's a lot to love about Matt's approach here. He said he wanted to build something lightweight that's easy to work with and highly customizable, and from what I've seen so far, he's done just that. Give it a try.


Comments
Hey there Aron. Thanks for writing about my gem! I appreciated it. There's actually a bit more to the gem that I haven't covered yet. (In particular, a nice helper for rendering page link.) I've got a couple more articles about it lined up for my blog, but in the meantime the README on GitHub has the details. (http://github.com/mholling/paged_scopes)
Matthew posted this about 1 year ago
Idiomatic and figurative language. ,
Bob85 posted this 11 months ago
What threw me off was that I was applying for a software trainer position. ,
Bob52 posted this 10 months ago