README.md

Summary

Maintainability
Test Coverage
[![Build Status](https://travis-ci.org/1and1/yap.svg?branch=master)](https://travis-ci.org/1and1/yap) [![Test Coverage](https://codeclimate.com/github/1and1/yap/badges/coverage.svg)](https://codeclimate.com/github/1and1/yap/coverage) [![Code Climate](https://codeclimate.com/github/1and1/yap/badges/gpa.svg)](https://codeclimate.com/github/1and1/yap)

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

## 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