pagy 9.4.0 → 43.5.5

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 (176) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/apps/calendar.ru +566 -562
  4. data/apps/demo.ru +246 -182
  5. data/apps/enable_rails_page_segment.rb +56 -0
  6. data/apps/index.rb +4 -2
  7. data/apps/keynav+root_key.ru +316 -0
  8. data/apps/keynav.ru +255 -0
  9. data/apps/{keyset_ar.ru → keyset.ru} +33 -42
  10. data/apps/{keyset_s.ru → keyset_sequel.ru} +32 -40
  11. data/apps/rails.ru +57 -58
  12. data/apps/repro.ru +54 -51
  13. data/bin/pagy +2 -95
  14. data/config/pagy.rb +33 -207
  15. data/javascripts/ai_widget.js +90 -0
  16. data/javascripts/pagy.js +166 -0
  17. data/javascripts/pagy.min.js +2 -4
  18. data/javascripts/pagy.mjs +126 -67
  19. data/javascripts/wand.js +1172 -0
  20. data/lib/pagy/classes/calendar/calendar.rb +101 -0
  21. data/lib/pagy/{calendar → classes/calendar}/day.rb +7 -11
  22. data/lib/pagy/{calendar → classes/calendar}/month.rb +5 -10
  23. data/lib/pagy/{calendar → classes/calendar}/quarter.rb +10 -15
  24. data/lib/pagy/classes/calendar/unit.rb +93 -0
  25. data/lib/pagy/{calendar → classes/calendar}/week.rb +5 -9
  26. data/lib/pagy/{calendar → classes/calendar}/year.rb +5 -6
  27. data/lib/pagy/classes/exceptions.rb +26 -0
  28. data/lib/pagy/classes/keyset/adapters/active_record.rb +50 -0
  29. data/lib/pagy/classes/keyset/adapters/sequel.rb +62 -0
  30. data/lib/pagy/classes/keyset/keynav.rb +85 -0
  31. data/lib/pagy/classes/keyset/keyset.rb +155 -0
  32. data/lib/pagy/classes/offset/countish.rb +17 -0
  33. data/lib/pagy/classes/offset/countless.rb +63 -0
  34. data/lib/pagy/classes/offset/offset.rb +63 -0
  35. data/lib/pagy/classes/offset/search.rb +34 -0
  36. data/lib/pagy/classes/request.rb +48 -0
  37. data/lib/pagy/cli.rb +122 -0
  38. data/lib/pagy/console.rb +5 -20
  39. data/lib/pagy/deprecated.rb +84 -0
  40. data/lib/pagy/modules/abilities/configurable.rb +37 -0
  41. data/lib/pagy/modules/abilities/countable.rb +23 -0
  42. data/lib/pagy/modules/abilities/linkable.rb +72 -0
  43. data/lib/pagy/modules/abilities/rangeable.rb +14 -0
  44. data/lib/pagy/modules/abilities/shiftable.rb +12 -0
  45. data/lib/pagy/{b64.rb → modules/b64.rb} +4 -2
  46. data/lib/pagy/modules/console.rb +33 -0
  47. data/lib/pagy/modules/i18n/i18n.rb +72 -0
  48. data/lib/pagy/modules/i18n/p11n/arabic.rb +30 -0
  49. data/lib/pagy/modules/i18n/p11n/east_slavic.rb +27 -0
  50. data/lib/pagy/modules/i18n/p11n/one_other.rb +15 -0
  51. data/lib/pagy/modules/i18n/p11n/one_upto_two_other.rb +15 -0
  52. data/lib/pagy/modules/i18n/p11n/other.rb +13 -0
  53. data/lib/pagy/modules/i18n/p11n/polish.rb +27 -0
  54. data/lib/pagy/modules/i18n/p11n/west_slavic.rb +22 -0
  55. data/lib/pagy/modules/i18n/p11n.rb +16 -0
  56. data/lib/pagy/modules/searcher.rb +20 -0
  57. data/lib/pagy/next.rb +25 -0
  58. data/lib/pagy/tasks/sync.rb +20 -0
  59. data/lib/pagy/toolbox/helpers/anchor_tags.rb +19 -0
  60. data/lib/pagy/toolbox/helpers/bootstrap/input_nav_js.rb +28 -0
  61. data/lib/pagy/toolbox/helpers/bootstrap/previous_next_html.rb +19 -0
  62. data/lib/pagy/toolbox/helpers/bootstrap/series_nav.rb +32 -0
  63. data/lib/pagy/toolbox/helpers/bootstrap/series_nav_js.rb +24 -0
  64. data/lib/pagy/toolbox/helpers/bulma/input_nav_js.rb +25 -0
  65. data/lib/pagy/toolbox/helpers/bulma/previous_next_html.rb +20 -0
  66. data/lib/pagy/toolbox/helpers/bulma/series_nav.rb +31 -0
  67. data/lib/pagy/toolbox/helpers/bulma/series_nav_js.rb +23 -0
  68. data/lib/pagy/toolbox/helpers/data_hash.rb +29 -0
  69. data/lib/pagy/toolbox/helpers/headers_hash.rb +30 -0
  70. data/lib/pagy/toolbox/helpers/info_tag.rb +30 -0
  71. data/lib/pagy/toolbox/helpers/input_nav_js.rb +22 -0
  72. data/lib/pagy/toolbox/helpers/limit_tag_js.rb +25 -0
  73. data/lib/pagy/toolbox/helpers/loaders.rb +58 -0
  74. data/lib/pagy/toolbox/helpers/page_url.rb +16 -0
  75. data/lib/pagy/toolbox/helpers/series_nav.rb +30 -0
  76. data/lib/pagy/toolbox/helpers/series_nav_js.rb +20 -0
  77. data/lib/pagy/toolbox/helpers/support/a_lambda.rb +36 -0
  78. data/lib/pagy/toolbox/helpers/support/data_pagy_attribute.rb +19 -0
  79. data/lib/pagy/toolbox/helpers/support/nav_aria_label_attribute.rb +10 -0
  80. data/lib/pagy/toolbox/helpers/support/series.rb +37 -0
  81. data/lib/pagy/toolbox/helpers/support/wrap_input_nav_js.rb +18 -0
  82. data/lib/pagy/toolbox/helpers/support/wrap_series_nav.rb +15 -0
  83. data/lib/pagy/toolbox/helpers/support/wrap_series_nav_js.rb +38 -0
  84. data/lib/pagy/toolbox/helpers/urls_hash.rb +12 -0
  85. data/lib/pagy/toolbox/paginators/calendar.rb +34 -0
  86. data/lib/pagy/toolbox/paginators/countish.rb +38 -0
  87. data/lib/pagy/toolbox/paginators/countless.rb +22 -0
  88. data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +56 -0
  89. data/lib/pagy/toolbox/paginators/keynav_js.rb +26 -0
  90. data/lib/pagy/toolbox/paginators/keyset.rb +15 -0
  91. data/lib/pagy/toolbox/paginators/meilisearch.rb +34 -0
  92. data/lib/pagy/toolbox/paginators/method.rb +38 -0
  93. data/lib/pagy/toolbox/paginators/offset.rb +24 -0
  94. data/lib/pagy/toolbox/paginators/searchkick.rb +34 -0
  95. data/lib/pagy/toolbox/paginators/typesense_rails.rb +34 -0
  96. data/lib/pagy.rb +79 -89
  97. data/locales/ar.yml +9 -6
  98. data/locales/be.yml +9 -6
  99. data/locales/bg.yml +9 -6
  100. data/locales/bs.yml +9 -6
  101. data/locales/ca.yml +9 -6
  102. data/locales/ckb.yml +8 -6
  103. data/locales/cs.yml +9 -6
  104. data/locales/da.yml +9 -6
  105. data/locales/de.yml +9 -6
  106. data/locales/dz.yml +9 -6
  107. data/locales/en.yml +9 -6
  108. data/locales/es.yml +9 -6
  109. data/locales/fr.yml +9 -6
  110. data/locales/hr.yml +9 -6
  111. data/locales/hu.yml +24 -0
  112. data/locales/id.yml +10 -9
  113. data/locales/it.yml +9 -6
  114. data/locales/ja.yml +10 -9
  115. data/locales/km.yml +10 -9
  116. data/locales/ko.yml +9 -6
  117. data/locales/nb.yml +9 -6
  118. data/locales/nl.yml +9 -6
  119. data/locales/nn.yml +9 -6
  120. data/locales/pl.yml +9 -6
  121. data/locales/pt-BR.yml +10 -7
  122. data/locales/pt.yml +10 -7
  123. data/locales/ru.yml +9 -6
  124. data/locales/sk.yml +9 -6
  125. data/locales/sr.yml +9 -6
  126. data/locales/sv-SE.yml +9 -6
  127. data/locales/sv.yml +9 -6
  128. data/locales/sw.yml +12 -9
  129. data/locales/ta.yml +9 -6
  130. data/locales/tr.yml +17 -12
  131. data/locales/uk.yml +9 -6
  132. data/locales/vi.yml +9 -6
  133. data/locales/zh-CN.yml +9 -6
  134. data/locales/zh-HK.yml +9 -6
  135. data/locales/zh-TW.yml +9 -6
  136. data/stylesheets/pagy-tailwind.css +68 -0
  137. data/stylesheets/pagy.css +64 -27
  138. metadata +134 -53
  139. data/javascripts/pagy.min.js.map +0 -10
  140. data/lib/optimist.rb +0 -1022
  141. data/lib/pagy/backend.rb +0 -44
  142. data/lib/pagy/calendar/unit.rb +0 -103
  143. data/lib/pagy/calendar.rb +0 -84
  144. data/lib/pagy/countless.rb +0 -38
  145. data/lib/pagy/exceptions.rb +0 -25
  146. data/lib/pagy/extras/arel.rb +0 -28
  147. data/lib/pagy/extras/array.rb +0 -19
  148. data/lib/pagy/extras/bootstrap.rb +0 -97
  149. data/lib/pagy/extras/bulma.rb +0 -93
  150. data/lib/pagy/extras/calendar.rb +0 -79
  151. data/lib/pagy/extras/countless.rb +0 -32
  152. data/lib/pagy/extras/elasticsearch_rails.rb +0 -71
  153. data/lib/pagy/extras/gearbox.rb +0 -55
  154. data/lib/pagy/extras/headers.rb +0 -54
  155. data/lib/pagy/extras/i18n.rb +0 -26
  156. data/lib/pagy/extras/js_tools.rb +0 -70
  157. data/lib/pagy/extras/jsonapi.rb +0 -88
  158. data/lib/pagy/extras/keyset.rb +0 -30
  159. data/lib/pagy/extras/limit.rb +0 -63
  160. data/lib/pagy/extras/meilisearch.rb +0 -57
  161. data/lib/pagy/extras/metadata.rb +0 -42
  162. data/lib/pagy/extras/overflow.rb +0 -81
  163. data/lib/pagy/extras/pagy.rb +0 -82
  164. data/lib/pagy/extras/searchkick.rb +0 -59
  165. data/lib/pagy/extras/size.rb +0 -40
  166. data/lib/pagy/extras/standalone.rb +0 -60
  167. data/lib/pagy/extras/trim.rb +0 -29
  168. data/lib/pagy/frontend.rb +0 -99
  169. data/lib/pagy/i18n.rb +0 -167
  170. data/lib/pagy/keyset/active_record.rb +0 -44
  171. data/lib/pagy/keyset/sequel.rb +0 -57
  172. data/lib/pagy/keyset.rb +0 -118
  173. data/lib/pagy/shared_methods.rb +0 -26
  174. data/lib/pagy/url_helpers.rb +0 -26
  175. data/stylesheets/pagy.scss +0 -48
  176. data/stylesheets/pagy.tailwind.css +0 -21
