Kaminari recipes
Here are tons of tips and howtos for Kaminari users!
Kaminari internally uses AR's limit
and offset
. That means, it uses the limit
and offset
clauses of the SQL query. So, make sure to chain your pagination query with an order scope. For those of you who aren't sure what I mean, the following document will help you understand the point.
When using
LIMIT
, it is a good idea to use anORDER BY
clause that constrains the result rows into a unique order. Otherwise you will get an unpredictable subset of the query's rows — you might be asking for the tenth through twentieth rows, but tenth through twentieth in what ordering? You don't know what ordering unless you specify ORDER BY.
http://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-LIMIT
As Kaminari's paginating scope is just an ordinal AR scope, you can do that in quite natural way.
Book.order('published_at').page(3).per(10)
Of course default_scope
works great with Kaminari for this purpose.
Rails 3:
class Book < ActiveRecord::Base
default_scope order('published_at')
end
Rails 4:
class Book < ActiveRecord::Base
default_scope ->{ order('published_at') }
end
Since version 0.9.6, you can specify default per_page
value per each model using a declarative DSL method paginates_per
.
class Book < ActiveRecord::Base
paginates_per 50
end
I still wonder whether this is grammatically correct and makes sense as an English phrase. I would appreciate if anyone can suggest me a better declarative method name for this feature.
( I suggest default_page_size
, or default_per_page
, or pagination_page_size
)
Also see the PageNumber
gem.
In Rails 4 and up, ActiveRecord::Base#all
(which moved to ActiveRecord::Scoping::Named::ClassMethods#all
) returns an ActiveRecord::Relation
, so you are able to call scope methods such as page
on it.
However, ActiveRecord::Base#all
in Rails 3 and below unfortunately returns an Array, meaning you could not do this:
User.all.page 2
#=> NoMethodError: undefined method `page' for #<Array:0x1017a9e80>
Let me tell you once again, in Rails 3 and earlier ActiveRecord::Base#all
returns an Array. This was a FAQ, but not my fault!
As described in README, the default labels for previous
, ...
and next
are stored in the I18n yaml inside the engine, and rendered through I18n API.
In fact this feature is not only for "international" apps but for everybody who wants to change the words on the labels.
For example, if you want to change the prev_label
to '<-', you don't have to override the whole partial template. Just add it to your locale file with en locale.
en:
views:
pagination:
previous: "<-"
I suppose you're all heavy addict of the life-changing autopagerize browser extension, right? So, here's a recipe that makes your website "autopagerizing".
- surround your repeating contents by a div with class="autopagerize_page_element"
.autopagerize_page_element
<div> or <table> or <whatever>
- @books.each do |book| or something
- place the paginate helper below
That's all. You'll realize your app starts working like Twitter! Hey, what a WEB 2.0!! It's paginating without clicking on any links! (so far as you're using "autopagirized" browser)
I never recommend you to do this. It's a horribly bad design. But you may sometimes encounter an unreasonable stupid situation that you are required to create POST links for paginating. Oh, dear. Then,
- pass :method => :post option to the paginator
= paginate @users, :method => :post
- override the
Link
sort of partials to pass the method value tolink_to
helper
= link_to page, url, :method => method
OK? Works?
You can override each tag's partial template by putting a file with proper name into app/views/kaminari/ directory. More explanation will be coming soon (or not soon).
Kaminari provides an Array wrapper class that adapts a generic Array object to the paginate view helper.
Kaminari.paginate_array(my_array_object).page(params[:page]).per(10)
For more information on this, you may want to look at the "Paginating a generic Array object" section of https://github.com/kaminari/kaminari#paginating-a-generic-array-object
Keep in mind though that it is fairly easy to paginate an array in Ruby without using Kaminari.
arr = (1..100).to_a
page, per_page = 1, 10
arr[((page - 1) * per_page)...(page * per_page)] #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
page, per_page = 2, 10
arr[((page - 1) * per_page)...(page * per_page)] #=> [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Who needs a special gem or plugin just for doing this?
OK, You're using Ruby. Something like this would do the work. Perhaps.
@users = User.order(:created_at).page(params[:page]).all
@users.instance_eval <<-EVAL
def current_page
#{params[:page] || 1}
end
def num_pages
count
end
def limit_value
20
end
EVAL
I'm not promising that this should work, but I'm just pointing out that you don't have to implement AbstractFactoryFactoryInterfaces
to make it act like a paginatable collection.
If you want to be able to convert a Relation that has already been paginated into an Array, and still preserve all of the pagination data (so that you don't get an error in your view about missing method current_page, etc.), you can do this:
class UsersController
def index
@users_scope = User.order(:created_at).page(params[:page])
@users = @users_scope.extend(Kaminari::PaginatableRelationToPaginatableArray).to_paginatable_array
end
end
module Kaminari
module PaginatableRelationToPaginatableArray
def to_paginatable_array
relation = self
PaginatableArray.new(self, total_count: relation.total_count).tap do |array|
array.singleton_class.class_eval do
define_method :current_page do
relation.current_page
end
define_method :total_pages do
relation.total_pages
end
define_method :limit_value do
relation.limit_value
end
define_method :offset_value do
relation.offset_value
end
define_method :last_page? do
relation.last_page?
end
end
end
end
end
end
It's dead easy to add pagination to inherited_resources. First, make sure you list inherited_resources
, kaminari
and has_scope
in your Gemfile.
Then, create a simple controller like this:
class ProductsController < InheritedResources::Base
has_scope :page, :default => 1
end
And you're done! Look at the READMEs of the specific gems to learn more.