folio-pagination 0.0.2
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.
- data/.gitignore +17 -0
- data/.travis.yml +3 -0
- data/Gemfile +16 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +235 -0
- data/Rakefile +11 -0
- data/folio-pagination.gemspec +27 -0
- data/lib/folio/core_ext/enumerable.rb +51 -0
- data/lib/folio/invalid_page.rb +5 -0
- data/lib/folio/ordinal/page.rb +107 -0
- data/lib/folio/ordinal.rb +33 -0
- data/lib/folio/page.rb +86 -0
- data/lib/folio/per_page.rb +15 -0
- data/lib/folio/rails.rb +35 -0
- data/lib/folio/version.rb +3 -0
- data/lib/folio/will_paginate/active_record.rb +132 -0
- data/lib/folio/will_paginate/collection.rb +19 -0
- data/lib/folio/will_paginate/view_helpers/action_view.rb +10 -0
- data/lib/folio/will_paginate/view_helpers/link_renderer.rb +54 -0
- data/lib/folio/will_paginate/view_helpers/link_renderer_base.rb +44 -0
- data/lib/folio/will_paginate/view_helpers.rb +57 -0
- data/lib/folio-pagination.rb +3 -0
- data/lib/folio.rb +84 -0
- data/test/folio/core_ext/enumerable_test.rb +55 -0
- data/test/folio/ordinal/page_test.rb +173 -0
- data/test/folio/ordinal_test.rb +57 -0
- data/test/folio/page_test.rb +198 -0
- data/test/folio/per_page_test.rb +49 -0
- data/test/folio/rails_test.rb +15 -0
- data/test/folio/version_test.rb +8 -0
- data/test/folio/will_paginate/active_record_test.rb +118 -0
- data/test/folio/will_paginate/collection_test.rb +43 -0
- data/test/folio/will_paginate/view_helpers/link_renderer_base_test.rb +58 -0
- data/test/folio/will_paginate/view_helpers/link_renderer_test.rb +58 -0
- data/test/folio/will_paginate/view_helpers_test.rb +49 -0
- data/test/folio_test.rb +192 -0
- data/test/setup/active_record.rb +13 -0
- metadata +133 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
begin
|
2
|
+
require 'will_paginate/active_record'
|
3
|
+
rescue LoadError
|
4
|
+
raise "folio-pagination's activerecord support requires will_paginate"
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'folio/page'
|
8
|
+
require 'folio/ordinal'
|
9
|
+
require 'folio/ordinal/page'
|
10
|
+
|
11
|
+
module Folio
|
12
|
+
module WillPaginate
|
13
|
+
# enhances model_or_scope.page(n) method from WillPaginate to behave like a
|
14
|
+
# Folio::Ordinal::Page. replaces model_or_scope.paginate(...) method to
|
15
|
+
# behave like that from Folio while preserving basic mechanics of the
|
16
|
+
# version from WillPaginate.
|
17
|
+
#
|
18
|
+
# differences:
|
19
|
+
#
|
20
|
+
# * total_entries loading via count is *not* deferred as it was under
|
21
|
+
# WillPaginate.
|
22
|
+
#
|
23
|
+
# * paginate does *not* recognize any parameters beyond those described in
|
24
|
+
# for Folio's paginate (e.g. finder options, count options).
|
25
|
+
#
|
26
|
+
module ActiveRecord
|
27
|
+
module RelationMethods
|
28
|
+
include Folio::Page
|
29
|
+
include Folio::Ordinal::Page
|
30
|
+
|
31
|
+
# overrides Folio::Page's per_page similar to WillPaginate's version,
|
32
|
+
# but uses correct semantics for per_page(nil) (vs. per_page()).
|
33
|
+
def per_page(*args)
|
34
|
+
if args.size > 0
|
35
|
+
raise ArgumentError if args.size > 1
|
36
|
+
value ||= @klass.per_page
|
37
|
+
limit(value)
|
38
|
+
end
|
39
|
+
limit_value
|
40
|
+
end
|
41
|
+
|
42
|
+
# overrides WillPaginate's weird "ignore limit when counting" behavior.
|
43
|
+
# I can't fall back on super in the branch with a limit_value, since
|
44
|
+
# that would fall into WillPaginate's lap, but since there is a
|
45
|
+
# limit_value, safe enough to just instantiate and check length
|
46
|
+
def count
|
47
|
+
if limit_value
|
48
|
+
to_a.size
|
49
|
+
else
|
50
|
+
super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module Pagination
|
56
|
+
# turns the sequence of calls made by Folio::Ordinal#paginate into the
|
57
|
+
# construction of an appropriate relation
|
58
|
+
class PageProxy
|
59
|
+
def initialize(target)
|
60
|
+
@target = target
|
61
|
+
end
|
62
|
+
|
63
|
+
# needed to prevent Folio::Ordinal#configure_pagination from trying
|
64
|
+
# to decorate it
|
65
|
+
include Folio::Ordinal::Page
|
66
|
+
|
67
|
+
# called during Folio#configure_pagination, used to build up @rel
|
68
|
+
# from @target and the arguments
|
69
|
+
attr_writer :current_page
|
70
|
+
|
71
|
+
def per_page=(per_page)
|
72
|
+
@rel = @target.limit(per_page.to_i).page(@current_page)
|
73
|
+
@rel.limit_value
|
74
|
+
end
|
75
|
+
|
76
|
+
def total_entries=(total_entries)
|
77
|
+
@rel.total_entries = total_entries
|
78
|
+
end
|
79
|
+
|
80
|
+
# called during Folio::Ordinal#configure_pagination for bounds
|
81
|
+
# checking, before the proxy has been replaced by its result
|
82
|
+
def current_page
|
83
|
+
@rel.current_page
|
84
|
+
end
|
85
|
+
|
86
|
+
def out_of_bounds?
|
87
|
+
@rel.out_of_bounds?
|
88
|
+
end
|
89
|
+
|
90
|
+
# get the result of the construction back out during fill_page
|
91
|
+
def result
|
92
|
+
@rel
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# set up the proxy to receive the calls
|
97
|
+
def build_page
|
98
|
+
PageProxy.new(self)
|
99
|
+
end
|
100
|
+
|
101
|
+
# pull the result out of the proxy
|
102
|
+
def fill_page(proxy)
|
103
|
+
proxy.result
|
104
|
+
end
|
105
|
+
|
106
|
+
# make sure the relation coming out of page(...) is folio-compatible
|
107
|
+
def page(num)
|
108
|
+
super.extending(RelationMethods)
|
109
|
+
end
|
110
|
+
|
111
|
+
# don't try and look at Class (ActiveRecord::Base.class, etc.) for
|
112
|
+
# defaults
|
113
|
+
def default_per_page
|
114
|
+
Folio.per_page
|
115
|
+
end
|
116
|
+
|
117
|
+
include ::Folio::Ordinal
|
118
|
+
end
|
119
|
+
|
120
|
+
# mix into Active Record. these are the same ones that WillPaginate mixes
|
121
|
+
# its version of Pagination into.
|
122
|
+
::ActiveRecord::Base.extend Pagination
|
123
|
+
klasses = [::ActiveRecord::Relation]
|
124
|
+
if defined? ::ActiveRecord::Associations::CollectionProxy
|
125
|
+
klasses << ::ActiveRecord::Associations::CollectionProxy
|
126
|
+
else
|
127
|
+
klasses << ::ActiveRecord::Associations::AssociationCollection
|
128
|
+
end
|
129
|
+
klasses.each { |klass| klass.send(:include, Pagination) }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'folio'
|
2
|
+
require 'folio/ordinal/page'
|
3
|
+
require 'will_paginate/collection'
|
4
|
+
|
5
|
+
# overrides WillPaginate::Collection.create to return an ordinal folio page
|
6
|
+
# instead of a WillPaginate::Collection; the rest of WillPaginate::Collection
|
7
|
+
# is unused when in folio-land.
|
8
|
+
module WillPaginate
|
9
|
+
class Collection
|
10
|
+
def self.create(current_page, per_page=nil, total_entries=nil)
|
11
|
+
page = ::Folio::Ordinal::Page.create
|
12
|
+
page.current_page = current_page
|
13
|
+
page.per_page = per_page
|
14
|
+
page.total_entries = total_entries
|
15
|
+
yield page if block_given?
|
16
|
+
page
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
begin
|
2
|
+
require 'will_paginate/view_helpers/action_view'
|
3
|
+
rescue LoadError
|
4
|
+
raise "folio-pagination's actionview support requires will_paginate"
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'folio/will_paginate/view_helpers'
|
8
|
+
|
9
|
+
# no overrides specific to action view necessary. just including the general
|
10
|
+
# view_helper overrides as above is sufficient.
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'will_paginate/view_helpers/link_renderer'
|
2
|
+
|
3
|
+
module Folio
|
4
|
+
module WillPaginate
|
5
|
+
module ViewHelpers
|
6
|
+
module LinkRenderer
|
7
|
+
def previous_page_with_folio
|
8
|
+
# the page identifier may not be ordinal; use the value as set on the
|
9
|
+
# collection, instead.
|
10
|
+
previous_or_next_page(@collection.previous_page, @options[:previous_label], 'previous_page')
|
11
|
+
end
|
12
|
+
|
13
|
+
def next_page_with_folio
|
14
|
+
# the page identifier may not be ordinal; use the value as set on the
|
15
|
+
# collection, instead.
|
16
|
+
previous_or_next_page(@collection.next_page, @options[:next_label], 'next_page')
|
17
|
+
end
|
18
|
+
|
19
|
+
def link_with_folio(text, target, attributes = {})
|
20
|
+
if target
|
21
|
+
# the non-folio version only does this for Fixnums, but we want to
|
22
|
+
# do it for any page value. fortunately, this method only ever
|
23
|
+
# receives Fixnums or nil, so we can do it now for any non-nil
|
24
|
+
# value, and the non-folio version will happily avoid a double
|
25
|
+
# application because even when the page was a fixnum, it won't be
|
26
|
+
# anymore.
|
27
|
+
attributes[:rel] = rel_value(target)
|
28
|
+
target = url(target)
|
29
|
+
end
|
30
|
+
link_without_folio(text, target, attributes)
|
31
|
+
end
|
32
|
+
|
33
|
+
def rel_value_with_folio(page)
|
34
|
+
# don't check against mathed out values, just check the values on the
|
35
|
+
# collection
|
36
|
+
rels = []
|
37
|
+
rels << 'prev' if page == @collection.previous_page
|
38
|
+
rels << 'next' if page == @collection.next_page
|
39
|
+
rels << 'start' if page == @collection.first_page
|
40
|
+
rels.empty? ? nil : rels.join(' ')
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.included(klass)
|
44
|
+
[:previous_page, :next_page, :link, :rel_value].each do |method|
|
45
|
+
klass.send(:alias_method, :"#{method}_without_folio", method)
|
46
|
+
klass.send(:alias_method, method, :"#{method}_with_folio")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
::WillPaginate::ViewHelpers::LinkRenderer.send :include, self
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'will_paginate/view_helpers/link_renderer_base'
|
2
|
+
|
3
|
+
module Folio
|
4
|
+
module WillPaginate
|
5
|
+
module ViewHelpers
|
6
|
+
module LinkRendererBase
|
7
|
+
def prepare_with_folio(collection, options)
|
8
|
+
# only include page_links if we're in a collection with ordinal
|
9
|
+
# pages; otherwise stick to just prev/next.
|
10
|
+
options = options.merge(page_links: false) unless collection.ordinal_pages?
|
11
|
+
prepare_without_folio(collection, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def windowed_page_numbers_with_folio
|
15
|
+
page_numbers = windowed_page_numbers_without_folio
|
16
|
+
unless @collection.last_page
|
17
|
+
# the last page is not known, so add a trailing gap (it won't
|
18
|
+
# already be there, because the right range during the super call
|
19
|
+
# won't exist).
|
20
|
+
page_numbers << :gap
|
21
|
+
end
|
22
|
+
page_numbers
|
23
|
+
end
|
24
|
+
|
25
|
+
def total_pages_with_folio
|
26
|
+
# the collection may not have a known last page. if so, there must be
|
27
|
+
# a next page; count that as the last known page. it's ok to use
|
28
|
+
# these page identifiers as a page count because (after fixing
|
29
|
+
# LinkRenderer) it's only called when ordinal_pages is true.
|
30
|
+
@collection.last_page || @collection.next_page
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.included(klass)
|
34
|
+
[:prepare, :windowed_page_numbers, :total_pages].each do |method|
|
35
|
+
klass.send(:alias_method, :"#{method}_without_folio", method)
|
36
|
+
klass.send(:alias_method, method, :"#{method}_with_folio")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
::WillPaginate::ViewHelpers::LinkRendererBase.send :include, self
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'folio'
|
2
|
+
require 'folio/will_paginate/view_helpers/link_renderer_base'
|
3
|
+
require 'folio/will_paginate/view_helpers/link_renderer'
|
4
|
+
require 'will_paginate/view_helpers'
|
5
|
+
|
6
|
+
module Folio
|
7
|
+
module WillPaginate
|
8
|
+
module ViewHelpers
|
9
|
+
# just copied from ::WillPaginate::ViewHelpers except line 14 (changed from
|
10
|
+
# "unless value > 1" to "if value == 1" to be friendly to unknown
|
11
|
+
# total_pages)
|
12
|
+
def will_paginate_with_folio(collection, options = {})
|
13
|
+
# early exit if there is nothing to render
|
14
|
+
return nil if collection.total_pages == 1
|
15
|
+
|
16
|
+
options = ::WillPaginate::ViewHelpers.pagination_options.merge(options)
|
17
|
+
|
18
|
+
options[:previous_label] ||= will_paginate_translate(:previous_label) { '← Previous' }
|
19
|
+
options[:next_label] ||= will_paginate_translate(:next_label) { 'Next →' }
|
20
|
+
|
21
|
+
# get the renderer instance
|
22
|
+
renderer = case options[:renderer]
|
23
|
+
when nil
|
24
|
+
raise ArgumentError, ":renderer not specified"
|
25
|
+
when String
|
26
|
+
klass = if options[:renderer].respond_to? :constantize then options[:renderer].constantize
|
27
|
+
else Object.const_get(options[:renderer]) # poor man's constantize
|
28
|
+
end
|
29
|
+
klass.new
|
30
|
+
when Class then options[:renderer].new
|
31
|
+
else options[:renderer]
|
32
|
+
end
|
33
|
+
# render HTML for pagination
|
34
|
+
renderer.prepare collection, options, self
|
35
|
+
output = renderer.to_html
|
36
|
+
output = output.html_safe if output.respond_to?(:html_safe)
|
37
|
+
output
|
38
|
+
end
|
39
|
+
|
40
|
+
def page_entries_info_with_folio(collection, options = {})
|
41
|
+
# skip outputting anything unless the collection has ordinal pages (to
|
42
|
+
# be able to get an offset) *and* a known total count.
|
43
|
+
return unless collection.total_entries && collection.ordinal_pages
|
44
|
+
page_entries_info_without_folio(collection, options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.included(klass)
|
48
|
+
[:will_paginate, :page_entries_info].each do |method|
|
49
|
+
klass.send(:alias_method, :"#{method}_without_folio", method)
|
50
|
+
klass.send(:alias_method, method, :"#{method}_with_folio")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
::WillPaginate::ViewHelpers.send :include, self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/folio.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require "folio/version"
|
2
|
+
require "folio/per_page"
|
3
|
+
|
4
|
+
# Mix into any class implementing the following two methods:
|
5
|
+
#
|
6
|
+
# +build_page+: Responsible for instantiating a Folio::Page and
|
7
|
+
# configuring its ordinal_pages?, first_page, and last_page attributes;
|
8
|
+
# those values being common to any page returned from the folio.
|
9
|
+
#
|
10
|
+
# +fill_page+: Receives a Folio::Page with the ordinal_pages?,
|
11
|
+
# first_page, last_page, current_page, per_page, and total_entries
|
12
|
+
# attributes configured, and populates the page with the corresponding
|
13
|
+
# items from the folio. Also sets appropriate values for the next_page
|
14
|
+
# and previous_page attributes on the page. If the value provided in the
|
15
|
+
# page's current_page cannot be interpreted as addressing a page in the
|
16
|
+
# folio, raises Folio::InvalidPage.
|
17
|
+
#
|
18
|
+
# In return, `Folio` provides a the paginate method and per_page
|
19
|
+
# attributes described below.
|
20
|
+
module Folio
|
21
|
+
# Returns a page worth of items from the folio in a Folio::Page.
|
22
|
+
# accepts the following parameters:
|
23
|
+
#
|
24
|
+
# +page+: a page identifier addressing which page of the folio to
|
25
|
+
# return. if not present, the first page will be returned. will raise
|
26
|
+
# Folio::InvalidPage if the provided value cannot be used to address a
|
27
|
+
# page.
|
28
|
+
#
|
29
|
+
# +per_page+: number of items to attempt to include in the page. if
|
30
|
+
# not present, defaults to the folio's per_page value. should only
|
31
|
+
# include fewer items if the end of the folio is reached.
|
32
|
+
#
|
33
|
+
# +total_entries+: pre-calculated value for the total number of items
|
34
|
+
# in the folio. may be nil, indicating the returned page should have
|
35
|
+
# total_entries nil.
|
36
|
+
#
|
37
|
+
# if the folio implements a count method and the total_entries
|
38
|
+
# parameter is not supplied, the page's total_entries will be set from
|
39
|
+
# the count method.
|
40
|
+
def paginate(options={})
|
41
|
+
page = self.build_page
|
42
|
+
page = self.configure_pagination(page, options)
|
43
|
+
page = self.fill_page(page)
|
44
|
+
page
|
45
|
+
end
|
46
|
+
|
47
|
+
def configure_pagination(page, options)
|
48
|
+
current_page = options.fetch(:page) { nil }
|
49
|
+
current_page = page.first_page if current_page.nil?
|
50
|
+
page.current_page = current_page
|
51
|
+
page.per_page = options.fetch(:per_page) { self.per_page }
|
52
|
+
page.total_entries = options.fetch(:total_entries) { self.respond_to?(:count) ? self.count : nil }
|
53
|
+
page
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_per_page
|
57
|
+
self.class.per_page
|
58
|
+
end
|
59
|
+
|
60
|
+
include ::Folio::PerPage
|
61
|
+
|
62
|
+
# this funny pattern is so that if a module (e.g. Folio::Ordinal)
|
63
|
+
# includes this module, it won't get the per_page attribute, but will
|
64
|
+
# still be able to bestow that attribute on any class that includes
|
65
|
+
# *it*.
|
66
|
+
module PerPageIncluder
|
67
|
+
def included(klass)
|
68
|
+
if klass.is_a?(Class)
|
69
|
+
klass.extend ::Folio::PerPage
|
70
|
+
else
|
71
|
+
klass.extend ::Folio::PerPageIncluder
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
extend PerPageIncluder
|
77
|
+
extend PerPage
|
78
|
+
per_page(30)
|
79
|
+
end
|
80
|
+
|
81
|
+
# load the other commonly used portions of the gem
|
82
|
+
require "folio/page"
|
83
|
+
require "folio/ordinal"
|
84
|
+
require "folio/ordinal/page"
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'folio/core_ext/enumerable'
|
3
|
+
|
4
|
+
describe Array do
|
5
|
+
it "should be an ordinal folio" do
|
6
|
+
assert_respond_to [], :build_page
|
7
|
+
[].build_page.first_page.must_equal 1
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "per_page" do
|
11
|
+
it "should have per_page attribute as expected on instances" do
|
12
|
+
assert_respond_to [], :per_page
|
13
|
+
assert_respond_to [], :per_page=
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should not have per_page attribute on classes" do
|
17
|
+
Array.wont_respond_to :per_page
|
18
|
+
Array.wont_respond_to :per_page=
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should default to Folio.per_page" do
|
22
|
+
was = Folio.per_page
|
23
|
+
Folio.per_page = 100
|
24
|
+
[].per_page.must_equal 100
|
25
|
+
Folio.per_page = was
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "paginate" do
|
30
|
+
before do
|
31
|
+
@ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return a page" do
|
35
|
+
page = @ary.paginate
|
36
|
+
assert_respond_to page, :current_page
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should return appropriate slice" do
|
40
|
+
@ary.paginate(page: 2, per_page: 4).must_equal [5, 6, 7, 8]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should return partial slice at end" do
|
44
|
+
@ary.paginate(page: 3, per_page: 4).must_equal [9, 10]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should raise InvalidPage after end even if size unknown" do
|
48
|
+
lambda{ @ary.paginate(page: 3, per_page: 5, total_entries: nil) }.must_raise Folio::InvalidPage
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should return empty first page if empty" do
|
52
|
+
[].paginate.must_equal []
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'folio/ordinal'
|
3
|
+
|
4
|
+
describe Folio::Ordinal::Page do
|
5
|
+
before do
|
6
|
+
@page = Folio::Ordinal::Page.create
|
7
|
+
@page.current_page = 1
|
8
|
+
@page.per_page = 10
|
9
|
+
@page.total_entries = 30
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should have ordinal_pages=true" do
|
13
|
+
@page.ordinal_pages.must_equal true
|
14
|
+
@page.ordinal_pages?.must_equal true
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have first_page=1" do
|
18
|
+
@page.first_page.must_equal 1
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should force current_page to an integer" do
|
22
|
+
@page.current_page = "3"
|
23
|
+
@page.current_page.must_equal 3
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "when total pages known" do
|
27
|
+
it "should have last_page=total_pages" do
|
28
|
+
@page.last_page.must_equal @page.total_pages
|
29
|
+
|
30
|
+
@page.total_entries = 0
|
31
|
+
@page.last_page.must_equal @page.total_pages
|
32
|
+
|
33
|
+
@page.total_entries = nil
|
34
|
+
@page.last_page.must_equal @page.total_pages
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should have next_page=current_page+1 when not at end" do
|
38
|
+
@page.current_page = 2
|
39
|
+
@page.next_page.must_equal 3
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should still have next_page=current_page+1 when not at end, despite set value" do
|
43
|
+
@page.current_page = 2
|
44
|
+
@page.next_page = nil
|
45
|
+
@page.next_page.must_equal 3
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should have next_page=nil when at end" do
|
49
|
+
@page.current_page = 3
|
50
|
+
@page.next_page.must_be_nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should still have next_page=nil when at end, despite set value" do
|
54
|
+
@page.current_page = 3
|
55
|
+
@page.next_page = 4
|
56
|
+
@page.next_page.must_be_nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "when total pages not known" do
|
61
|
+
before do
|
62
|
+
@page.total_entries = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should have last_page=nil if next_page is known or assumed" do
|
66
|
+
# @page.next_page unset
|
67
|
+
@page.last_page.must_be_nil
|
68
|
+
|
69
|
+
# @page.next_page explicitly non-nil
|
70
|
+
@page.next_page = 2
|
71
|
+
@page.last_page.must_be_nil
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should have last_page=current_page if next_page is explicitly nil" do
|
75
|
+
@page.next_page = nil
|
76
|
+
@page.last_page.must_equal @page.current_page
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should have next_page=current_page+1 when not explicitly set" do
|
80
|
+
@page.current_page = 2
|
81
|
+
@page.next_page.must_equal 3
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should force non-nil set next_page to an integer" do
|
85
|
+
@page.next_page = "4"
|
86
|
+
@page.next_page.must_equal 4
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should not force nil set next_page to an integer" do
|
90
|
+
@page.next_page = nil
|
91
|
+
@page.next_page.must_be_nil
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should have next_page=set value when explicitly set" do
|
95
|
+
@page.next_page = nil
|
96
|
+
@page.current_page = 2
|
97
|
+
@page.next_page.must_be_nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should have previous_page=nil when at beginning" do
|
102
|
+
@page.current_page = 1
|
103
|
+
@page.previous_page.must_be_nil
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should have previous_page=current_page-1 when not at beginning" do
|
107
|
+
@page.current_page = 3
|
108
|
+
@page.previous_page.must_equal 2
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "out_of_bounds?" do
|
112
|
+
it "should be false if in first_page..last_page range" do
|
113
|
+
@page.current_page = 1
|
114
|
+
@page.out_of_bounds?.must_equal false
|
115
|
+
|
116
|
+
@page.current_page = 3
|
117
|
+
@page.out_of_bounds?.must_equal false
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should be true if negative page" do
|
121
|
+
@page.current_page = -1
|
122
|
+
@page.out_of_bounds?.must_equal true
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should be true if zero page" do
|
126
|
+
@page.current_page = 0
|
127
|
+
@page.out_of_bounds?.must_equal true
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should be true after known last_page" do
|
131
|
+
@page.current_page = 4
|
132
|
+
@page.out_of_bounds?.must_equal true
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should not care about large numbers when total_pages not known" do
|
136
|
+
@page.total_entries = nil
|
137
|
+
@page.current_page = 50
|
138
|
+
@page.out_of_bounds?.must_equal false
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "decorate" do
|
143
|
+
before do
|
144
|
+
@page = Folio::Ordinal::Page.decorate([])
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should add page methods to the object" do
|
148
|
+
assert_respond_to @page, :current_page
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should add ordinal page methods to the object" do
|
152
|
+
@page.first_page.must_equal 1
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should preserve other methods on the object" do
|
156
|
+
assert_respond_to @page, :each
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe "create" do
|
161
|
+
before do
|
162
|
+
@page = Folio::Ordinal::Page.create
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should be an Array at heart" do
|
166
|
+
@page.must_be :is_a?, Array
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should be decorated as an ordinal page" do
|
170
|
+
@page.first_page.must_equal 1
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'folio/ordinal'
|
3
|
+
|
4
|
+
describe Folio::Ordinal do
|
5
|
+
before do
|
6
|
+
klass = Class.new do
|
7
|
+
def build_page
|
8
|
+
Folio::Page.create
|
9
|
+
end
|
10
|
+
|
11
|
+
def fill_page(page)
|
12
|
+
page
|
13
|
+
end
|
14
|
+
|
15
|
+
include Folio::Ordinal
|
16
|
+
end
|
17
|
+
@folio = klass.new
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "paginate" do
|
21
|
+
it "should decorate the result of build_page" do
|
22
|
+
@page = @folio.paginate
|
23
|
+
@page.is_a?(Folio::Ordinal::Page).must_equal true
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "bounds checking" do
|
27
|
+
before do
|
28
|
+
@folio.per_page = 10
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should raise on non-integer page" do
|
32
|
+
lambda{ @folio.paginate(page: "non-integer") }.must_raise Folio::InvalidPage
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should raise on negative page" do
|
36
|
+
lambda{ @folio.paginate(page: -1) }.must_raise Folio::InvalidPage
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should raise on page of 0" do
|
40
|
+
lambda{ @folio.paginate(page: 0) }.must_raise Folio::InvalidPage
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should raise on page greater than known last_page" do
|
44
|
+
lambda{ @folio.paginate(page: 4, total_entries: 30) }.must_raise Folio::InvalidPage
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should not raise on page number between first_page and known last_page" do
|
48
|
+
@folio.paginate(page: 1, total_entries: 30)
|
49
|
+
@folio.paginate(page: 3, total_entries: 30)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not raise on large page if last_page unknown" do
|
53
|
+
@folio.paginate(page: 100)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|