folio-pagination-legacy 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.
Files changed (38) hide show
  1. data/.gitignore +17 -0
  2. data/.travis.yml +3 -0
  3. data/Gemfile +16 -0
  4. data/Guardfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +235 -0
  7. data/Rakefile +11 -0
  8. data/folio-pagination-legacy.gemspec +30 -0
  9. data/lib/folio-pagination-legacy.rb +3 -0
  10. data/lib/folio.rb +84 -0
  11. data/lib/folio/core_ext/enumerable.rb +51 -0
  12. data/lib/folio/invalid_page.rb +5 -0
  13. data/lib/folio/ordinal.rb +33 -0
  14. data/lib/folio/ordinal/page.rb +107 -0
  15. data/lib/folio/page.rb +86 -0
  16. data/lib/folio/per_page.rb +15 -0
  17. data/lib/folio/rails.rb +21 -0
  18. data/lib/folio/version.rb +3 -0
  19. data/lib/folio/will_paginate/active_record.rb +71 -0
  20. data/lib/folio/will_paginate/array.rb +10 -0
  21. data/lib/folio/will_paginate/collection.rb +19 -0
  22. data/lib/folio/will_paginate/view_helpers.rb +59 -0
  23. data/lib/folio/will_paginate/view_helpers/action_view.rb +10 -0
  24. data/lib/folio/will_paginate/view_helpers/link_renderer.rb +53 -0
  25. data/test/folio/core_ext/enumerable_test.rb +55 -0
  26. data/test/folio/ordinal/page_test.rb +173 -0
  27. data/test/folio/ordinal_test.rb +57 -0
  28. data/test/folio/page_test.rb +198 -0
  29. data/test/folio/per_page_test.rb +49 -0
  30. data/test/folio/rails_test.rb +11 -0
  31. data/test/folio/version_test.rb +8 -0
  32. data/test/folio/will_paginate/active_record_test.rb +113 -0
  33. data/test/folio/will_paginate/collection_test.rb +43 -0
  34. data/test/folio/will_paginate/view_helpers/link_renderer_test.rb +160 -0
  35. data/test/folio/will_paginate/view_helpers_test.rb +49 -0
  36. data/test/folio_test.rb +192 -0
  37. data/test/setup/active_record.rb +13 -0
  38. metadata +133 -0
