6.1. Adding Pagination

As seen before, when you visit / or /articles all the currently published posts are shown. If you are a regular blogger you'll probably post at least an article each week. At that pace, the front page would soon become an exceedingly long scroll of content. To work around this issue, you are going to add pagination, which is the ability to define a set number of articles that will appear per page. Here it is assumed that you want to display only the latest post on the homepage.

You could implement this feature yourself, given that ActiveRecord's finders allow you to specify :limit and :offset conditions for SELECT queries, but why should you reinvent the wheel? Pagination is an extremely common requirement for Web applications, and several plugins are available that can be readily used.

In earlier versions of Rails, pagination was a feature that was included by the framework, but its implementation and performance were less than satisfactory, so it has since been removed.

6.1.1. Installing the will_paginate Plugin

The most commonly used pagination plugin is will_paginate. This excellent plugin makes adding pagination to your applications a joy. The recommended way of installing it is through RubyGems. The first thing that you need to do, then, is to add a requirement for the gem in configenvironment.rb.

Add to that file the following line:

config.gem 'mislav-will_paginate', :version => '>= 2.3.2',
                                   :lib => 'will_paginate',
                                   :source => 'http://gems.github.com'

This should be placed after the existing config.gem line in the Rails::Initializer.run block.

You need to specify the :lib key/option because the name of the gem is mislav-will_paginate, whereas the name of the actual library that needs to be loaded is will_paginate. GitHub is a very popular and highly appreciated site for Ruby projects and libraries, and as such it's common for many gems and plugins to be hosted there.

Not all plugins are available as gems, and many of them need to be installed by running ruby script/plugin install http://example.org/repository/name_plugin, which copies the plugin to the vendorplugins directory of the project.

So far, you've only specified what's required for loading a specific gem when the HTTP server starts up; you've yet to actually install the gem though. Let's do that next.

From the main directory of your project, run rake gems:install. You should see a reassuring message that confirms that the gem was correctly installed. Now you can restart Mongrel, wherein you shouldn't see any warnings about missing gems.

6.1.2. Using the Plugin

Now that the plugin is properly installed, you'll need to do two things. The first is to specify the number of articles per page that you'd like to retrieve. The second is to modify the index.html.erb template so that it contains a page menu that will allow you to navigate between pages. The will_paginate plugin makes both of these steps incredibly straightforward.

You're interested in paginating the main page of your site, so you'll need to modify the finder used in the index action of the Articles controller. The plugin you just installed adds a paginate class method to ActiveRecord::Base, which is essentially a finder with two additional options: :page and :per_page.

Replace the existing finder in the index action with the following:

@articles = Article.published.paginate(:page =< params[:page],
                                       :per_page =< 1,
                                       :order =< "published_at DESC")

With that line you specified that only one record per page should be fetched. The method paginate knows which record should be retrieved based on the :per_page setting and the page parameter (with 1 being the default for page if no parameter is given). As you can verify by visiting http://localhost:3000/, in this case loading the homepage would lead to the retrieval of only the last published, non-scheduled article.

Don't forget to have the Web server running with ruby script/server.

Upon visiting http://localhost:3000/?page=2 in your browser, you'd assign the value 2 to the page parameter, trigger the index action, and obtain the second page that should be rendered. Because you specified that you'd like to see only one article per page (with :per_page => 1), that request will lead you to the second record. Had you specified :per_page => 5, the second page would have displayed records from the 6th to the 10th post (based on the order condition "published_at DESC").

Before proceeding to add a convenient pagination menu to move between pages, let's think this one through. Is the index action the only action that should be paginated? It could be argued that any page that needs to display a series of articles should have pagination. In this scenario, this assumption leads us to consider pagination for the unpublished action as well.

Replace the existing finder in the unpublished action with the following:

@articles = Article.unpublished.paginate(:page => params[:page], :per_page => 1,
:order => "published DESC, published_at DESC")

If you now load http://localhost:3000/articles/unpulished in your browser, you'll see only the latest unpublished article.

The problem with what you just did is that you need to repeat how many records you want per page in both actions. With only two existing actions, this is not a big violation of the DRY principle, but every time you need to add a new action that's able to display a collection of articles, you'll face this same repetition (assuming that they all share the same per_page setting).

Truth be told, some may opt for a larger number of articles per page for the unpublished page, in which case no repetition occurs when specifying the per_page option.

6.1.2.1. Setting per_page in the Model

Thankfully, the will_paginate plugin provides a way to specify this option model-wide. All you need to do is define a class method called per_page that returns the number of records that you want to render on each page.

Sometimes you'll see code that looks like the following:

cattr_reader :per_page
@@per_page = 1

This works too. cattr_reader is a method provided by ActiveSupport, which adds the equivalent of attr_reader to class variables. Using the preceding code in a class will define a class method and an instance method called per_page. Both will return the value of the class variable @@per_page.

You can improve your code slightly then, so go ahead and change your Article model by adding the following class method to its definition:

def self.per_page
  1
end

Having defined the per_page option in the model, you can now remove it from the two actions. The "final" finders appear as follows:

# In index
@articles = Article.published.paginate(:page => params[:page], :order =>
"published_at DESC")

# In unpublished
@articles = Article.unpublished.paginate(:page => params[:page], :order => "published DESC,
published_at DESC")

6.1.2.2. Adding a Pagination Menu to the View

Now that the model and controller have done their part, you'll need to modify the view layer to include a pagination menu. Edit the index.html.erb template (used by both index and unpublished) so that it looks like the following:

<%= render :partial => @articles %>
<%= will_paginate @articles %>

Save the file and reload the homepage; you should see the pagination menu at the bottom as shown in Figure 6-1. The number of pages will depend on the quantity of published articles that already exist.

Figure 6.1. Figure 6-1

If you visit http://localhost:3000/articles/unpublished you can obtain a similar menu that may have a different number of pages, depending on the number of scheduled and drafted articles.

The plugin repository for the plugin is located at http://github.com/mislav/will_paginate/. Within the file examples/pagination.css are a few predefined CSS classes that you can use to customize the look of the output of the will_paginate helper.

Try to think about what you just did and you'll realize how adding pagination was very straightforward and much easier than what you would have to do if you were implementing it in ASP or ASP.NET.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.35.247