Yet another paginator for Ruby on Rails, which adds a paginate
scope to your ActiveRecords.
Include Yap
into your models to add the paginate
scope like so:
class User < ActiveRecord::Base
include Yap
belongs_to :team
end
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.
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
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.
The paginate
scope can be chained with other ActiveRecord
methods like joins
, where
etc..
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')
If an option cannot be parsed it will raise Yap::PaginationError
or Yap::FilterError
, which are both
Yap::YapError
s. 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
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
- [BREAKING] dropped support for ruby 1.9.x
- [BREAKING] renamed
filter
method tofiltered
due to a conflict with a new method inActiverecord::Relation
- [BREAKING] dropped support for activerecord 4
- added support for strings and dates with comparison parameters
- 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)
- 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
- changed default behavior for
range
to not includetotal
; saves time when usingrange
- call
range(true)
to include total value
- call
- changed
last_page
to base on the actual query not only the parameters- this now produces correct results if there are custom
where
conditions
- this now produces correct results if there are custom
- added
range
method which can be used likelast_page
- provides a hash containing the limits of the latest queried page
- added
total
method to get the total number of results - added
without_pagination
which takes a block an serves anActiverecord::Relation
which is not paginated