@@ -0,0 +1,5 @@
1
+ # Raised when a value passed to a Folio's paginate method is
2
+ # non-sensical or out of bounds for that folio.
3
+ module Folio
4
+ class InvalidPage < ArgumentError; end
5
+ end
@@ -0,0 +1,33 @@
1
+ require 'folio'
2
+ require 'folio/ordinal/page'
3
+ require 'folio/invalid_page'
4
+
5
+ # Mix in to a source to provide the same methods as Folio, but with simpler
6
+ # build_page and fill_page methods required on the host (some responsibility is
7
+ # moved into the paginate method).
8
+ #
9
+ # * build_page no longer needs to configure ordinal_page?, first_page,
10
+ # or last_page on the instantiated page. Instead, just instantiate
11
+ # and return a Folio::Ordinal::Page. If what you return is not a
12
+ # Folio::Ordinal::Page, paginate will decorate it to be one.
13
+ #
14
+ # * fill_page no longer needs to configure next_page and previous_page; the
15
+ # ordinal page will handle them. (Note that if necessary, you can still set
16
+ # next_page explicitly to nil.) Also, paginate will now perform ordinal
17
+ # bounds checking for you, so you can focus entirely on populating the page.
18
+ #
19
+ module Folio
20
+ module Ordinal
21
+ # decorate the page before configuring, and then validate the configured
22
+ # current_page before returning it
23
+ def configure_pagination(page, options)
24
+ page = super(::Folio::Ordinal::Page.decorate(page), options)
25
+ raise ::Folio::InvalidPage unless page.current_page.is_a?(Integer)
26
+ raise ::Folio::InvalidPage if page.out_of_bounds?
27
+ page
28
+ end
29
+
30
+ # otherwise acts like a normal folio
31
+ include ::Folio
32
+ end
33
+ end
@@ -0,0 +1,107 @@
1
+ require 'folio/page'
2
+
3
+ # Mix in to an Enumerable to provide the same methods as Folio::Page but with
4
+ # the following overrides:
5
+ #
6
+ # * ordinal_pages is always true
7
+ #
8
+ # * first_page is always 1
9
+ #
10
+ # * current_page is forced to an integer
11
+ #
12
+ # * previous_page is always either current_page-1 or nil, depending on how
13
+ # current_page relates to first_page.
14
+ #
15
+ # * next_page can only be set if total_pages is unknown. if total_pages is
16
+ # known, next_page will be either current_page+1 or nil, depending on how
17
+ # current_page relates to last_page. if total_pages is unknown and next_page
18
+ # is unset (vs. explicitly set to nil), it will default to current_page+1.
19
+ # if next_page is set to a non-nil value, that value will be forced to an
20
+ # integer.
21
+ #
22
+ # * last_page is deterministic: always total_pages if total_pages is known,
23
+ # current_page if total_pages is unknown and next_page is nil, nil otherwise
24
+ # (indicating the page sequence continues until next_page is nil).
25
+ module Folio
26
+ module Ordinal
27
+ module Page
28
+ def ordinal_pages
29
+ true
30
+ end
31
+ alias :ordinal_pages? :ordinal_pages
32
+
33
+ def first_page
34
+ 1
35
+ end
36
+
37
+ def last_page
38
+ (total_pages || next_page) ? total_pages : current_page
39
+ end
40
+
41
+ def current_page=(value)
42
+ @current_page = value.to_i
43
+ end
44
+
45
+ def next_page=(value)
46
+ @next_page = value && value.to_i
47
+ end
48
+
49
+ def next_page
50
+ if total_pages && current_page >= total_pages
51
+ # known number of pages and we've reached the last one. no next page
52
+ # (even if explicitly set)
53
+ nil
54
+ elsif total_pages || !defined?(@next_page)
55
+ # (1) known number of pages and we haven't reached the last one
56
+ # (because we're not in the branch above), or
57
+ # (2) unknown number of pages, but nothing set, so we assume an
58
+ # infinite stream
59
+ # so there's a next page, and it's the one after this one
60
+ current_page + 1
61
+ else
62
+ # just use what they set
63
+ @next_page
64
+ end
65
+ end
66
+
67
+ def previous_page
68
+ current_page > first_page ? current_page - 1 : nil
69
+ end
70
+
71
+ def out_of_bounds?
72
+ (current_page < first_page) || (last_page && current_page > last_page) || false
73
+ end
74
+
75
+ def offset
76
+ (current_page - 1) * per_page
77
+ end
78
+
79
+ class Decorator < Folio::Page::Decorator
80
+ include Folio::Ordinal::Page
81
+ end
82
+
83
+ class DecoratedArray < Decorator
84
+ def initialize
85
+ super []
86
+ end
87
+
88
+ def replace(array)
89
+ result = super
90
+ if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
91
+ self.total_entries = offset + length
92
+ end
93
+ result
94
+ end
95
+ end
96
+
97
+ def self.decorate(collection)
98
+ collection = Folio::Ordinal::Page::Decorator.new(collection) unless collection.is_a?(Folio::Ordinal::Page)
99
+ collection
100
+ end
101
+
102
+ def self.create
103
+ Folio::Ordinal::Page::DecoratedArray.new
104
+ end
105
+ end
106
+ end
107
+ end
data/lib/folio/page.rb ADDED
@@ -0,0 +1,86 @@
1
+ require 'folio/per_page'
2
+ require 'delegate'
3
+
4
+ # Mix into any Enumerable. The mixin gives you the eight attributes and
5
+ # one method described below.
6
+ #
7
+ # ordinal_pages?, first_page, and last_page are common to all pages
8
+ # created by a folio and are configured, as available, when the folio
9
+ # creates a blank page.
10
+ #
11
+ # current_page, per_page, and total_entries control the filling of a
12
+ # page and are configured from parameters to the folio's paginate
13
+ # method.
14
+ #
15
+ # next_page and previous_page are configured, as available, when the
16
+ # folio fills the configured page.
17
+ module Folio
18
+ module Page
19
+ # indicates whether the page identifiers in current_page,
20
+ # first_page, last_page, previous_page, and next_page should be
21
+ # considered ordinal or not.
22
+ attr_accessor :ordinal_pages
23
+ alias :ordinal_pages? :ordinal_pages
24
+
25
+ # page identifier addressing this page within the folio.
26
+ attr_accessor :current_page
27
+
28
+ # number of items requested from the folio when filling the page.
29
+ include Folio::PerPage
30
+
31
+ # page identifier addressing the first page within the folio.
32
+ attr_accessor :first_page
33
+
34
+ # page identifier addressing the final page within the folio, if
35
+ # known.
36
+ def last_page=(value)
37
+ @last_page = value
38
+ end
39
+
40
+ def last_page
41
+ if next_page.nil?
42
+ current_page
43
+ else
44
+ @last_page
45
+ end
46
+ end
47
+
48
+ # page identifier addressing the immediately following page within
49
+ # the folio, if there is one.
50
+ attr_accessor :next_page
51
+
52
+ # page identifier addressing the immediately preceding page within
53
+ # the folio, if there is one and it is known.
54
+ attr_accessor :previous_page
55
+
56
+ # number of items in the folio, if known.
57
+ attr_accessor :total_entries
58
+
59
+ # number of pages in the folio, if known. calculated from
60
+ # total_entries and per_page.
61
+ def total_pages
62
+ return nil unless total_entries && per_page && per_page > 0
63
+ return 1 if total_entries <= 0
64
+ (total_entries / per_page.to_f).ceil
65
+ end
66
+
67
+ class Decorator < ::SimpleDelegator
68
+ include Folio::Page
69
+ end
70
+
71
+ class DecoratedArray < Decorator
72
+ def initialize
73
+ super []
74
+ end
75
+ end
76
+
77
+ def self.decorate(collection)
78
+ collection = Folio::Page::Decorator.new(collection) unless collection.is_a?(Folio::Page)
79
+ collection
80
+ end
81
+
82
+ def self.create
83
+ Folio::Page::DecoratedArray.new
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,15 @@
1
+ module Folio
2
+ module PerPage
3
+ def default_per_page
4
+ Folio.per_page
5
+ end
6
+
7
+ def per_page(*args)
8
+ raise ArgumentError if args.size > 1
9
+ @per_page = (args.first && args.first.to_i) if args.size > 0
10
+ @per_page ? @per_page : default_per_page
11
+ end
12
+
13
+ alias_method :per_page=, :per_page
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'active_support/core_ext/module/attribute_accessors'
3
+ require 'action_controller'
4
+ rescue LoadError
5
+ raise "folio-pagination-legacy's rails support requires rails"
6
+ end
7
+
8
+ begin
9
+ require 'will_paginate/view_helpers'
10
+ rescue LoadError
11
+ raise "folio-pagination-legacy's rails support requires will_paginate"
12
+ end
13
+
14
+ require 'folio/will_paginate/view_helpers'
15
+
16
+ ActionView::Base.send :include, WillPaginate::ViewHelpers
17
+ if defined?(ActionController::Base) and ActionController::Base.respond_to? :rescue_responses
18
+ ActionController::Base.rescue_responses['Folio::InvalidPage'] = :not_found
19
+ end
20
+
21
+ require 'folio/will_paginate/active_record'
@@ -0,0 +1,3 @@
1
+ module Folio
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,71 @@
1
+ # place WillPaginate's versions in place first to ensure the folio versions
2
+ # included later are used
3
+ begin
4
+ require 'active_record'
5
+ rescue LoadError
6
+ raise "folio-pagination-legacy's activerecord support requires activerecord"
7
+ end
8
+
9
+ begin
10
+ require 'will_paginate/finder'
11
+ rescue LoadError
12
+ raise "folio-pagination-legacy's activerecord support requires will_paginate"
13
+ end
14
+
15
+ ActiveRecord::Base.send :include, WillPaginate::Finder
16
+ ActiveRecord::Associations::AssociationCollection.send :include, WillPaginate::Finder::ClassMethods
17
+
18
+ require 'folio/ordinal'
19
+ require 'folio/ordinal/page'
20
+ require 'folio/will_paginate/array'
21
+
22
+ module Folio
23
+ module WillPaginate
24
+ # replaces model_or_association.paginate(...) method to behave like that
25
+ # from Folio while preserving basic mechanics of the version from
26
+ # WillPaginate.
27
+ #
28
+ # differences:
29
+ #
30
+ # * paginate does *not* recognize any parameters beyond those described in
31
+ # for Folio's paginate (e.g. finder options, count options).
32
+ #
33
+ module ActiveRecord
34
+ module Pagination
35
+ include Folio::Ordinal
36
+
37
+ def paginate_with_wp_count(options={})
38
+ unless options.has_key?(:total_entries)
39
+ page = (options.fetch(:page) { 1 }).to_i
40
+ per_page = (options.fetch(:per_page) { self.per_page }).to_i
41
+ offset = (page - 1) * per_page
42
+ options[:total_entries] = wp_count({}, [:all, {offset: offset, limit: per_page}], 'find')
43
+ end
44
+ paginate_without_wp_count(options)
45
+ end
46
+ alias_method_chain :paginate, :wp_count
47
+
48
+ def build_page
49
+ Folio::Ordinal::Page.create
50
+ end
51
+
52
+ # load the results and place them in the page
53
+ def fill_page(page)
54
+ page.replace self.find(:all, offset: page.offset, limit: page.per_page)
55
+ page
56
+ end
57
+
58
+ # don't try and look at Class (ActiveRecord::Base.class, etc.) for
59
+ # defaults
60
+ def default_per_page
61
+ Folio.per_page
62
+ end
63
+ end
64
+
65
+ # mix into Active Record. these are the same ones that WillPaginate mixes
66
+ # its version of pagination into.
67
+ ::ActiveRecord::Base.extend Pagination
68
+ ::ActiveRecord::Associations::AssociationCollection.send(:include, Pagination)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,10 @@
1
+ require 'will_paginate/array'
2
+ require 'folio/core_ext/enumerable'
3
+
4
+ # make sure Array#paginate uses to folio version from Enumerable, not the one
5
+ # will_paginate added.
6
+ class Array
7
+ def paginate(*args)
8
+ super
9
+ end
10
+ 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,59 @@
1
+ require 'folio'
2
+ require 'folio/will_paginate/view_helpers/link_renderer'
3
+ require 'will_paginate/view_helpers'
4
+
5
+ module Folio
6
+ module WillPaginate
7
+ module ViewHelpers
8
+ # just copied from ::WillPaginate::ViewHelpers except line 20 (changed from
9
+ # "unless value > 1" to "if value == 1" to be friendly to unknown
10
+ # total_pages)
11
+ def will_paginate_with_folio(collection = nil, options = {})
12
+ options, collection = collection, nil if collection.is_a? Hash
13
+ unless collection or !controller
14
+ collection_name = "@#{controller.controller_name}"
15
+ collection = instance_variable_get(collection_name)
16
+ raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " +
17
+ "forget to pass the collection object for will_paginate?" unless collection
18
+ end
19
+ # early exit if there is nothing to render
20
+ return nil if ::WillPaginate::ViewHelpers.total_pages_for_collection(collection) == 1
21
+
22
+ options = options.symbolize_keys.reverse_merge ::WillPaginate::ViewHelpers.pagination_options
23
+ if options[:prev_label]
24
+ WillPaginate::Deprecation::warn(":prev_label view parameter is now :previous_label; the old name has been deprecated", caller)
25
+ options[:previous_label] = options.delete(:prev_label)
26
+ end
27
+
28
+ # get the renderer instance
29
+ renderer = case options[:renderer]
30
+ when String
31
+ options[:renderer].to_s.constantize.new
32
+ when Class
33
+ options[:renderer].new
34
+ else
35
+ options[:renderer]
36
+ end
37
+ # render HTML for pagination
38
+ renderer.prepare collection, options, self
39
+ renderer.to_html
40
+ end
41
+
42
+ def page_entries_info_with_folio(collection, options = {})
43
+ # skip outputting anything unless the collection has ordinal pages (to
44
+ # be able to get an offset) *and* a known total count.
45
+ return unless collection.total_entries && collection.ordinal_pages
46
+ page_entries_info_without_folio(collection, options)
47
+ end
48
+
49
+ def self.included(klass)
50
+ [:will_paginate, :page_entries_info].each do |method|
51
+ klass.send(:alias_method, :"#{method}_without_folio", method)
52
+ klass.send(:alias_method, method, :"#{method}_with_folio")
53
+ end
54
+ end
55
+
56
+ ::WillPaginate::ViewHelpers.send :include, self
57
+ end
58
+ end
59
+ end