@@ -0,0 +1,101 @@
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
10
+ # Calendar class, subclass of Hash
11
+ class Calendar < Hash
12
+ path = Pathname.new(__dir__)
13
+ autoload :Unit, path.join('unit')
14
+ autoload :Day, path.join('day')
15
+ autoload :Week, path.join('week')
16
+ autoload :Month, path.join('month')
17
+ autoload :Quarter, path.join('quarter')
18
+ autoload :Year, path.join('year')
19
+
20
+ # List of units in desc order of duration. It can be used for custom units.
21
+ UNITS = %i[year quarter month week day] # rubocop:disable Style/MutableConstant
22
+
23
+ class << self
24
+ def localize_with_rails_i18n_gem(*locales)
25
+ Unit.prepend(Module.new { def localize(...) = ::I18n.localize(...) })
26
+ # :nocov:
27
+ raise RailsI18nLoadError, "Pagy: The gem 'rails-i18n' must be installed if you don't use Rails" \
28
+ unless (path = Gem.loaded_specs['rails-i18n']&.full_gem_path)
29
+
30
+ # :nocov:
31
+ path = Pathname.new(path)
32
+ ::I18n.load_path += locales.map { |locale| path.join("rails/locale/#{locale}.yml") }
33
+ end
34
+
35
+ private
36
+
37
+ # Return calendar, from, to
38
+ def init(...) = new.send(:init, ...)
39
+ end
40
+
41
+ # The current time of the smallest time unit shown
42
+ def showtime = self[@units.last].from
43
+
44
+ # The url for the calendar (shortest unit) page at time
45
+ def url_at(time, **)
46
+ page_keys = {}
47
+
48
+ @units.inject(nil) do |parent, unit|
49
+ unit_conf = @conf[unit]
50
+ unit_conf[:period] = parent&.send(:active_period) || @period
51
+ unit_conf[:page] = page = create(unit, **unit_conf).send(:page_at, time, **)
52
+
53
+ page_keys["#{unit}_#{@page_key}"] = page
54
+ unit_conf[:querify] = ->(params) { params.merge!(page_keys) }
55
+
56
+ create(unit, **unit_conf)
57
+ end.send(:compose_page_url, 1, **)
58
+ end
59
+
60
+ private
61
+
62
+ # Create the calendar
63
+ def init(conf, period, params)
64
+ @conf = conf
65
+ @units = Calendar::UNITS & conf.keys # get the units in time length desc order
66
+ @period = period
67
+ @params = params
68
+ @page_key = conf[:offset][:page_key] || DEFAULT[:page_key]
69
+
70
+ # Set all the :page_key options for later deletion
71
+ @units.each { |unit| conf[unit][:page_key] = "#{unit}_#{@page_key}" }
72
+
73
+ calendar = {}
74
+ unit_object = nil
75
+
76
+ @units.each_with_index do |unit, index|
77
+ params_to_delete = @units[(index + 1)..].map { conf[_1][:page_key] } + [@page_key]
78
+ unit_conf = conf[unit]
79
+ unit_conf[:querify] = ->(up) { up.except!(*params_to_delete.map(&:to_s)) }
80
+ unit_conf[:period] = unit_object&.send(:active_period) || @period
81
+ unit_conf[:page] = @params[unit_conf[:page_key]] # requested page
82
+ # Simplecov doesn't need to cover a fail block_given?
83
+ # :nocov:
84
+ unit_conf[:counts] = yield(unit, unit_conf[:period]) if block_given?
85
+ # :nocov:
86
+ calendar[unit] = unit_object = create(unit, **unit_conf)
87
+ end
88
+
89
+ [replace(calendar), unit_object.from, unit_object.to]
90
+ end
91
+
92
+ # Create an instance of a Unit subclass by using its name (internal use)
93
+ def create(unit, **)
94
+ raise InternalError, "unit must be in #{UNITS.inspect}; got #{unit}" unless UNITS.include?(unit)
95
+
96
+ unit_class = Pagy::Calendar.const_get(unit.to_s.capitalize)
97
+
98
+ unit_class.new(**, request: @conf[:request])
99
+ end
100
+ end
101
+ end
@@ -1,19 +1,16 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
2
1
  # frozen_string_literal: true
