Yet another paginator for Ruby on Rails, which adds a paginate scope to your ActiveRecords.
Setup
Include Yap into your models to add the paginate scope like so:
class User < ActiveRecord::Base
include Yap
belongs_to :team
end
Defaults (optional)
To setup default parameters call Yap.configure. You can access the defaults as block parameter. Call this somewhere in
config/initializers/.
Yap.configure do |defaults|
defaults.page = 1
defaults.per_page = 10
defaults.sort = 'id'
defaults.direction = 'ASC'
defaults.disable_warnings = false
end
The above settings will be applied if you do not set your own.
Custom Naming (optional)
ActiveRecords can implement the method map_name_to_column to define aliases for columns. This can be useful to hide internal naming from users and to make sorting by associations possible (more on this below).
COLUMN_MAP = {
'team' => 'teams.name',
'birthday' => 'date_of_birth'
}
def self.map_name_to_column(name)
return COLUMN_MAP[name]
end
Usage
Basics
Assuming you included Yap into User, you can now do something like this:
User.paginate
# => Page 1 with default order and size
User.paginate(
page: 1,
per_page: 10,
sort: 'id',
direction: 'ASC'
)
# => Invocation with custom options.
User.paginate(params).last_page
# => Last page as a number for the previously paginated query
User.paginate(params).range
# => E.g. { from: 1, to: 10, total: 100 }
User.paginate(params).total
# => total number of results for this filters
User.paginate(params).without_pagination do |rel|
# access rel without limit and offset; filters still apply
rel.count
end
# => total number of results
User.paginate(
sort: {
'gender' => 'desc',
'date_of_birth' => 'asc'
}
)
# => Sort by gender and date_of_birth (method 1)
User.paginate(sort: 'gender,date_of_birth', direction: 'desc,asc')
# => Sort by gender and date_of_birth (method 2)
User.filtered('gender' => 'f')
# => All female users
User.filtered(
'team_id' => '1,2',
'gender' => 'm'
)
# => All males of teams 1 and 2
User.filtered(
# Note that '0...3' means [0,1,2] while '0..3' means [0,1,2,3]
'date_of_birth' => '1990-01-01...1991-01-01'
)
# => All users born in 1990
User.filtered('team_id' => '!null')
# => All users with any team
User.paginate(
page: 1,
filter: { 'team' => 'null' }
)
# => Combining filter and pagination
User.paginate(params)
# => Passing parameters in controller (http://localhost/users?filter[gender]=f)
Yap will convert strings to symbols or numbers and vice versa where necessary. This make the last one a really powerful method of offering the pagination API directly to the user.
Chaining
The paginate scope can be chained with other ActiveRecord methods like joins, where etc..
Advanced
The "team" alias defined in the column map above allows us to sort the results by the name of the team a user belongs to. "teams.name" describes the "teams" table and the "name" column in our database. We need to join the team association to make this work. Example:
User.joins(:team).paginate(sort: 'team')
Error Handling
If an option cannot be parsed it will raise Yap::PaginationError or Yap::FilterError, which are both
Yap::YapErrors. I suggest to use rescue_from in the controller to handle such a case.
rescue_from Yap::YapError, with: :handle_yap_error
def handle_yap_error
# generate user friendly error here, set flash[:error] or whatever you like.
end
Full Example
require 'yap'
class User < ActiveRecord::Base
include Yap
belongs_to :team
COLUMN_MAP = {
'team' => 'teams.name',
'birthday' => 'date_of_birth'
}
def self.map_name_to_column(name)
return COLUMN_MAP[name]
end
end
class UsersController < ApplicationController
rescue_from Yap::YapError, with: :handle_yap_error
def handle_yap_error
# generate user friendly error here, set flash[:error] or whatever you like.
end
def index
respond_with User.joins(:team).paginate(params)
end
end
Changelog
2.0
- [BREAKING] dropped support for ruby 1.9.x
- [BREAKING] renamed
filtermethod tofiltereddue to a conflict with a new method inActiverecord::Relation - [BREAKING] dropped support for activerecord 4
1.4
- added support for strings and dates with comparison parameters
1.3
- added comparison operators <, >, <=, >= to filters
- usage: ?filter[id]=>=100
- will give you any element with and id equal to or greater 100
- you can even combine multiple filters: ?filter[id]=>=100,<200
- although the same result can be achieved through ?filter[id]=100...200
- usage: ?filter[id]=>=100
-
currently, there is no support for strings and dates. This will be implemented in the future.(see version 1.4)
1.2
- added sorting by multiple elements
- method 1: ?sort[team]=desc&sort[date_of_birth]=asc
- method 2: ?sort=team,date_of_birth&direction=desc,asc
- missing directions will fall back to default
1.1
- changed default behavior for
rangeto not includetotal; saves time when usingrange- call
range(true)to include total value
- call
1.0
- changed
last_pageto base on the actual query not only the parameters- this now produces correct results if there are custom
whereconditions
- this now produces correct results if there are custom
- added
rangemethod which can be used likelast_page- provides a hash containing the limits of the latest queried page
- added
totalmethod to get the total number of results - added
without_paginationwhich takes a block an serves anActiverecord::Relationwhich is not paginated