Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2a2d9b0
commit 4856782
Showing
9 changed files
with
224 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
--- | ||
title: Arel | ||
--- | ||
# Arel Extra | ||
|
||
This extra adds a specialized pagination for collections from sql databases with `GROUP BY` clauses, by computing the total number of results with `COUNT(*) OVER ()`. It was tested against MySQL (8.0.17) and Postgres (11.5). Before using in a different database, make sure the sql `COUNT(*) OVER ()` performs a count of all the lines after the `GROUP BY` clause is applied. | ||
|
||
## Synopsis | ||
|
||
See [extras](../extras.md) for general usage info. | ||
|
||
In the `pagy.rb` initializer: | ||
|
||
```ruby | ||
require 'pagy/extras/arel' | ||
``` | ||
|
||
In a controller: | ||
|
||
```ruby | ||
@pagy_a, @items = pagy_arel(a_collection, ...) | ||
|
||
# independently paginate some other collections as usual | ||
@pagy_b, @records = pagy(some_scope, ...) | ||
``` | ||
|
||
## Files | ||
|
||
- [arel.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/arel.rb) | ||
|
||
## Methods | ||
|
||
This extra adds the `pagy_arel` method to the `Pagy::Backend` to be used in place (or in parallel) of the `pagy` method when you want to paginate a collection with sql `GROUP BY` clause, where the number of groups/rows is big (for instance when group by day of the last 3 years). It also adds a `pagy_arel_get_variables` sub-method, used for easy customization of variables by overriding. | ||
|
||
**Notice**: there is no `pagy_arel_get_items` method to override, since the items are fetched directly by the specialized `pagy_arel` method. | ||
|
||
### pagy_arel(collection, vars=nil) | ||
|
||
This method is the same as the generic `pagy` method, but with improved speed for SQL `GROUP BY` collections. (see the [pagy doc](../api/backend.md#pagycollection-varsnil)) | ||
|
||
### pagy_arel_get_vars(collection) | ||
|
||
This sub-method is the same as the `pagy_get_vars` sub-method, but it is called only by the `pagy_arel` method. (see the [pagy_get_vars doc](../api/backend.md#pagy_get_varscollection-vars)). | ||
|
||
### pagy_arel_count(collection) | ||
|
||
This sub-method it is called only by the `pagy_get_vars` method. It will detect which query to perform based on the active record groups (sql `GROUP BY`s). In case there aren't group values performs a normal `.count(:all)`, otherwise it will perform a `COUNT(*) OVER ()`. The last tells database to perform a count of all the lines after the `GROUP BY` clause is applied. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/arel | ||
# encoding: utf-8 | ||
# frozen_string_literal: true | ||
|
||
class Pagy | ||
module Backend ; private | ||
|
||
def pagy_arel(collection, vars={}) | ||
pagy = Pagy.new(pagy_arel_get_vars(collection, vars)) | ||
return pagy, pagy_get_items(collection, pagy) | ||
end | ||
|
||
def pagy_arel_get_vars(collection, vars) | ||
vars[:count] ||= pagy_arel_count(collection) | ||
vars[:page] ||= params[ vars[:page_param] || VARS[:page_param] ] | ||
vars | ||
end | ||
|
||
def pagy_arel_count(collection) | ||
if collection.group_values.empty? | ||
# COUNT(*) | ||
collection.count(:all) | ||
else | ||
# COUNT(*) OVER () | ||
sql = Arel.star.count.over(Arel::Nodes::Grouping.new([])) | ||
collection.unscope(:order).limit(1).pluck(sql).first | ||
end | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
module Arel | ||
def self.star(*) | ||
self | ||
end | ||
|
||
def self.count(*) | ||
self | ||
end | ||
|
||
def self.over(*) | ||
self | ||
end | ||
|
||
module Nodes | ||
class Grouping | ||
def initialize(array) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# encoding: utf-8 | ||
# frozen_string_literal: true | ||
|
||
require_relative '../../test_helper' | ||
require_relative '../../mock_helpers/arel' | ||
require 'pagy/extras/arel' | ||
|
||
describe Pagy::Backend do | ||
|
||
let(:controller) { MockController.new } | ||
|
||
describe "#pagy_arel" do | ||
|
||
before do | ||
@collection = MockCollection.new | ||
end | ||
|
||
it 'paginates with defaults' do | ||
pagy, items = controller.send(:pagy_arel, @collection) | ||
pagy.must_be_instance_of Pagy | ||
pagy.count.must_equal 1000 | ||
pagy.items.must_equal Pagy::VARS[:items] | ||
pagy.page.must_equal controller.params[:page] | ||
items.size.must_equal Pagy::VARS[:items] | ||
items.must_equal [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60] | ||
end | ||
|
||
it 'paginates with vars' do | ||
pagy, items = controller.send(:pagy_arel, @collection, page: 2, items: 10, link_extra: 'X') | ||
pagy.must_be_instance_of Pagy | ||
pagy.count.must_equal 1000 | ||
pagy.items.must_equal 10 | ||
pagy.page.must_equal 2 | ||
pagy.vars[:link_extra].must_equal 'X' | ||
items.size.must_equal 10 | ||
items.must_equal [11, 12, 13, 14, 15, 16, 17, 18, 19, 20] | ||
end | ||
|
||
end | ||
|
||
describe "#pagy_arel_get_vars" do | ||
|
||
before do | ||
@collection = MockCollection.new | ||
end | ||
|
||
it 'gets defaults' do | ||
vars = {} | ||
merged = controller.send :pagy_arel_get_vars, @collection, vars | ||
merged.keys.must_include :count | ||
merged.keys.must_include :page | ||
merged[:count].must_equal 1000 | ||
merged[:page].must_equal 3 | ||
end | ||
|
||
it 'gets vars' do | ||
vars = {page: 2, items: 10, link_extra: 'X'} | ||
merged = controller.send :pagy_arel_get_vars, @collection, vars | ||
merged.keys.must_include :count | ||
merged.keys.must_include :page | ||
merged.keys.must_include :items | ||
merged.keys.must_include :link_extra | ||
merged[:count].must_equal 1000 | ||
merged[:page].must_equal 2 | ||
merged[:items].must_equal 10 | ||
merged[:link_extra].must_equal 'X' | ||
end | ||
|
||
it 'works with grouped collections' do | ||
@collection = MockCollection::Grouped.new((1..1000).to_a) | ||
vars = {page: 2, items: 10, link_extra: 'X'} | ||
merged = controller.send :pagy_arel_get_vars, @collection, vars | ||
merged.keys.must_include :count | ||
merged.keys.must_include :page | ||
merged.keys.must_include :items | ||
merged.keys.must_include :link_extra | ||
merged[:count].must_equal 1000 | ||
merged[:page].must_equal 2 | ||
merged[:items].must_equal 10 | ||
merged[:link_extra].must_equal 'X' | ||
end | ||
|
||
it 'overrides count and page' do | ||
vars = {count: 10, page: 32} | ||
merged = controller.send :pagy_arel_get_vars, @collection, vars | ||
merged.keys.must_include :count | ||
merged[:count].must_equal 10 | ||
merged.keys.must_include :page | ||
merged[:page].must_equal 32 | ||
end | ||
|
||
end | ||
|
||
end |