pagy 5.7.5 → 8.6.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 (153) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/apps/calendar.ru +745 -0
  4. data/apps/demo.ru +435 -0
  5. data/apps/rails.ru +213 -0
  6. data/apps/repro.ru +177 -0
  7. data/apps/tmp/calendar.sqlite3 +0 -0
  8. data/apps/tmp/calendar.sqlite3-shm +0 -0
  9. data/apps/tmp/calendar.sqlite3-wal +0 -0
  10. data/apps/tmp/local_secret.txt +1 -0
  11. data/bin/pagy +100 -0
  12. data/{lib/config → config}/pagy.rb +72 -96
  13. data/javascripts/pagy-module.js +100 -0
  14. data/javascripts/pagy.d.ts +5 -0
  15. data/javascripts/pagy.js +4 -0
  16. data/javascripts/pagy.min.js +4 -0
  17. data/javascripts/pagy.min.js.map +10 -0
  18. data/javascripts/pagy.mjs +100 -0
  19. data/lib/optimist.rb +1022 -0
  20. data/lib/pagy/backend.rb +16 -4
  21. data/lib/pagy/calendar/day.rb +22 -10
  22. data/lib/pagy/calendar/month.rb +35 -9
  23. data/lib/pagy/calendar/quarter.rb +36 -10
  24. data/lib/pagy/calendar/unit.rb +106 -0
  25. data/lib/pagy/calendar/week.rb +16 -16
  26. data/lib/pagy/calendar/year.rb +15 -9
  27. data/lib/pagy/calendar.rb +60 -68
  28. data/lib/pagy/console.rb +3 -3
  29. data/lib/pagy/countless.rb +17 -12
  30. data/lib/pagy/exceptions.rb +1 -1
  31. data/lib/pagy/extras/arel.rb +2 -2
  32. data/lib/pagy/extras/array.rb +2 -2
  33. data/lib/pagy/extras/bootstrap.rb +57 -53
  34. data/lib/pagy/extras/bulma.rb +53 -57
  35. data/lib/pagy/extras/calendar.rb +54 -37
  36. data/lib/pagy/extras/countless.rb +3 -3
  37. data/lib/pagy/extras/elasticsearch_rails.rb +12 -11
  38. data/lib/pagy/extras/foundation.rb +59 -54
  39. data/lib/pagy/extras/gearbox.rb +32 -19
  40. data/lib/pagy/extras/headers.rb +11 -11
  41. data/lib/pagy/extras/i18n.rb +5 -5
  42. data/lib/pagy/extras/items.rb +34 -24
  43. data/lib/pagy/extras/{frontend_helpers.rb → js_tools.rb} +24 -24
  44. data/lib/pagy/extras/jsonapi.rb +79 -0
  45. data/lib/pagy/extras/materialize.rb +63 -47
  46. data/lib/pagy/extras/meilisearch.rb +26 -22
  47. data/lib/pagy/extras/metadata.rb +13 -9
  48. data/lib/pagy/extras/overflow.rb +13 -10
  49. data/lib/pagy/extras/pagy.rb +82 -0
  50. data/lib/pagy/extras/searchkick.rb +12 -11
  51. data/lib/pagy/extras/semantic.rb +57 -45
  52. data/lib/pagy/extras/size.rb +40 -0
  53. data/lib/pagy/extras/standalone.rb +12 -16
  54. data/lib/pagy/extras/trim.rb +13 -13
  55. data/lib/pagy/extras/uikit.rb +60 -46
  56. data/lib/pagy/frontend.rb +62 -45
  57. data/lib/pagy/i18n.rb +4 -3
  58. data/lib/pagy/url_helpers.rb +13 -25
  59. data/lib/pagy.rb +61 -59
  60. data/locales/ar.yml +28 -0
  61. data/locales/be.yml +25 -0
  62. data/locales/bg.yml +21 -0
  63. data/locales/bs.yml +25 -0
  64. data/locales/ca.yml +21 -0
  65. data/locales/ckb.yml +18 -0
  66. data/locales/cs.yml +23 -0
  67. data/locales/da.yml +21 -0
  68. data/locales/de.yml +21 -0
  69. data/locales/en.yml +21 -0
  70. data/locales/es.yml +21 -0
  71. data/locales/fr.yml +21 -0
  72. data/locales/hr.yml +25 -0
  73. data/locales/id.yml +19 -0
  74. data/locales/it.yml +21 -0
  75. data/locales/ja.yml +19 -0
  76. data/locales/km.yml +19 -0
  77. data/locales/ko.yml +17 -0
  78. data/locales/nb.yml +21 -0
  79. data/locales/nl.yml +21 -0
  80. data/locales/nn.yml +21 -0
  81. data/locales/pl.yml +25 -0
  82. data/locales/pt-BR.yml +21 -0
  83. data/locales/pt.yml +21 -0
  84. data/locales/ru.yml +25 -0
  85. data/locales/sr.yml +25 -0
  86. data/locales/sv-SE.yml +21 -0
  87. data/locales/sv.yml +21 -0
  88. data/locales/sw.yml +23 -0
  89. data/locales/ta.yml +23 -0
  90. data/locales/tr.yml +19 -0
  91. data/locales/uk.yml +25 -0
  92. data/locales/vi.yml +17 -0
  93. data/locales/zh-CN.yml +19 -0
  94. data/locales/zh-HK.yml +19 -0
  95. data/locales/zh-TW.yml +19 -0
  96. data/stylesheets/pagy.css +46 -0
  97. data/stylesheets/pagy.scss +48 -0
  98. data/stylesheets/pagy.tailwind.css +21 -0
  99. metadata +79 -66
  100. data/lib/javascripts/pagy-dev.js +0 -118
  101. data/lib/javascripts/pagy-module.d.ts +0 -30
  102. data/lib/javascripts/pagy-module.js +0 -117
  103. data/lib/javascripts/pagy.js +0 -1
  104. data/lib/locales/ar.yml +0 -26
  105. data/lib/locales/bg.yml +0 -22
  106. data/lib/locales/bs.yml +0 -24
  107. data/lib/locales/ca.yml +0 -22
  108. data/lib/locales/cs.yml +0 -22
  109. data/lib/locales/da.yml +0 -22
  110. data/lib/locales/de.yml +0 -22
  111. data/lib/locales/en.yml +0 -22
  112. data/lib/locales/es.yml +0 -22
  113. data/lib/locales/fr.yml +0 -22
  114. data/lib/locales/hr.yml +0 -24
  115. data/lib/locales/id.yml +0 -20
  116. data/lib/locales/it.yml +0 -22
  117. data/lib/locales/ja.yml +0 -20
  118. data/lib/locales/km.yml +0 -19
  119. data/lib/locales/ko.yml +0 -20
  120. data/lib/locales/nb.yml +0 -22
  121. data/lib/locales/nl.yml +0 -22
  122. data/lib/locales/pl.yml +0 -24
  123. data/lib/locales/pt-BR.yml +0 -22
  124. data/lib/locales/pt.yml +0 -22
  125. data/lib/locales/ru.yml +0 -24
  126. data/lib/locales/sr.yml +0 -23
  127. data/lib/locales/sv-SE.yml +0 -23
  128. data/lib/locales/sv.yml +0 -23
  129. data/lib/locales/sw.yml +0 -22
  130. data/lib/locales/ta.yml +0 -22
  131. data/lib/locales/tr.yml +0 -20
  132. data/lib/locales/uk.yml +0 -24
  133. data/lib/locales/zh-CN.yml +0 -20
  134. data/lib/locales/zh-HK.yml +0 -20
  135. data/lib/locales/zh-TW.yml +0 -20
  136. data/lib/pagy/calendar/month_mixin.rb +0 -49
  137. data/lib/pagy/extras/navs.rb +0 -63
  138. data/lib/pagy/extras/support.rb +0 -54
  139. data/lib/templates/bootstrap_nav.html.erb +0 -24
  140. data/lib/templates/bootstrap_nav.html.haml +0 -34
  141. data/lib/templates/bootstrap_nav.html.slim +0 -34
  142. data/lib/templates/bulma_nav.html.erb +0 -24
  143. data/lib/templates/bulma_nav.html.haml +0 -32
  144. data/lib/templates/bulma_nav.html.slim +0 -32
  145. data/lib/templates/foundation_nav.html.erb +0 -24
  146. data/lib/templates/foundation_nav.html.haml +0 -34
  147. data/lib/templates/foundation_nav.html.slim +0 -34
  148. data/lib/templates/nav.html.erb +0 -22
  149. data/lib/templates/nav.html.haml +0 -30
  150. data/lib/templates/nav.html.slim +0 -29
  151. data/lib/templates/uikit_nav.html.erb +0 -15
  152. data/lib/templates/uikit_nav.html.haml +0 -28
  153. data/lib/templates/uikit_nav.html.slim +0 -28
