Module: WillPaginate::Finders::Base

Included in:
ActiveRecord, ActiveResource, DataMapper
Defined in:
lib/will_paginate/finders/base.rb

Overview

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:

  • ActiveRecord

  • DataMapper

  • Sequel

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.

Instance Method Summary collapse

Instance Method Details

#paginate(*args, &block) ⇒ Object

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.



58
59
60
61
62
63
64
65
66
# 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) ⇒ Object

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.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
# 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_pageObject



39
40
41
# File 'lib/will_paginate/finders/base.rb', line 39

def per_page
  @per_page ||= 30
end

#per_page=(limit) ⇒ Object



43
44
45
# File 'lib/will_paginate/finders/base.rb', line 43

def per_page=(limit)
  @per_page = limit.to_i
end