3
2
 
4
- class Pagy # :nodoc:
5
- class Calendar # :nodoc:
6
- # Day unit subclass
3
+ class Pagy
4
+ class Calendar
7
5
  class Day < Unit
8
- DEFAULT = { size: 31, # rubocop:disable Style/MutableConstant
9
- ends: false,
10
- order: :asc,
11
- format: '%d' }
6
+ DEFAULT = { slots: 31,
7
+ compact: true,
8
+ order: :asc,
9
+ format: '%d' }.freeze
12
10
 
13
11
  protected
14
12
 
15
- # Set up the calendar variables
16
- def assign_unit_vars
13
+ def assign_unit_variables
17
14
  super
18
15
  @initial = @starting.beginning_of_day
19
16
  @final = @ending.tomorrow.beginning_of_day
@@ -22,7 +19,6 @@ class Pagy # :nodoc:
22
19
  @to = @from.tomorrow
23
20
  end
24
21
 
25
- # Starting time for the page
26
22
  def starting_time_for(page)
27
23
  @initial.days_since(time_offset_for(page))
28
24
  end
@@ -1,19 +1,16 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
2
1
  # frozen_string_literal: true
3
2
 
4
3
  class Pagy # :nodoc:
5
4
  class Calendar # :nodoc:
6
- # Month unit subclass
7
5
  class Month < Unit