data/lib/pagy/backend.rb CHANGED
@@ -1,8 +1,8 @@
1
- # See Pagy::Backend API documentation: https://ddnexus.github.io/pagy/api/backend
1
+ # See Pagy::Backend API documentation: https://ddnexus.github.io/pagy/docs/api/backend
2
2
  # frozen_string_literal: true
3
3
 
4
4
  class Pagy
5
- # Define a few generic methods to paginate an ORM collection out of the box,
5
+ # Define a few generic methods to paginate a collection out of the box,
6
6
  # or any collection by overriding pagy_get_items and/or pagy_get_vars in your controller
7
7
  # See also the extras if you need specialized methods to paginate Arrays or other collections
8
8
  module Backend
@@ -18,11 +18,23 @@ class Pagy
18
18
  # You may need to override the count call for non AR collections
19
19
  def pagy_get_vars(collection, vars)
20
20
  pagy_set_items_from_params(vars) if defined?(ItemsExtra)
21
- vars[:count] ||= (count = collection.count(:all)).is_a?(Hash) ? count.size : count
22
- vars[:page] ||= params[vars[:page_param] || DEFAULT[:page_param]]
21
+ vars[:count] ||= pagy_get_count(collection, vars)
22
+ vars[:page] ||= pagy_get_page(vars)
23
23
  vars
