Зная способы улучшить скромный быт разработчика, мне порой до слез больно смотреть, как люди корячатся над простейшими задачами. Одной из таких задач является вывод коллекции объектов (новостей, картинок, товаров в каталоге) с разбивкой по страницам.

Чаще всего эта задача реализуется через стандартный модуль Pagination, встроенный в Rails. К счастью, в Edge эта безумная кувалда вынесена в отдельный плагин, дабы не смущать умы честных разработчиков. Стандартный Pagination невероятно медленный и громоздкий. Слишком много ненужного хлама вокруг достаточно простой задачи. Есть гораздо более удобное решение задачи разбивки объектов по страницам - плагин will_paginate.

Использование

В плагине will_paginate вся логика разбивки коллекции по страницам вынесена в модель. Вместо того, чтобы создавать отдельно объект для страниц и отдельно для коллекции данных, will_paginate при выборке создает прокси-объект, который одновременно отвечает и за страницы, и за данные.

Выборка данных выглядит следующим образом:

@posts = Post.paginate :page => params[:page]

В переменную @page будет выбрана коллекция объектов, расположенных на странице с номером params[:page]. С этой переменной можно работать точно так же, как с массивом объектов, выбранных методом find. Однако, кроме стандартных методов коллекции, у этой переменной добавляются следующие методы:

  • current_page - текущая страница (по умолчанию 1)
  • per_page - количество объектов на страницу (по умолчанию 30)
  • total_entries - общее число объектов на всех страницах
  • page_count - общее число страниц
  • offset - текущее смещение (на первой странице смещение равно 0)
  • previous_page - номер предыдущей страницы
  • next_page - номер следующей страницы

Методу paginate можно передавать параметры точно так же, как методу find:

@posts = Post.paginate :conditions => ["publicated = ?", true], 
                       :order => "date", 
                       :page => params[:page]

Кроме того, можно использовать "магические" методы paginate_by_*:

@posts = Post.paginate_by_publicated(true, :order => "date", :page => params[:page])

Выборки данных в этом случае будут осуществляться с помощью методов find_all_by_*. В данном примере с помощью find_all_by_publicated.

Мало того, выборку можно делать не только для класса, но и для связанных объектов (например, через has_many):

@posts = @user.posts.paginate :page => params[:page]

По умолчанию коллекция разбивается на страницы по 30 объектов, однако это значение можно изменить, задав для класса переменную per_page:

class Post < ActiveRecord::Base
  cattr_reader :per_page
  @@per_page = 10
end

или

class Post < ActiveRecord::Base
  def self.per_page
    return 10
  end
end

Вывод данных и нумератора страниц в шаблонах реализуется более чем просто:

<h1>Мои записи</h1>
<% @posts.each do |post| %>
  <p><%= post.title %></p>
<% end %>

<b>Страницы:</b>
<%= will_paginate @posts %>

Собственно, это всё :) Согласитесь, гораздо проще, чем дефолтный Pagination.

Установка

ruby script/plugin install svn://errtheblog.com/svn/plugins/will_paginate