8
- DEFAULT = { size: 12, # rubocop:disable Style/MutableConstant
9
- ends: false,
10
- order: :asc,
11
- format: '%b' }
6
+ DEFAULT = { slots: 12,
7
+ compact: true,
8
+ order: :asc,
9
+ format: '%b' }.freeze
12
10
 
13
11
  protected
14
12
 
15
- # Set up the calendar variables
16
- def assign_unit_vars
13
+ def assign_unit_variables
17
14
  super
18
15
  @initial = @starting.beginning_of_month
19
16
  @final = @ending.next_month.beginning_of_month
@@ -22,7 +19,6 @@ class Pagy # :nodoc:
22
19
  @to = @from.next_month
23
20
  end
24
21
 
25
- # Starting time for the page
26
22
  def starting_time_for(page)
27
23
  @initial.months_since(time_offset_for(page))
28
24
  end
@@ -33,7 +29,6 @@ class Pagy # :nodoc:
33
29
 
34
30
  private
35
31
 
36
- # Number of months in time
37
32
  def months_in(time)
38
33
  (time.year * 12) + time.month
39
34
  end
@@ -1,26 +1,23 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
2
1
  # frozen_string_literal: true
3
2
 
4
3
  class Pagy # :nodoc:
5
4
  class Calendar # :nodoc:
