folio-pagination 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|