Skip to content
Rory O’Kane edited this page Jan 27, 2021 · 42 revisions

Here are tons of tips and howtos for Kaminari users!

☇ Don't forget to add an order scope

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 an ORDER 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

 

☇ Specifying default per_page

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 3 and below, ActiveRecord::Base#all is not a scope

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!  

☇ Dealing with labels for I18n and non-I18n apps

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: "<-"

 

☇ How to "autopagerize" my views?

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".

  1. 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
  1. 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)

 

☇ POST links?

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,

  1. pass :method => :post option to the paginator
= paginate @users, :method => :post
  1. override the Link sort of partials to pass the method value to link_to helper
= link_to page, url, :method => method

OK? Works?

 

☇ Overriding partials

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).

☇ How do I paginate an Array?

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?

 

☇ But I still want to paginate an Array by any means. How can I render the paginator?

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

☇ Using Kaminari with InheritedResources

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.