Pageturner
Cursor based pagination for activerecord queries as described in Pagination Done the Right Way.
Pageturner allows you to paginate over a collection's attribute (anchor_column
) starting from a given record as noted by its id and value (anchor_id
, anchor_value
). The anchor_value
is the main value that divides your collection's pages. When the anchor_value
is nil
, we can't perform comparisons so we augment it with anchor_id
to decide ambiguities about ordering.
Usage
In your controllers:
query = Model.where(params[:filters])
@pagination = Pageturner.new(
anchor_column: "name",
anchor_id: params[:last_id],
anchor_value: params[:last_value],
ar_relation: query,
sort_direction: Pageturner::DESC,
page_size: 25
)
@next_path = model_path(
anchor_column: @pagination.next_cursor[:anchor_column]
anchor_id: @pagination.next_cursor[:anchor_id],
anchor_value: @pagination.next_cursor[:anchor_value],
page_size: @pagination.next_cursor[:page_size],
sort_direction: @pagination.next_cursor[:sort_direction]
)
In your views:
json.models @pagination.resources do |resource|
json.partial! 'attributes', model: resource
end
json.pagination do
json.next_path @next_path
end
If the column that you want to paginate on is referenced differently in your database schema than in your application, then you can pass the translations into Pageturner. This may happen when you want to paginate over an associated model's column and the association is aliased in your model (e.g. has_one :subordinate, class_name: "Employee"
).
@pagination = Pageturner.new(
anchor_column: "subordinate",
anchor_column_active_record_identifier: "subordinate.id",
anchor_column_sql_identifier: "employees.id",
...
)
Pageturner will send the anchor_column_active_record_identifier
as a method chain to an instance of the model you're paginating when calculating the next cursor from the current page and it will interpolate the anchor_column_sql_identifier
string when building the where and order by clauses of the query that paginates your model.
API
#has_next_page?
Returns whether or not there is a next page.
irb(main):001:0> @pagination.has_next_page?
=> true
#next_cursor
Returns the next page's cursor.
query = Model.where(params[:filters])
@pagination = Pageturner.new(
anchor_column: "name",
anchor_id: params[:last_id],
anchor_value: params[:last_value],
ar_relation: query,
sort_direction: Pageturner::DESC,
page_size: 25
)
irb(main):001:0> @pagination.next_cursor
=> {:anchor_column=>"name", :anchor_value=>"foo", :sort_direction=>"desc", :anchor_id=>3, :page_size=>25}
#resources
Returns the current page's collection.
Installation
Add this line to your application's Gemfile:
gem 'pageturner'
And then execute:
$ bundle
Or install it yourself as:
$ gem install pageturner
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/crzrcn/pageturner.