folio-pagination-legacy 0.0.2

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