24
24
  end
25
25
 
26
+ # Get the count from the collection
27
+ def pagy_get_count(collection, vars)
28
+ count_args = vars[:count_args] || DEFAULT[:count_args]
29
+ (count = collection.count(*count_args)).is_a?(Hash) ? count.size : count
30
+ end
31
+
32
+ # Get the page integer from the params
33
+ # Overridable by the jsonapi extra
34
+ def pagy_get_page(vars)
35
+ [params[vars[:page_param] || DEFAULT[:page_param]].to_i, 1].max
36
+ end
37
+
26
38
  # Sub-method called only by #pagy: here for easy customization of record-extraction by overriding
27
39
  # You may need to override this method for collections without offset|limit
28
40
  def pagy_get_items(collection, pagy)
@@ -1,28 +1,40 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
1
+ # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
2
2
  # frozen_string_literal: true
3
3
 
4
4
  class Pagy # :nodoc:
5
5
  class Calendar # :nodoc:
6
- # Calendar day subclass
7
- class Day < Calendar
8
- DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
9
- format: '%Y-%m-%d' }
6
+ # Day unit subclass
7
+ class Day < Unit
8
+ DEFAULT = { size: 31, # rubocop:disable Style/MutableConstant
9
+ ends: false,
10
+ order: :asc,
11
+ format: '%d' }
10
12
 
11
13
  protected
12
14
 
13
15
  # Setup the calendar variables
14
16
  def setup_unit_vars
15
17
  super
16
- @initial = new_time(@starting.year, @starting.month, @starting.day)
17
- @final = new_time(@ending.year, @ending.month, @ending.day) + DAY
18
- @pages = @last = (@final - @initial).to_i / DAY
18
+ @initial = @starting.beginning_of_day
19
+ @final = @ending.tomorrow.beginning_of_day
20
+ @last = page_offset(@initial, @final)
19
21
  @from = starting_time_for(@page)
20
- @to = @from + DAY
22
+ @to = @from.tomorrow
21
23
  end
22
24
 
23
25
  # Starting time for the page
24
26
  def starting_time_for(page)
25
- @initial + (offset_units_for(page) * DAY)
27
+ @initial.days_since(time_offset_for(page))
28
+ end
29
+
30
+ def page_offset_at(time)
31
+ page_offset(@initial, time.beginning_of_day)
32
+ end
33
+
34
+ private
35
+
36
+ def page_offset(time_a, time_b) # remove in 6.0
37
+ (time_b.time - time_a.time).to_i / 1.day
26
38
  end
27
39
  end
28
40
  end
@@ -1,16 +1,42 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
1
+ # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative 'month_mixin'
5
-
6
4
  class Pagy # :nodoc:
7
5
  class Calendar # :nodoc:
