module WillPaginate::Finders::Base

Database-agnostic finder module

Out of the box, will_paginate supports hooking in several ORMs to provide paginating finders based on their API. As of this writing, the supported libraries are:

It’s easy to write your own adapter for anything that can load data with explicit limit and offset settings. DataMapper adapter is a nice and compact example of writing an adapter to bring the paginate method to DataMapper models.

The importance of SQL’s ORDER BY

In most ORMs, :order parameter specifies columns for the ORDER BY clause in SQL. It is important to have it, since pagination only makes sense with ordered sets. Without the order clause, databases aren’t required to do consistent ordering when performing SELECT queries.

Ordering by a field for which many records share the same value (e.g. “status”) can still result in incorrect ordering with some databases (MS SQL and Postgres for instance). With these databases it’s recommend that you order by primary key as well. That is, instead of ordering by “status DESC”, use the alternative “status DESC, id DESC” and this will yield consistent results.

Therefore, make sure you are doing ordering on a column that makes the most sense in the current context. Make that obvious to the user, also. For perfomance reasons you will also want to add an index to that column.

Public Instance Methods

paginate(*args, &block) click to toggle source

This is the main paginating finder.

Special parameters for paginating finders

  • :page – REQUIRED, but defaults to 1 if false or nil

  • :per_page – defaults to CurrentModel.per_page (which is 30 if not overridden)

  • :total_entries – use only if you manually count total entries

  • :count – additional options that are passed on to count

  • :finder – name of the finder method to use (default: “find”)

All other options (conditions, order, …) are forwarded to find and count calls.

# File lib/will_paginate/finders/base.rb, line 58
def paginate(*args, &block)
  options = args.pop
  page, per_page, total_entries = wp_parse_options(options)

  WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
    query_options = options.except :page, :per_page, :total_entries
    wp_query(query_options, pager, args, &block)
  end
end
paginated_each(options = {}, &block) click to toggle source

Iterates through all records by loading one page at a time. This is useful for migrations or any other use case where you don’t want to load all the records in memory at once.

It uses paginate internally; therefore it accepts all of its options. You can specify a starting page with :page (default is 1). Default :order is "id", override if necessary.

Jamis Buck describes this and also uses a more efficient way for MySQL.

# File lib/will_paginate/finders/base.rb, line 78
def paginated_each(options = {}, &block)
  options = { :order => 'id', :page => 1 }.merge options
  options[:page] = options[:page].to_i
  options[:total_entries] = 0 # skip the individual count queries
  total = 0
  
  begin 
    collection = paginate(options)
    total += collection.each(&block).size
    options[:page] += 1
  end until collection.size < collection.per_page
  
  total
end
per_page() click to toggle source
# File lib/will_paginate/finders/base.rb, line 39
def per_page
  @per_page ||= 30
end
per_page=(limit) click to toggle source
# File lib/will_paginate/finders/base.rb, line 43
def per_page=(limit)
  @per_page = limit.to_i
end