geared_pagination 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 86fef30f9fb2e850e8b305a079d05d39007ec6bd
4
+ data.tar.gz: 83e91624d0fbd65747604f91628e826f00a131b0
5
+ SHA512:
6
+ metadata.gz: f7e00eb4a7598860c873eb1810ffdba1c8e5a30d625d42b6a7e10f035786e593724b74c8c496089495ada18c992af7b41534041e1b5951f8573932a3d533509e
7
+ data.tar.gz: c2feb3068a763034a474fca15cd793b3fdeae695cee4c6f0187c4ea2918a76a24f9a124061407922a3b397137119fdc234413153563ba0ed9f3301af9204238e
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'byebug'
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ geared_pagination (0.1)
5
+ activesupport (>= 5.0)
6
+ addressable (>= 2.5.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (5.1.0.beta1)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (~> 0.7)
14
+ minitest (~> 5.1)
15
+ tzinfo (~> 1.1)
16
+ addressable (2.5.0)
17
+ public_suffix (~> 2.0, >= 2.0.2)
18
+ byebug (9.0.6)
19
+ concurrent-ruby (1.0.4)
20
+ i18n (0.8.1)
21
+ minitest (5.10.1)
22
+ public_suffix (2.0.5)
23
+ rake (12.0.0)
24
+ thread_safe (0.3.5)
25
+ tzinfo (1.2.2)
26
+ thread_safe (~> 0.1)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ bundler (~> 1.12)
33
+ byebug
34
+ geared_pagination!
35
+ rake
36
+
37
+ BUNDLED WITH
38
+ 1.14.6
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2017 David Heinemeier Hansson, Basecamp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # Geared Pagination
2
+
3
+ Most pagination schemes use a fixed page size. Page 1 returns as many elements as page 2. But that's
4
+ frequently not the most sensible way to page through a large collection when you care about serving the
5
+ initial request as quickly as possible. This is particularly the case when using the pagination scheme
6
+ in combination with an infinite scrolling UI.
7
+
8
+ Geared Pagination allows you to define different ratios. By default, we will return 15 elements on page 1,
9
+ 30 on page 2, 50 on page 3, and 100 from page 4 and forward. This has proben to be a very sensible set of
10
+ ratios for much of the Basecamp UIs. But you can of course tweak the ratios, use fewer, or even none at all,
11
+ if you certain page calls for a fixed-rate scheme.
12
+
13
+ On json actions that set a page, we'll also also automatically set Link and X-Total-Count headers for APIs
14
+ to be able to page through a collection.
15
+
16
+ ## Example
17
+
18
+ ```ruby
19
+ class MessagesController < ApplicationController
20
+ def index
21
+ @page = current_page_from Message.order(created_at: :desc)
22
+ end
23
+ end
24
+
25
+ # app/views/messages/index.html.erb
26
+
27
+ Showing page <%= @page.number %> of <%= @page.collection.page_count %> (<%= @page.collection.record_count %> total messages):
28
+
29
+ <%= render @page.records %>
30
+
31
+ <% if @page.last? %>
32
+ No more pages!
33
+ <% else %>
34
+ <%= link_to "Next page", messages_path(page: @page.next_number) %>
35
+ <% end %>
36
+
37
+ ```
38
+
39
+ ## License
40
+ Geared Pagination is released under the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/setup"
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |test|
6
+ test.libs << "test"
7
+ test.test_files = FileList["test/*_test.rb"]
8
+ end
9
+
10
+ task default: :test
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'geared_pagination'
3
+ s.version = '0.1'
4
+ s.authors = 'David Heinemeier Hansson'
5
+ s.email = 'david@basecamp.com'
6
+ s.summary = 'Paginate Active Record sets at variable speeds'
7
+ s.homepage = 'https://github.com/basecamp/geared_pagination'
8
+ s.license = 'MIT'
9
+
10
+ s.required_ruby_version = '>= 1.9.3'
11
+
12
+ s.add_dependency 'activesupport', '>= 5.0'
13
+ s.add_dependency 'addressable', '>= 2.5.0'
14
+
15
+ s.add_development_dependency 'bundler', '~> 1.12'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- test/*`.split("\n")
19
+ end
@@ -0,0 +1,2 @@
1
+ require 'geared_pagination/recordset'
2
+ require 'geared_pagination/railtie' if defined?(Rails)
@@ -0,0 +1,30 @@
1
+ require 'geared_pagination/recordset'
2
+ require 'geared_pagination/headers'
3
+
4
+ module GearedPagination
5
+ module Controller
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ after_action :set_paginated_headers
10
+ end
11
+
12
+ private
13
+ def set_page_and_extract_portion_from(records, per_page: nil)
14
+ @page = current_page_from(records, per_page: per_page)
15
+ @page.records
16
+ end
17
+
18
+ def current_page_from(records, per_page: nil)
19
+ GearedPagination::Recordset.new(records, per_page: per_page).page(current_page_param)
20
+ end
21
+
22
+ def set_paginated_headers
23
+ GearedPagination::Headers.new(page: @page, controller: self).apply if @page.is_a?(GearedPagination::Page)
24
+ end
25
+
26
+ def current_page_param
27
+ params[:page].to_i > 0 ? params[:page].to_i : 1
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ require 'addressable/uri'
2
+
3
+ module GearedPagination
4
+ class Headers
5
+ def initialize(page:, controller:)
6
+ @page, @controller = page, controller
7
+ end
8
+
9
+ def apply
10
+ @controller.headers.update(headers) if applicable?
11
+ end
12
+
13
+ private
14
+ def headers
15
+ Hash.new.tap do |h|
16
+ h["X-Total-Count"] = @page.collection.records_count.to_s
17
+ h["Link"] = next_page_link_header unless @page.last?
18
+ end
19
+ end
20
+
21
+ def applicable?
22
+ @controller.request.format&.json?
23
+ end
24
+
25
+ def next_page_link_header
26
+ link_header(rel: :next, page_number: @page.next_number).to_s
27
+ end
28
+
29
+ def link_header(rel:, page_number:)
30
+ uri = Addressable::URI.parse(@controller.request.url)
31
+ uri.query_values = (uri.query_values || {}).merge("page" => page_number)
32
+ %{<#{uri}>; rel="#{rel}"}
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,43 @@
1
+ require 'geared_pagination/portion'
2
+
3
+ module GearedPagination
4
+ class Page
5
+ attr_reader :number, :collection
6
+
7
+ def initialize(number, from:)
8
+ @number, @collection = number, from
9
+ @portion = GearedPagination::Portion.new(page_number: number, per_page: from.ratios)
10
+ end
11
+
12
+ def records
13
+ @records ||= @portion.from(collection.records)
14
+ end
15
+
16
+
17
+ def used?
18
+ records.load.any?
19
+ end
20
+
21
+ def empty?
22
+ records.load.none?
23
+ end
24
+
25
+
26
+ def first?
27
+ number == 1
28
+ end
29
+
30
+ def only?
31
+ collection.page_count == 1
32
+ end
33
+
34
+ def last?
35
+ number == collection.page_count
36
+ end
37
+
38
+
39
+ def next_number
40
+ number + 1
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,23 @@
1
+ require 'geared_pagination/ratios'
2
+
3
+ module GearedPagination
4
+ class Portion
5
+ attr_reader :page_number, :ratios
6
+
7
+ def initialize(page_number: 1, per_page: GearedPagination::Ratios.new)
8
+ @page_number, @ratios = page_number, per_page
9
+ end
10
+
11
+ def from(scope)
12
+ scope.limit(limit).offset(offset)
13
+ end
14
+
15
+ def limit
16
+ ratios[page_number]
17
+ end
18
+
19
+ def offset
20
+ (page_number - 1).times.sum { |index| ratios[index + 1] }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ require 'rails/railtie'
2
+ require 'geared_pagination/controller'
3
+
4
+ class GearedPagination::Engine < ::Rails::Engine
5
+ initializer :webpacker do |app|
6
+ ActiveSupport.on_load :action_controller do
7
+ ActionController::Base.send :include, GearedPagination::Controller
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module GearedPagination
2
+ class Ratios
3
+ DEFAULTS = [ 15, 30, 50, 100 ]
4
+
5
+ def initialize(ratios = nil)
6
+ @ratios = Array(ratios || DEFAULTS)
7
+ end
8
+
9
+ def [](page_number)
10
+ @ratios[page_number - 1] || @ratios.last
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,35 @@
1
+ require 'geared_pagination/ratios'
2
+ require 'geared_pagination/page'
3
+
4
+ module GearedPagination
5
+ class Recordset
6
+ attr_reader :records, :ratios
7
+
8
+ def initialize(records, per_page: nil)
9
+ @records = records
10
+ @ratios = GearedPagination::Ratios.new(per_page)
11
+ end
12
+
13
+ def page(number)
14
+ GearedPagination::Page.new(number, from: self)
15
+ end
16
+
17
+ def page_count
18
+ @page_count ||= begin
19
+ count = 0
20
+ residual = records_count
21
+
22
+ while residual > 0
23
+ count += 1
24
+ residual = residual - ratios[count]
25
+ end
26
+
27
+ count
28
+ end
29
+ end
30
+
31
+ def records_count
32
+ @records_count ||= records.unscope(:limit).unscope(:offset).count
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ require 'test_helper'
2
+ require 'active_support/core_ext/string/inquiry'
3
+ require 'geared_pagination/headers'
4
+ require 'geared_pagination/recordset'
5
+
6
+ class GearedPagination::HeadersTest < ActiveSupport::TestCase
7
+ Request = Struct.new(:url, :format)
8
+ Controller = Struct.new(:request, :headers)
9
+
10
+ setup do
11
+ @controller_serving_json = Controller.new(Request.new("http://example.com/collection.json", "json".inquiry), {})
12
+ @controller_serving_html = Controller.new(Request.new("http://example.com/collection", "html".inquiry), {})
13
+
14
+ @single_page_collection = GearedPagination::Recordset.new(Recording.all, per_page: 1000)
15
+ @many_page_collection = GearedPagination::Recordset.new(Recording.all, per_page: 1)
16
+ end
17
+
18
+ test "total count" do
19
+ GearedPagination::Headers.new(page: @many_page_collection.page(1), controller: @controller_serving_json).apply
20
+ assert_equal Recording.all.count.to_s, @controller_serving_json.headers["X-Total-Count"]
21
+ end
22
+
23
+ test "no link for html requests" do
24
+ GearedPagination::Headers.new(page: @many_page_collection.page(1), controller: @controller_serving_html).apply
25
+ assert @controller_serving_html.headers["Link"].nil?
26
+ end
27
+
28
+ test "no link for json request with single page" do
29
+ GearedPagination::Headers.new(page: @many_page_collection.page(1), controller: @controller_serving_html).apply
30
+ assert @controller_serving_html.headers["Link"].nil?
31
+ end
32
+
33
+ test "links for json request with multiple pages" do
34
+ GearedPagination::Headers.new(page: @many_page_collection.page(1), controller: @controller_serving_json).apply
35
+ assert_equal '<http://example.com/collection.json?page=2>; rel="next"',
36
+ @controller_serving_json.headers["Link"]
37
+
38
+ GearedPagination::Headers.new(page: @many_page_collection.page(2), controller: @controller_serving_json).apply
39
+ assert_equal '<http://example.com/collection.json?page=3>; rel="next"',
40
+ @controller_serving_json.headers["Link"]
41
+ end
42
+
43
+ test "no link for json request with multiple pages on last page" do
44
+ GearedPagination::Headers.new(page: @many_page_collection.page(Recording.all.count), controller: @controller_serving_json).apply
45
+ assert @controller_serving_json.headers["Link"].nil?
46
+ end
47
+ end
data/test/page_test.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+ require 'geared_pagination/recordset'
3
+
4
+ class GearedPagination::PageTest < ActiveSupport::TestCase
5
+ test "first" do
6
+ assert GearedPagination::Recordset.new(Recording.all).page(1).first?
7
+ assert_not GearedPagination::Recordset.new(Recording.all).page(2).first?
8
+ end
9
+
10
+ test "only" do
11
+ assert GearedPagination::Recordset.new(Recording.all, per_page: 1000).page(1).only?
12
+ assert_not GearedPagination::Recordset.new(Recording.all, per_page: 1).page(1).only?
13
+ end
14
+
15
+ test "last" do
16
+ assert GearedPagination::Recordset.new(Recording.all, per_page: 1000).page(1).last?
17
+ assert_not GearedPagination::Recordset.new(Recording.all, per_page: 1).page(1).last?
18
+ end
19
+
20
+ test "next_number" do
21
+ assert_equal 2, GearedPagination::Recordset.new(Recording.all, per_page: 1000).page(1).next_number
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+ require 'active_support/core_ext/array/access'
3
+ require 'geared_pagination/portion'
4
+
5
+ class GearedPagination::PortionTest < ActiveSupport::TestCase
6
+ test "offset" do
7
+ assert_equal 0, GearedPagination::Portion.new(page_number: 1).offset
8
+ assert_equal GearedPagination::Ratios::DEFAULTS.first, GearedPagination::Portion.new(page_number: 2).offset
9
+ assert_equal GearedPagination::Ratios::DEFAULTS.first + GearedPagination::Ratios::DEFAULTS.second, GearedPagination::Portion.new(page_number: 3).offset
10
+ end
11
+
12
+ test "limit" do
13
+ assert_equal GearedPagination::Ratios::DEFAULTS.first, GearedPagination::Portion.new(page_number: 1).limit
14
+ assert_equal GearedPagination::Ratios::DEFAULTS.second, GearedPagination::Portion.new(page_number: 2).limit
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ require 'test_helper'
2
+ require 'geared_pagination/ratios'
3
+
4
+ class GearedPagination::RatiosTest < ActiveSupport::TestCase
5
+ test "single limit" do
6
+ limits = GearedPagination::Ratios.new(10)
7
+
8
+ assert_equal 10, limits[1]
9
+ assert_equal 10, limits[2]
10
+ end
11
+
12
+ test "range of limits" do
13
+ limits = GearedPagination::Ratios.new([ 10, 25, 100 ])
14
+
15
+ assert_equal 10, limits[1]
16
+ assert_equal 25, limits[2]
17
+ assert_equal 100, limits[3]
18
+ assert_equal 100, limits[4]
19
+ end
20
+
21
+ test "default limits" do
22
+ limits = GearedPagination::Ratios.new
23
+
24
+ assert_equal GearedPagination::Ratios::DEFAULTS.first, limits[1]
25
+ assert_equal GearedPagination::Ratios::DEFAULTS.last, limits[99]
26
+ end
27
+ end
@@ -0,0 +1,62 @@
1
+ class Recordings
2
+ def initialize(recordings)
3
+ @recordings = recordings
4
+ @offset = 0
5
+ @limit = nil
6
+ end
7
+
8
+ def limit(size)
9
+ @limit = size
10
+ self
11
+ end
12
+
13
+ def offset(number)
14
+ @offset = number
15
+ self
16
+ end
17
+
18
+ def unscope(restriction)
19
+ case restriction
20
+ when :offset then @offset = 0
21
+ when :limit then @limit = nil
22
+ end
23
+
24
+ self
25
+ end
26
+
27
+
28
+ def [](index)
29
+ slice[index]
30
+ end
31
+
32
+ def count
33
+ slice.size
34
+ end
35
+ alias :size :count
36
+
37
+ def include?(record)
38
+ slice.include?(record)
39
+ end
40
+
41
+
42
+ private
43
+ def slice
44
+ @recordings[@offset..(@limit ? @offset + (@limit - 1) : -1)]
45
+ end
46
+ end
47
+
48
+ class Recording
49
+ def self.all(count = 120)
50
+ Recordings.new(count.times.collect { |i| new(i) })
51
+ end
52
+
53
+ attr_reader :number
54
+
55
+ def initialize(number)
56
+ @number = 1
57
+ end
58
+
59
+ def ==(comparison)
60
+ @number == comparison.number
61
+ end
62
+ end
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+ require 'geared_pagination/recordset'
3
+
4
+ class GearedPagination::RecordsetTest < ActiveSupport::TestCase
5
+ test "single limit pagination" do
6
+ collection = GearedPagination::Recordset.new(Recording.all, per_page: 10)
7
+
8
+ assert_equal 10, collection.page(1).records.size
9
+ assert_equal 10, collection.page(2).records.size
10
+ end
11
+
12
+ test "variable limit pagination" do
13
+ collection = GearedPagination::Recordset.new(Recording.all, per_page: [ 10, 15, 20 ])
14
+
15
+ assert_equal 10, collection.page(1).records.size
16
+ assert collection.page(1).records.include?(Recording.all[0])
17
+
18
+ assert_equal 15, collection.page(2).records.size
19
+ assert collection.page(2).records.include?(Recording.all[11])
20
+
21
+ assert_equal 20, collection.page(3).records.size
22
+ assert collection.page(3).records.include?(Recording.all[26])
23
+
24
+ assert_equal 20, collection.page(4).records.size
25
+ assert collection.page(4).records.include?(Recording.all[46])
26
+ end
27
+
28
+ test "page count" do
29
+ assert_equal 7, GearedPagination::Recordset.new(Recording.all, per_page: [ 10, 15, 20 ]).page_count
30
+ end
31
+
32
+ test "records count" do
33
+ assert_equal Recording.all.count, GearedPagination::Recordset.new(Recording.all, per_page: [ 10, 15, 20 ]).records_count
34
+ end
35
+ end
@@ -0,0 +1,5 @@
1
+ require 'bundler/setup'
2
+ require 'active_support'
3
+ require 'active_support/testing/autorun'
4
+ require 'byebug'
5
+ require 'recording_stubs'
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: geared_pagination
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - David Heinemeier Hansson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: addressable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.5.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.5.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.12'
55
+ description:
56
+ email: david@basecamp.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - Gemfile
62
+ - Gemfile.lock
63
+ - MIT-LICENSE
64
+ - README.md
65
+ - Rakefile
66
+ - geared_pagination.gemspec
67
+ - lib/geared_pagination.rb
68
+ - lib/geared_pagination/controller.rb
69
+ - lib/geared_pagination/headers.rb
70
+ - lib/geared_pagination/page.rb
71
+ - lib/geared_pagination/portion.rb
72
+ - lib/geared_pagination/railtie.rb
73
+ - lib/geared_pagination/ratios.rb
74
+ - lib/geared_pagination/recordset.rb
75
+ - test/headers_test.rb
76
+ - test/page_test.rb
77
+ - test/portion_test.rb
78
+ - test/ratios_test.rb
79
+ - test/recording_stubs.rb
80
+ - test/recordset_test.rb
81
+ - test/test_helper.rb
82
+ homepage: https://github.com/basecamp/geared_pagination
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 1.9.3
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 2.6.8
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: Paginate Active Record sets at variable speeds
106
+ test_files:
107
+ - test/headers_test.rb
108
+ - test/page_test.rb
109
+ - test/portion_test.rb
110
+ - test/ratios_test.rb
111
+ - test/recording_stubs.rb
112
+ - test/recordset_test.rb
113
+ - test/test_helper.rb