8
- # Calendar month subclass
9
- class Month < Calendar
10
- DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
11
- format: '%Y-%m' }
12
- MONTHS = 1 # number of months in the unit
13
- include MonthMixin
6
+ # Month unit subclass
7
+ class Month < Unit
8
+ DEFAULT = { size: 12, # rubocop:disable Style/MutableConstant
9
+ ends: false,
10
+ order: :asc,
11
+ format: '%b' }
12
+
13
+ protected
14
+
15
+ # Setup the calendar variables
16
+ def setup_unit_vars
17
+ super
18
+ @initial = @starting.beginning_of_month
19
+ @final = @ending.next_month.beginning_of_month
20
+ @last = (months_in(@final) - months_in(@initial))
21
+ @from = starting_time_for(@page)
22
+ @to = @from.next_month
23
+ end
24
+
25
+ # Starting time for the page
26
+ def starting_time_for(page)
27
+ @initial.months_since(time_offset_for(page))
28
+ end
29
+
30
+ def page_offset_at(time)
31
+ months_in(time.beginning_of_month) - months_in(@initial)
32
+ end
33
+
34
+ private
35
+
36
+ # Number of months in time
37
+ def months_in(time)
38
+ (time.year * 12) + time.month
39
+ end
14
40
  end
15
41
  end
16
42
  end
@@ -1,23 +1,49 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
1
+ # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
2
2
  # frozen_string_literal: true
3
3
 
4
- require_relative 'month_mixin'
5
-
6
4
  class Pagy # :nodoc:
7
5
  class Calendar # :nodoc:
8
- # Calendar quarter subclass
9
- class Quarter < Calendar
10
- DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
11
- format: '%Y-Q%q' } # '%q' token
12
- MONTHS = 3 # number of months of the unit
13
- include MonthMixin
6
+ # Quarter unit subclass
7
+ class Quarter < Unit
8
+ DEFAULT = { size: 4, # rubocop:disable Style/MutableConstant
9
+ ends: false,
10
+ order: :asc,
11
+ format: 'Q%q' } # '%q' token
14
12
 
15
13
  # The label for any page, with the substitution of the '%q' token
16
14
  def label_for(page, opts = {})
17
15
  starting_time = starting_time_for(page.to_i) # page could be a string
18
- opts[:format] = (opts[:format] || @vars[:format]).gsub('%q') { (starting_time.month / 4) + 1 }
16
+ opts[:format] = (opts[:format] || @vars[:format]).gsub('%q') { (starting_time.month / 3.0).ceil }
19
17
  localize(starting_time, opts)
20
18
  end
19
+
20
+ protected
21
+
22
+ # Setup the calendar variables
23
+ def setup_unit_vars
24
+ super
25
+ @initial = @starting.beginning_of_quarter
26
+ @final = @ending.next_quarter.beginning_of_quarter
27
+ @last = (months_in(@final) - months_in(@initial)) / 3
28
+ @from = starting_time_for(@page)
29
+ @to = @from.next_quarter
30
+ end
31
+
32
+ # Starting time for the page
33
+ def starting_time_for(page)
34
+ @initial.months_since(time_offset_for(page) * 3)
35
+ end
36
+
37
+ def page_offset_at(time)
38
+ (months_in(time.beginning_of_quarter) - months_in(@initial)) / 3
39
+ end
40
+
41
+ private
42
+
43
+ # Number of months in time
44
+ def months_in(time)
45
+ (time.year * 12) + time.month
46
+ end
21
47
  end
22
48
  end
23
49
  end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/core_ext/time'