6
- # Quarter unit subclass
7
5
  class Quarter < Unit
8
- DEFAULT = { size: 4, # rubocop:disable Style/MutableConstant
9
- ends: false,
10
- order: :asc,
11
- format: 'Q%q' } # '%q' token
6
+ DEFAULT = { slots: 4,
7
+ compact: true,
8
+ order: :asc,
9
+ format: 'Q%q' }.freeze # '%q' token
10
+
11
+ protected
12
12
 
13
13
  # The label for any page, with the substitution of the '%q' token
14
- def label_for(page, opts = {})
14
+ def page_label(page, **options)
15
15
  starting_time = starting_time_for(page.to_i) # page could be a string
16
- opts[:format] = (opts[:format] || @vars[:format]).gsub('%q') { (starting_time.month / 3.0).ceil }
17
- localize(starting_time, opts)
16
+ options[:format] = (options[:format] || @options[:format]).gsub('%q') { (starting_time.month / 3.0).ceil }
17
+ localize(starting_time, **options)
18
18
  end
19
19
 
20
- protected
21
-
22
- # Set up the calendar variables
23
- def assign_unit_vars
20
+ def assign_unit_variables
24
21
  super
25
22
  @initial = @starting.beginning_of_quarter
26
23
  @final = @ending.next_quarter.beginning_of_quarter
@@ -29,7 +26,6 @@ class Pagy # :nodoc:
29
26
  @to = @from.next_quarter
30
27
  end
31
28
 
32
- # Starting time for the page
33
29
  def starting_time_for(page)
34
30
  @initial.months_since(time_offset_for(page) * 3)
35
31
  end
@@ -40,7 +36,6 @@ class Pagy # :nodoc:
40
36
 
41
37
  private
42
38
 
43
- # Number of months in time
44
39
  def months_in(time)
45
40
  (time.year * 12) + time.month
46
41
  end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../modules/abilities/shiftable'
