Skip to content

Latest commit

 

History

History
205 lines (149 loc) · 6.23 KB

README.md

File metadata and controls

205 lines (149 loc) · 6.23 KB

Build Status Test Coverage Code Climate

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 filter method to filtered due to a conflict with a new method in Activerecord::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
  • 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 range to not include total; saves time when using range
    • call range(true) to include total value

1.0

  • changed last_page to base on the actual query not only the parameters
    • this now produces correct results if there are custom where conditions
  • added range method which can be used like last_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 an Activerecord::Relation which is not paginated