5
+ require 'active_support/core_ext/date_and_time/calculations'
6
+ require 'active_support/core_ext/numeric/time'
7
+ require 'active_support/core_ext/integer/time'
8
+
9
+ class Pagy # :nodoc:
10
+ class Calendar < Hash # :nodoc:
11
+ # Base class for time units subclasses (Year, Quarter, Month, Week, Day)
12
+ class Unit < Pagy
13
+ attr_reader :order, :from, :to
14
+
15
+ # Merge and validate the options, do some simple arithmetic and set a few instance variables
16
+ def initialize(vars) # rubocop:disable Lint/MissingSuper
17
+ raise InternalError, 'Pagy::Calendar::Unit is a base class; use one of its subclasses' \
18
+ if instance_of?(Pagy::Calendar::Unit)
19
+
20
+ vars = self.class::DEFAULT.merge(vars) # subclass specific default
21
+ normalize_vars(vars) # general default
22
+ setup_vars(page: 1)
23
+ setup_unit_vars
24
+ raise OverflowError.new(self, :page, "in 1..#{@last}", @page) if @page > @last
25
+
26
+ @prev = (@page - 1 unless @page == 1)
27
+ @next = @page == @last ? (1 if @vars[:cycle]) : @page + 1
28
+ end
29
+
30
+ # The label for the current page (it can pass along the I18n gem opts when it's used with the i18n extra)
31
+ def label(opts = {})
32
+ label_for(@page, opts)
33
+ end
34
+
35
+ # The label for any page (it can pass along the I18n gem opts when it's used with the i18n extra)
36
+ def label_for(page, opts = {})
37
+ opts[:format] ||= @vars[:format]
38
+ localize(starting_time_for(page.to_i), opts) # page could be a string
39
+ end
40
+
41
+ protected
42
+
43
+ # The page that includes time
44
+ # In case of out of range time, the :fit_time option avoids the outOfRangeError
45
+ # and returns the closest page to the passed time argument (first or last page)
46
+ def page_at(time, **opts)
47
+ fit_time = time
48
+ fit_final = @final - 1
49
+ unless time.between?(@initial, fit_final)
50
+ raise OutOfRangeError.new(self, :time, "between #{@initial} and #{fit_final}", time) unless opts[:fit_time]
51
+
52
+ if time < @final
53
+ fit_time = @initial
54
+ ordinal = 'first'
55
+ else
56
+ fit_time = fit_final
57
+ ordinal = 'last'
58
+ end
59
+ warn "Pagy::Calendar#page_at: Rescued #{time} out of range by returning the #{ordinal} page."
60
+ end
61
+ offset = page_offset_at(fit_time) # offset starts from 0
62
+ @order == :asc ? offset + 1 : @last - offset
63
+ end
64
+
65
+ # Base class method for the setup of the unit variables (subclasses must implement it and call super)
66
+ def setup_unit_vars
67
+ raise VariableError.new(self, :format, 'to be a strftime format', @vars[:format]) unless @vars[:format].is_a?(String)
68
+ raise VariableError.new(self, :order, 'to be in [:asc, :desc]', @order) \
69
+ unless %i[asc desc].include?(@order = @vars[:order])
70
+
71
+ @starting, @ending = @vars[:period]
72
+ raise VariableError.new(self, :period, 'to be a an Array of min and max TimeWithZone instances', @vars[:period]) \
73
+ unless @starting.is_a?(ActiveSupport::TimeWithZone) \
74
+ && @ending.is_a?(ActiveSupport::TimeWithZone) && @starting <= @ending
75
+ end
76
+
77
+ # Apply the strftime format to the time (overridden by the i18n extra when localization is required)
78
+ def localize(time, opts)
79
+ time.strftime(opts[:format])
80
+ end
81
+
82
+ # Number of time units to offset from the @initial time, in order to get the ordered starting time for the page.
83
+ # Used in starting_time_for(page) where page starts from 1 (e.g. page to starting_time means subtracting 1)
84
+ def time_offset_for(page)
85
+ @order == :asc ? page - 1 : @last - page
86
+ end
87
+
88
+ # Period of the active page (used internally for nested units)
89
+ def active_period
90
+ [[@starting, @from].max, [@to - 1, @ending].min] # -1 sec: include only last unit day
91
+ end
92
+
93
+ # :nocov:
94
+ # This method must be implemented by the unit subclass
95
+ def starting_time_for(*)
96
+ raise NoMethodError, 'the starting_time_for method must be implemented by the unit subclass'
97
+ end
98
+
99
+ # This method must be implemented by the unit subclass
100
+ def page_offset_at(*)
101
+ raise NoMethodError, 'the page_offset_at method must be implemented by the unit subclass'
102
+ end
103
+ # :nocov:
104
+ end
105
+ end
106
+ end
@@ -1,38 +1,38 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
1
+ # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
2
2
  # frozen_string_literal: true
3
3
 
4
4
  class Pagy # :nodoc:
5
5
  class Calendar # :nodoc:
6
- # Calendar week subclass
7
- class Week < Calendar
8
- DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
9
- format: '%Y-%W',
10
- offset: 0 }
6
+ # Week unit subclass
7
+ class Week < Unit
8
+ DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
9
+ format: '%Y-%W' }
11
10
 
12
11
  protected
13
12
 
14
13
  # Setup the calendar variables
15
14
  def setup_unit_vars
16
- setup_vars(offset: 0)
17
15
  super
18
- @initial = unit_starting_time_for(@starting)
19
- @final = unit_starting_time_for(@ending) + WEEK
20
- @pages = @last = (@final - @initial).to_i / WEEK
16
+ @initial = @starting.beginning_of_week
17
+ @final = @ending.next_week.beginning_of_week
18
+ @last = page_offset(@initial, @final)
21
19
  @from = starting_time_for(@page)
22
- @to = @from + WEEK
20
+ @to = @from.next_week
23
21
  end