4
+ require_relative '../../modules/abilities/rangeable'
5
+
6
+ class Pagy
7
+ class Calendar
8
+ # Base class for time units subclasses (Year, Quarter, Month, Week, Day).
9
+ #
10
+ # To define a "bimester" unit you should:
11
+ # - Define a `Pagy::Calendar::Bimester` class.
12
+ # - Add the `:bimester` unit symbol in the `Pagy::Calendar::UNITS`.
13
+ # - Ensure the desc duration order of the UNITS (i.e. insert it between `:quarter` and `:month`).
14
+ class Unit < Pagy
15
+ DEFAULT = { page: 1 }.freeze
16
+
17
+ include Rangeable
18
+ include Shiftable
19
+ include NumericHelpers
20
+
21
+ def initialize(**)
22
+ assign_options(**)
23
+ assign_and_check(page: 1)
24
+ assign_unit_variables
25
+ unless in_range? { @page <= @last }
26
+ assign_empty_page_variables
27
+ return
28
+ end
29
+
30
+ assign_previous_and_next
31
+ end
32
+
33
+ attr_reader :order, :from, :to, :previous, :last
34
+ alias pages last
35
+
36
+ protected
37
+
38
+ def calendar? = true
39
+
40
+ # Called by false in_range?
41
+ def assign_empty_page_variables
42
+ @in = @from = @to = 0 # options relative to the actual page
43
+ edge = @order == :asc ? @final : @initial # get the edge of the range (neat, but any time would do)
44
+ @from = @to = edge # set both to the edge time (a >=&&< query will get no records)
45
+ @previous = @last
46
+ end
47
+
48
+ # The page that includes time.
49
+ # In case of time out of range, the :fit_time option avoids the RangeError
50
+ # and returns the closest page to the passed time argument (first or last page).
51
+ def page_at(time, **options)
52
+ fit_time = time
53
+ fit_final = @final - 1
54
+ unless time.between?(@initial, fit_final)
55
+ raise RangeError.new(self, :time, "between #{@initial} and #{fit_final}", time) unless options[:fit_time]
56
+
57
+ fit_time = time < @final ? @initial : fit_final
58
+ end
59
+ offset = page_offset_at(fit_time) # offset starts from 0
60
+ @order == :asc ? offset + 1 : @last - offset
61
+ end
62
+
63
+ # Base class method for the setup of the unit variables (subclasses must implement it and call super).
64
+ def assign_unit_variables
65
+ @order = @options[:order]
66
+ @starting, @ending = @options[:period]
67
+ raise OptionError.new(self, :period, 'to be an Array of min and max TimeWithZone instances', @options[:period]) \
68
+ unless @starting.is_a?(ActiveSupport::TimeWithZone) \
69
+ && @ending.is_a?(ActiveSupport::TimeWithZone) && @starting <= @ending
70
+ end
71
+
72
+ # Apply the strftime format to the time.
73
+ # Localization other than :en, requires the rails-I18n gem.
74
+ def localize(time, **options)
75
+ # Impossible to "unprepend" the rails-i18n after it runs localize_with_rails_i18n_gem in test.
76
+ # :nocov:
77
+ time.strftime(options[:format])
78
+ # :nocov:
79
+ end
80
+
81
+ # The number of time units to offset from the @initial time, in order to get the ordered starting time for the page.
82
+ # Used in starting_time_for(page) where page starts from 1 (e.g. page to starting_time means subtracting 1).
83
+ def time_offset_for(page)
84
+ @order == :asc ? page - 1 : @last - page
85
+ end
86
+
87
+ # Period of the active page (used internally for nested units).
88
+ def active_period
89
+ [[@starting, @from].max, [@to - 1, @ending].min] # -1 sec: include only last unit day
90
+ end
91
+ end
92
+ end
93
+ end
@@ -1,17 +1,14 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
2
1
  # frozen_string_literal: true
3
2
 
4
- class Pagy # :nodoc:
5
- class Calendar # :nodoc:
6
- # Week unit subclass
3
+ class Pagy
4
+ class Calendar
7
5
  class Week < Unit
8
- DEFAULT = { order: :asc, # rubocop:disable Style/MutableConstant
9
- format: '%Y-%W' }
6
+ DEFAULT = { order: :asc,
7
+ format: '%Y-%W' }.freeze
10
8
 
11
9
  protected
12
10
 
