< Back
Paged Scopes == Awesome

Aron posted this about 1 year ago | 3 comments

I've never really loved the will_paginate plugin. Sure, it works right out of the box. And if you stick to the defaults, it's falling-down simple. But that's always been my problem with it: What if I don't want the defaults?

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

About

Aron's Head in Death Valley

I'm Aron Pilhofer and this here is my website. And by website, I really do mean mine.

I wrote it from scratch in Ruby on Rails. I did most of the CSS, so if it's not semantic and standards-based, now you know why. I started with a lovely two-column layout from Matthew James Taylor and went from there.

I also work for The New York Times. If you want more of a bio, check my Linkedin profile or find me on Twitter. You can contact me directly at: aron [at] nytimes [dot] com

Smart People

Derek Willis
Chase Davis
Matt Waite
Charles Brian Quinn
Jacob Harris

Projects

What's Saving Journalism?
EZPolls
Fuel Dump
Scraping

Training

Rails Class, ONA

Feed

IE6 Sucks