24
22
 
25
23
  # Starting time for the page
26
24
  def starting_time_for(page)
27
- @initial + (offset_units_for(page) * WEEK)
25
+ @initial.weeks_since(time_offset_for(page))
26
+ end
27
+
28
+ def page_offset_at(time)
29
+ page_offset(@initial, time.beginning_of_week)
28
30
  end
29
31
 
30
32
  private
31
33
 
32
- # Unit starting time for time
33
- def unit_starting_time_for(time)
34
- starting_time = time - (((time.wday - @offset) * DAY) % WEEK)
35
- new_time(starting_time.year, starting_time.month, starting_time.day)
34
+ def page_offset(time_a, time_b) # remove in 6.0
35
+ (time_b.time - time_a.time).to_i / 1.week
36
36
  end
37
37
  end
38
38
  end
@@ -1,11 +1,13 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
1
+ # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
2
2
  # frozen_string_literal: true
3
3
 
4
4
  class Pagy # :nodoc:
5
5
  class Calendar # :nodoc:
6
- # Calendar year subclass
7
- class Year < Calendar
8
- DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
6
+ # Year unit subclass
7
+ class Year < Unit
8
+ DEFAULT = { size: 10, # rubocop:disable Style/MutableConstant
9
+ ends: false,
10
+ order: :asc,
9
11
  format: '%Y' }
10
12
 
11
13
  protected
@@ -13,16 +15,20 @@ class Pagy # :nodoc:
13
15
  # Setup the calendar variables
14
16
  def setup_unit_vars
15
17
  super
16
- @initial = new_time(@starting.year)
17
- @final = new_time(@ending.year + 1)
18
- @pages = @last = @final.year - @initial.year
18
+ @initial = @starting.beginning_of_year
19
+ @final = @ending.next_year.beginning_of_year
20
+ @last = @final.year - @initial.year
19
21
  @from = starting_time_for(@page)
20
- @to = new_time(@from.year + 1)
22
+ @to = @from.next_year
21
23
  end
22
24
 
23
25
  # Starting time for the page
24
26
  def starting_time_for(page)
25
- new_time(@initial.year + offset_units_for(page))
27
+ @initial.years_since(time_offset_for(page))
28
+ end
29
+
30
+ def page_offset_at(time)
31
+ time.beginning_of_year.year - @initial.year
26
32
  end
27
33
  end
28
34
  end
data/lib/pagy/calendar.rb CHANGED
@@ -1,87 +1,79 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
1
+ # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'pagy'
4
+ require_relative '../pagy'
5
+ require_relative 'calendar/unit'
5
6
 
6
7
  class Pagy # :nodoc:
7
- # Base class for time units subclasses (Year, Quarter, Month, Week, Day)
8
- class Calendar < Pagy
8
+ # Calendar class
9
+ class Calendar < Hash
10
+ # Specific out of range error
11
+ class OutOfRangeError < VariableError; end
12
+
9
13
  # List of units in desc order of duration. It can be used for custom units.
10
14
  UNITS = %i[year quarter month week day] # rubocop:disable Style/MutableConstant
11
- DAY = 60 * 60 * 24 # One day in seconds
12
- WEEK = DAY * 7 # One week in seconds
13
-
14
- attr_reader :order
15
-
16
- # Merge and validate the options, do some simple arithmetic and set a few instance variables
17
- def initialize(vars) # rubocop:disable Lint/MissingSuper
18
- raise InternalError, 'Pagy::Calendar is a base class; use one of its subclasses' if instance_of?(Pagy::Calendar)
19
-
20
- vars = self.class::DEFAULT.merge(vars) # subclass specific default
21
- normalize_vars(vars) # general default
22
- setup_vars(page: 1)
23
- setup_unit_vars
24
- setup_params_var
25
- raise OverflowError.new(self, :page, "in 1..#{@last}", @page) if @page > @last
26
-
27
- @prev = (@page - 1 unless @page == 1)
28
- @next = @page == @last ? (1 if @vars[:cycle]) : @page + 1
29
- end
30
-
31
- # The label for the current page (it can pass along the I18n gem opts when it's used with the i18n extra)
32
- def label(opts = {})
33
- label_for(@page, opts)
34
- end
35
15
 