13
- # Set up the calendar variables
14
- def assign_unit_vars
11
+ def assign_unit_variables
15
12
  super
16
13
  @initial = @starting.beginning_of_week
17
14
  @final = @ending.next_week.beginning_of_week
@@ -20,7 +17,6 @@ class Pagy # :nodoc:
20
17
  @to = @from.next_week
21
18
  end
22
19
 
23
- # Starting time for the page
24
20
  def starting_time_for(page)
25
21
  @initial.weeks_since(time_offset_for(page))
26
22
  end
@@ -1,19 +1,18 @@
1
- # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
2
1
  # frozen_string_literal: true
3
2
 
4
3
  class Pagy # :nodoc:
5
4
  class Calendar # :nodoc:
6
5
  # Year unit subclass
7
6
  class Year < Unit
8
- DEFAULT = { size: 10, # rubocop:disable Style/MutableConstant
9
- ends: false,
10
- order: :asc,
11
- format: '%Y' }
7
+ DEFAULT = { slots: 10,
8
+ compact: true,
9
+ order: :asc,
10
+ format: '%Y' }.freeze
12
11
 
13
12
  protected
14
13
 
15
14
  # Set up the calendar variables
16
- def assign_unit_vars
15
+ def assign_unit_variables
17
16
  super
18
17
  @initial = @starting.beginning_of_year
