pagy 5.7.5 → 8.6.2

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