36
- # The label for any page (it can pass along the I18n gem opts when it's used with the i18n extra)
37
- def label_for(page, opts = {})
38
- opts[:format] ||= @vars[:format]
39
- localize(starting_time_for(page.to_i), opts) # page could be a string
40
- end
41
-
42
- protected
43
-
44
- # Base class method for the setup of the unit variables (subclasses must implement it and call super)
45
- def setup_unit_vars
46
- raise VariableError.new(self, :format, 'to be a strftime format', @vars[:format]) unless @vars[:format].is_a?(String)
47
- raise VariableError.new(self, :order, 'to be in [:asc, :desc]', @order) \
48
- unless %i[asc desc].include?(@order = @vars[:order])
16
+ class << self
17
+ private
49
18
 
50
- @starting, @ending = @vars[:period]
51
- raise VariableError.new(self, :period, 'to be a an Array of min and max local Time instances', @vars[:period]) \
52
- unless @starting.is_a?(Time) && @ending.is_a?(Time) && !@starting.utc? && !@ending.utc? && @starting <= @ending \
53
- && (@utc_offset = @starting.utc_offset) == @ending.utc_offset
54
- end
19
+ # Create a unit subclass instance by using the unit name (internal use)
20
+ def create(unit, vars)
21
+ raise InternalError, "unit must be in #{UNITS.inspect}; got #{unit}" unless UNITS.include?(unit)
55
22
 
56
- # Apply the strftime format to the time (overridden by the i18n extra when localization is required)
57
- def localize(time, opts)
58
- time.strftime(opts[:format])
59
- end
23
+ name = unit.to_s
24
+ name[0] = name[0].capitalize
25
+ Object.const_get("Pagy::Calendar::#{name}").new(vars)
26
+ end
60
27
 
61
- # Number of units to offset from the @initial time, in order to get the ordered starting time for the page.
62
- # Used in starting_time_for(page) with a logic equivalent to: @initial + (offset_units_for(page) * unit_time_length)
63
- def offset_units_for(page)
64
- @order == :asc ? page - 1 : @pages - page
28
+ # Return calendar, from, to
29
+ def init(conf, period, params)
30
+ new.send(:init, conf, period, params)
31
+ end
65
32
  end
66
33
 
67
- # Create a new local time at the beginning of the day
68
- def new_time(year, month = 1, day = 1)
69
- Time.new(year, month, day, 0, 0, 0, @utc_offset)
34
+ # Return the current time of the smallest time unit shown
35
+ def showtime
36
+ self[@units.last].from
70
37
  end
71
38
 
72
- # Period of the active page (used internally for nested units)
73
- def active_period
74
- [[@starting, @from].max, [@to - 1, @ending].min] # -1 sec: include only last unit day
39
+ private
40
+
41
+ # Create the calendar
42
+ def init(conf, period, params)
43
+ @conf = Marshal.load(Marshal.dump(conf)) # store a copy
44
+ @units = Calendar::UNITS & @conf.keys # get the units in time length desc order
45
+ raise ArgumentError, 'no calendar unit found in pagy_calendar @configuration' if @units.empty?
46
+
47
+ @period = period
48
+ @params = params
49
+ @page_param = conf[:pagy][:page_param] || DEFAULT[:page_param]
50
+ @units.each do |unit| # set all the :page_param vars for later deletion
51
+ unit_page_param = :"#{unit}_#{@page_param}"
52
+ conf[unit][:page_param] = unit_page_param
53
+ conf[unit][:page] = @params[unit_page_param]
54
+ end
55
+ calendar = {}
56
+ object = nil
57
+ @units.each_with_index do |unit, index|
58
+ params_to_delete = @units[(index + 1), @units.size].map { |sub| conf[sub][:page_param] } + [@page_param]
59
+ conf[unit][:params] = lambda { |up| up.except(*params_to_delete.map(&:to_s)) } # rubocop:disable Style/Lambda
60
+ conf[unit][:period] = object&.send(:active_period) || @period
61
+ calendar[unit] = object = Calendar.send(:create, unit, conf[unit])
62
+ end
63
+ [replace(calendar), object.from, object.to]
75
64
  end
76
65
 
77
- class << self
78
- # Create a subclass instance by unit name (internal use)
79
- def create(unit, vars)
80
- raise InternalError, "unit must be in #{UNITS.inspect}; got #{unit}" unless UNITS.include?(unit)
81
-
82
- name = unit.to_s
83
- name[0] = name[0].capitalize
84
- Object.const_get("Pagy::Calendar::#{name}").new(vars)
66
+ # Return the calendar object at time
67
+ def calendar_at(time, **opts)
68
+ conf = Marshal.load(Marshal.dump(@conf))
69
+ page_params = {}
70
+ @units.inject(nil) do |object, unit|
71
+ conf[unit][:period] = object&.send(:active_period) || @period
72
+ conf[unit][:page] = page_params[:"#{unit}_#{@page_param}"] \
73
+ = Calendar.send(:create, unit, conf[unit]).send(:page_at, time, **opts)
74
+ conf[unit][:params] ||= {}
75
+ conf[unit][:params].merge!(page_params)
76
+ Calendar.send(:create, unit, conf[unit])
85
77
  end