19
18
  @final = @ending.next_year.beginning_of_year
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ # Specific subclass of ArgumentError
5
+ class OptionError < ArgumentError
6
+ attr_reader :pagy, :option, :value
7
+
8
+ # Prepare a useful feedback
9
+ def initialize(pagy, option, description, value)
10
+ @pagy = pagy
11
+ @option = option
12
+ @value = value
13
+
14
+ super("expected :#{@option} #{description}; got #{@value.inspect}")
15
+ end
16
+ end
17
+
18
+ # Specific subclass of OptionError
19
+ class RangeError < OptionError; end
20
+
21
+ # I18n localization error
22
+ class RailsI18nLoadError < LoadError; end
23
+
24
+ # Generic internal error
25
+ class InternalError < StandardError; end
26
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ class Keyset
5
+ module Adapters
6
+ # Keyset adapter for ActiveRecord
7
+ module ActiveRecord
8
+ def extract_keyset
9
+ @set.order_values.each_with_object({}) do |node, keyset|
10
+ keyset[node.value.name.to_sym] ||= node.direction
11
+ end
12
+ end
13
+
14
+ def keyset_attributes_from(record)
15
+ record.slice(*@keyset.keys)
16
+ end
17
+
18
+ def quoted_identifiers(table)
19
+ connection = @set.connection
20
+ @keyset.to_h { |column| [column, "#{connection.quote_table_name(table)}.#{connection.quote_column_name(column)}"] }
21
+ end
22
+
23
+ def typecast(attributes)
24
+ model = @set.model
25
+ {}.tap do |result|
26
+ @keyset.each_key do |k|
27
+ result[k] = model.type_for_attribute(k).cast(attributes[k])
28
+ end
29
+ end
30
+ end
31
+
32
+ def ensure_select
33
+ return if @set.select_values.empty?
34
+
35
+ @set = @set.select(*@keyset.keys)
36
+ end
37
+
38
+ def apply_where(predicate, arguments)
39
+ @set = @set.where(predicate, **arguments)
40
+ end
41
+
42
+ def self.included(including)
43
+ instance_methods(false).each do |method_name|
44
+ including.send(:protected, method_name)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ class Keyset
5
+ module Adapters
6
+ # Keyset adapter for Sequel ORM
7
+ module Sequel
8
+ def extract_keyset
9
+ return {} unless @set.opts[:order]
10
+
11
+ @set.opts[:order].each_with_object({}) do |item, keyset|
12
+ case item
13
+ when Symbol
14
+ keyset[item] ||= :asc
15
+ when ::Sequel::SQL::OrderedExpression
16
+ keyset[item.expression] ||= (item.descending ? :desc : :asc)
17
+ else
18
+ raise TypeError, "#{item.class.inspect} is not a supported Sequel::SQL::OrderedExpression"
19
+ end
20
+ end
21
+ end
22
+
23
+ def keyset_attributes_from(record)
24
+ record.to_hash.slice(*@keyset.keys)
25
+ end
26
+
27
+ def quoted_identifiers(table)
28
+ db = @set.db
29
+ @keyset.to_h { |column| [column, "#{db.quote_identifier(table)}.#{db.quote_identifier(column)}"] }
30
+ end
31
+
32
+ def typecast(attributes)
33
+ model = @set.model
34
+ db = @set.db
35
+ {}.tap do |result|
36
+ @keyset.each_key do |k|
37
+ type = model.db_schema[k].fetch(:type)
38
+ result[k] = db.typecast_value(type, attributes[k])
39
+ end
40
+ end
41
+ end
42
+
43
+ def ensure_select
44
+ return if @set.opts[:select].nil?
45
+
46
+ selected = @set.opts[:select]
47
+ @set = @set.select_append(*@keyset.keys.reject { |c| selected.include?(c) })
48
+ end
49
+
50
+ def apply_where(predicate, arguments)
51
+ @set = @set.where(::Sequel.lit(predicate, **arguments))
52
+ end
53
+
54
+ def self.included(including)
55
+ instance_methods(false).each do |method_name|
56
+ including.send(:protected, method_name)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pagy
4
+ class Keyset
5
+ # Keyset pagination with broad UI support
6
+ class Keynav < Keyset
7
+ # Avoid conflicts between filter arguments in composite SQL fragments
8
+ PRIOR_PREFIX = 'prior_'
9
+ PAGE_PREFIX = 'page_'
10
+
11
+ # Define empty subclasses to allow specific typing without triggering autoload
12
+ class ActiveRecord < self; end
13
+ class Sequel < self; end
14
+
15
+ include NumericHelpers
16
+
17
+ # Finalize the instance variables needed for the UI
18
+ def initialize(set, **)
19
+ super
20
+
21
+ @previous = @page - 1 unless @page == 1
22
+ @in = @records.size
23
+ end
24
+
25
+ attr_reader :update, :previous, :last
26
+ alias pages last
27
+
28
+ # Prepare the @update for the client when it's a new page, and return the next page number
29
+ def next
30
+ records
31
+ @count = 0 if !@more && @page == 1 # empty records (trigger the right info message for known 0 count)
32
+ return unless @more
33
+
34
+ @next ||= begin
35
+ unless @page_cutoff
36
+ @page_cutoff = extract_cutoff
37
+ @last += 1 # reflect the added cutoff
38
+ @update.push(@last, [@page, 1, @page_cutoff]) # last + splice arguments for the client cutoffs
39
+ end
40
+ @page + 1
41
+ end
42
+ end
43
+
44
+ protected
45
+
46
+ def keynav? = true
47
+
48
+ # Process the page array
49
+ def assign_page
50
+ if @options[:page]
51
+ storage_key, @page, @last, @prior_cutoff, @page_cutoff = @options[:page]
52
+ else
53
+ @page = @last = 1
54
+ end
55
+
56
+ @update = [storage_key, @options[:root_key], @options[:page_key]]
57
+ end
58
+
59
+ # Use a compound predicate to fetch the records
60
+ def fetch_records
61
+ return super unless @page_cutoff # last page
62
+
63
+ # Compound predicate for visited pages
64
+ predicate = +''
65
+ arguments = {}
66
+
67
+ if @prior_cutoff # not the first page
68
+ # Include the records after @prior_cutoff
69
+ predicate << "(#{compose_predicate(PRIOR_PREFIX)}) AND "
70
+ arguments.merge!(arguments_from(@prior_cutoff, PRIOR_PREFIX))
71
+ end
72
+
73
+ # Exclude the records after @page_cutoff
74
+ predicate << "NOT (#{compose_predicate(PAGE_PREFIX)})"
75
+ arguments.merge!(arguments_from(@page_cutoff, PAGE_PREFIX))
76
+ apply_where(predicate, arguments)
77
+
78
+ @more = true # not the last page
79
+ @set.limit(nil).to_a # replaced by the compound predicate
80
+ end
81
+
82
+ prepend Deprecated::Keynav if defined?(Deprecated::Keynav)
83
+ end
84
+ end
85
+ end