86
78
  end
87
79
  end
data/lib/pagy/console.rb CHANGED
@@ -1,8 +1,8 @@
1
- # See Pagy::Console API documentation: https://ddnexus.github.io/pagy/api/console
1
+ # See Pagy::Console API documentation: https://ddnexus.github.io/pagy/docs/api/console
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'pagy' # so you can require just the extra in the console
5
- require 'pagy/extras/standalone'
4
+ require_relative '../pagy' # so you can require just the extra in the console
5
+ require_relative 'extras/standalone'
6
6
 
7
7
  class Pagy
8
8
  # Provide a ready to use pagy environment when included in irb/rails console
@@ -1,9 +1,9 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/countless
1
+ # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/countless
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'pagy'
4
+ require_relative '../pagy'
5
5
 
6
- class Pagy
6
+ class Pagy # :nodoc:
7
7
  # No need to know the count to paginate
8
8
  class Countless < Pagy
9
9
  # Merge and validate the options, do some simple arithmetic and set a few instance variables
@@ -11,27 +11,32 @@ class Pagy
11
11
  normalize_vars(vars)
12
12
  setup_vars(page: 1, outset: 0)
13
13
  setup_items_var
14
- setup_params_var
15
- @offset = (@items * (@page - 1)) + @outset
14
+ setup_offset_var
16
15
  end
17
16
 
18
17
  # Finalize the instance variables based on the fetched size
19
18
  def finalize(fetched_size)
20
19
  raise OverflowError.new(self, :page, "to be < #{@page}", @page) if fetched_size.zero? && @page > 1
21
20
 
22
- @pages = @last = (fetched_size > @items ? @page + 1 : @page)
23
- @in = [fetched_size, @items].min
24
- @from = @in.zero? ? 0 : @offset - @outset + 1
25
- @to = @offset - @outset + @in
26
- @prev = (@page - 1 unless @page == 1)
27
- @next = @page == @last ? (1 if @vars[:cycle]) : @page + 1
21
+ @last = fetched_size > @items ? @page + 1 : @page
22
+ @last = vars[:max_pages] if vars[:max_pages] && @last > vars[:max_pages]
23
+ raise OverflowError.new(self, :page, "in 1..#{@last}", @page) if @page > @last
24
+
25
+ @in = [fetched_size, @items].min
26
+ @from = @in.zero? ? 0 : @offset - @outset + 1
27
+ @to = @offset - @outset + @in
28
+ @prev = (@page - 1 unless @page == 1)
29
+ @next = @page == @last ? (1 if @vars[:cycle]) : @page + 1
28
30
  self
29
31
  end
32
+ end
30
33
 
34
+ module SeriesOverride # :nodoc:
31
35
  # Override the original series.
32
36
  # Return nil if :countless_minimal is enabled
33
- def series(*)
37
+ def series(**)
34
38
  super unless @vars[:countless_minimal]
35
39
  end
36
40
  end
41
+ prepend SeriesOverride
37
42
  end
@@ -10,7 +10,7 @@ class Pagy
10
10
  @pagy = pagy
11
11
  @variable = variable
12
12
  @value = value
13
- super "expected :#{@variable} #{description}; got #{@value.inspect}"
13
+ super("expected :#{@variable} #{description}; got #{@value.inspect}")
14
14
  end
15
15
  end
16
16
 
@@ -1,4 +1,4 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/arel
1
+ # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/arel
2
2
  # frozen_string_literal: true
3
3
 
4
4
  class Pagy # :nodoc:
@@ -16,7 +16,7 @@ class Pagy # :nodoc:
16
16
  def pagy_arel_get_vars(collection, vars)
17
17
  pagy_set_items_from_params(vars) if defined?(ItemsExtra)
18
18
  vars[:count] ||= pagy_arel_count(collection)
19
- vars[:page] ||= params[vars[:page_param] || DEFAULT[:page_param]]
19
+ vars[:page] ||= pagy_get_page(vars)
20
20
  vars
21
21
  end
22
22