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
@@ -1,82 +0,0 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/pagy
2
- # frozen_string_literal: true
3
-
4
- require_relative 'js_tools'
5
-
6
- class Pagy # :nodoc:
7
- # Frontend modules are specially optimized for performance.
8
- # The resulting code may not look very elegant, but produces the best benchmarks
9
- module PagyExtra
10
- # pagy_nav is defined in the Frontend itself
11
- # Javascript pagination: it returns a nav with a data-pagy attribute used by the pagy.js file
12
- def pagy_nav_js(pagy, id: nil, aria_label: nil, **vars)
13
- sequels = pagy.sequels(**vars)
14
- id = %( id="#{id}") if id
15
- a = pagy_anchor(pagy, **vars)
16
- tokens = { 'before' => prev_a(pagy, a),
17
- 'a' => a.(PAGE_TOKEN, LABEL_TOKEN),
18
- 'current' => %(<a class="current" role="link" aria-current="page" aria-disabled="true">#{
19
- LABEL_TOKEN}</a>),
20
- 'gap' => %(<a class="gap" role="link" aria-disabled="true">#{pagy_t('pagy.gap')}</a>),
21
- 'after' => next_a(pagy, a) }
22
-
23
- %(<nav#{id} class="#{'pagy-rjs ' if sequels.size > 1}pagy nav-js" #{
24
- nav_aria_label(pagy, aria_label:)} #{
25
- pagy_data(pagy, :nav, tokens, sequels, pagy.label_sequels(sequels))
26
- }></nav>)
27
- end
28
-
29
- # Javascript combo pagination: it returns a nav with a data-pagy attribute used by the pagy.js file
30
- def pagy_combo_nav_js(pagy, id: nil, aria_label: nil, **vars)
31
- id = %( id="#{id}") if id
32
- a = pagy_anchor(pagy, **vars)
33
- pages = pagy.pages
34
-
35
- page_input = %(<input name="page" type="number" min="1" max="#{pages}" value="#{pagy.page}" aria-current="page" ) <<
36
- %(style="text-align: center; width: #{pages.to_s.length + 1}rem; padding: 0;">#{JSTools::A_TAG})
37
-
38
- %(<nav#{id} class="pagy combo-nav-js" #{
39
- nav_aria_label(pagy, aria_label:)} #{
40
- pagy_data(pagy, :combo, pagy_url_for(pagy, PAGE_TOKEN, **vars))}>#{
41
- prev_a(pagy, a)
42
- }<label>#{
43
- pagy_t('pagy.combo_nav_js', page_input:, pages:)
44
- }</label>#{
45
- next_a(pagy, a)
46
- }</nav>)
47
- end
48
-
49
- # Return the previous page URL string or nil
50
- def pagy_prev_url(pagy, **vars)
51
- pagy_url_for(pagy, pagy.prev, **vars) if pagy.prev
52
- end
53
-
54
- # Return the next page URL string or nil
55
- def pagy_next_url(pagy, **vars)
56
- pagy_url_for(pagy, pagy.next, **vars) if pagy.next
57
- end
58
-
59
- # Return the enabled/disabled previous page anchor tag
60
- def pagy_prev_a(pagy, text: pagy_t('pagy.prev'), aria_label: pagy_t('pagy.aria_label.prev'), **vars)
61
- a = pagy_anchor(pagy, **vars)
62
- prev_a(pagy, a, text:, aria_label:)
63
- end
64
-
65
- # Return the enabled/disabled next page anchor tag
66
- def pagy_next_a(pagy, text: pagy_t('pagy.next'), aria_label: pagy_t('pagy.aria_label.next'), **vars)
67
- a = pagy_anchor(pagy, **vars)
68
- next_a(pagy, a, text:, aria_label:)
69
- end
70
-
71
- # Conditionally return the previous page link tag
72
- def pagy_prev_link(pagy, **vars)
73
- %(<link href="#{pagy_url_for(pagy, pagy.prev, **vars)}"/>) if pagy.prev
74
- end
75
-
76
- # Conditionally return the next page link tag
77
- def pagy_next_link(pagy, **vars)
78
- %(<link href="#{pagy_url_for(pagy, pagy.next, **vars)}"/>) if pagy.next
79
- end
80
- end
81
- Frontend.prepend PagyExtra
82
- end
@@ -1,59 +0,0 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/searchkick
2
- # frozen_string_literal: true
3
-
4
- class Pagy # :nodoc:
5
- DEFAULT[:searchkick_search] ||= :search
6
- DEFAULT[:searchkick_pagy_search] ||= :pagy_search
7
-
8
- # Paginate Searchkick::Results objects
9
- module SearchkickExtra
10
- module ModelExtension # :nodoc:
11
- # Return an array used to delay the call of #search
12
- # after the pagination variables are merged to the options.
13
- # It also pushes to the same array an optional method call.
14
- def pagy_searchkick(term = '*', **options, &block)
15
- [self, term, options, block].tap do |args|
16
- args.define_singleton_method(:method_missing) { |*a| args += a }
17
- end
18
- end
19
- alias_method Pagy::DEFAULT[:searchkick_pagy_search], :pagy_searchkick
20
- end
21
- Pagy::Searchkick = ModelExtension
22
-
23
- # Additions for the Pagy class
24
- module PagyExtension
25
- # Create a Pagy object from a Searchkick::Results object
26
- def new_from_searchkick(results, **vars)
27
- vars[:limit] = results.options[:per_page]
28
- vars[:page] = results.options[:page]
29
- vars[:count] = results.total_count
30
- new(**vars)
31
- end
32
- end
33
- Pagy.extend PagyExtension
34
-
35
- # Add specialized backend methods to paginate Searchkick::Results
36
- module BackendAddOn
37
- private
38
-
39
- # Return Pagy object and results
40
- def pagy_searchkick(pagy_search_args, **vars)
41
- vars[:page] ||= pagy_get_page(vars)
42
- vars[:limit] ||= pagy_get_limit(vars)
43
- model, term, options, block, *called = pagy_search_args
44
- options[:per_page] = vars[:limit]
45
- options[:page] = vars[:page]
46
- results = model.send(DEFAULT[:searchkick_search], term, **options, &block)
47
- vars[:count] = results.total_count
48
-
49
- pagy = ::Pagy.new(**vars)
50
- # with :last_page overflow we need to re-run the method in order to get the hits
51
- return pagy_searchkick(pagy_search_args, **vars, page: pagy.page) \
52
- if defined?(::Pagy::OverflowExtra) && pagy.overflow? && pagy.vars[:overflow] == :last_page
53
-
54
- [pagy, called.empty? ? results : results.send(*called)]
55
- end
56
- end
57
- Backend.prepend BackendAddOn
58
- end
59
- end
@@ -1,40 +0,0 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/size
2
- # frozen_string_literal: true
3
-
4
- class Pagy # :nodoc:
5
- # Implement the legacy bar using the array size.
6
- # Unless you have very specific requirements, use the faster and better looking default bar.
7
- module SizeExtra
8
- # Implements the old series algorithm
9
- def series(size: @vars[:size], **_)
10
- return super unless size.is_a?(Array)
11
- return [] if size == []
12
- raise VariableError.new(self, :size, 'to be an Array of 4 Integers or []', size) \
13
- unless size.is_a?(Array) && size.size == 4 && size.all? { |num| !num.negative? rescue false } # rubocop:disable Style/RescueModifier
14
-
15
- [].tap do |series|
16
- # This algorithm is up to ~5x faster and ~2.3x lighter than the previous one (pagy < 4.3)
17
- # However the behavior of the legacy nav bar was taken straight from WillPaginate and Kaminari:
18
- # it's ill-concieved and complicates the experience of devs and users.
19
- left_gap_start = 1 + size[0]
20
- left_gap_end = @page - size[1] - 1
21
- right_gap_start = @page + size[2] + 1
22
- right_gap_end = @last - size[3]
23
- left_gap_end = right_gap_end if left_gap_end > right_gap_end
24
- right_gap_start = left_gap_start if left_gap_start > right_gap_start
25
- start = 1
26
- if (left_gap_end - left_gap_start).positive?
27
- series.push(*start...left_gap_start, :gap)
28
- start = left_gap_end + 1
29
- end
30
- if (right_gap_end - right_gap_start).positive?
31
- series.push(*start...right_gap_start, :gap)
32
- start = right_gap_end + 1
33
- end
34
- series.push(*start..@last)
35
- series[series.index(@page)] = @page.to_s
36
- end
37
- end
38
- end
39
- prepend SizeExtra
40
- end
@@ -1,60 +0,0 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/standalone
2
- # frozen_string_literal: true
3
-
4
- require 'uri'
5
-
6
- class Pagy # :nodoc:
7
- # Use pagy without any request object, nor Rack environment/gem, nor any defined params method,
8
- # even in the irb/rails console without any app or config.
9
- module StandaloneExtra
10
- # Extracted from Rack::Utils and reformatted for rubocop
11
- # :nocov:
12
- module QueryUtils
13
- module_function
14
-
15
- def build_nested_query(value, prefix = nil)
16
- case value
17
- when Array
18
- value.map { |v| build_nested_query(v, "#{prefix}[]") }.join('&')
19
- when Hash
20
- value.map do |k, v|
21
- build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
22
- end.delete_if(&:empty?).join('&')
23
- when nil
24
- escape(prefix)
25
- else
26
- raise ArgumentError, 'value must be a Hash' if prefix.nil?
27
-
28
- "#{escape(prefix)}=#{escape(value)}"
29
- end
30
- end
31
-
32
- def escape(str)
33
- URI.encode_www_form_component(str)
34
- end
35
- end
36
- # :nocov:
37
-
38
- # Return the URL for the page. If there is no pagy.vars[:url]
39
- # it works exactly as the regular #pagy_url_for, relying on the params method and Rack.
40
- # If there is a defined pagy.vars[:url] variable it does not need the params method nor Rack.
41
- def pagy_url_for(pagy, page, fragment: nil, **_)
42
- return super unless pagy.vars[:url]
43
-
44
- vars = pagy.vars
45
- params = vars[:params].is_a?(Hash) ? vars[:params].clone : {} # safe when it gets reused
46
- pagy_set_query_params(page, vars, params)
47
- params = vars[:params].(params) if vars[:params].is_a?(Proc)
48
- query_string = "?#{QueryUtils.build_nested_query(params)}"
49
- "#{vars[:url]}#{query_string}#{fragment}"
50
- end
51
- end
52
- UrlHelpers.prepend StandaloneExtra
53
-
54
- # Define a dummy params method if it's not already defined in the including module
55
- module Backend
56
- def self.included(controller)
57
- controller.define_method(:params) { {} } unless controller.method_defined?(:params)
58
- end
59
- end
60
- end
@@ -1,29 +0,0 @@
1
- # See the Pagy documentation: https://ddnexus.github.io/pagy/docs/extras/trim
2
- # frozen_string_literal: true
3
-
4
- class Pagy # :nodoc:
5
- DEFAULT[:trim_extra] = true # extra enabled by default
6
-
7
- # Remove the page=1 param from the first page link
8
- module TrimExtra
9
- # Override the original pagy_anchor.
10
- # Call the pagy_trim method for page 1 if the trim_extra is enabled
11
- def pagy_anchor(pagy, **_)
12
- a_proc = super
13
- return a_proc unless pagy.vars[:trim_extra]
14
-
15
- lambda do |page, text = pagy.label_for(page), **opts|
16
- a = +a_proc.(page, text, **opts)
17
- return a unless page.to_s == '1'
18
-
19
- pagy_trim(pagy, a) # in method for isolated testing
20
- end
21
- end
22
-
23
- # Remove the :page_param param from the first page anchor
24
- def pagy_trim(pagy, a)
25
- a.sub!(/[?&]#{pagy.vars[:page_param]}=1\b(?!&)|\b#{pagy.vars[:page_param]}=1&/, '')
26
- end
27
- end
28
- Frontend.prepend TrimExtra
29
- end
data/lib/pagy/frontend.rb DELETED
@@ -1,99 +0,0 @@
1
- # See Pagy::Frontend API documentation: https://ddnexus.github.io/pagy/docs/api/frontend
2
- # frozen_string_literal: true
3
-
4
- require_relative 'i18n'
5
- require_relative 'url_helpers'
6
-
7
- class Pagy
8
- # Used for search and replace, hardcoded also in the pagy.js file
9
- PAGE_TOKEN = '__pagy_page__'
10
- LABEL_TOKEN = '__pagy_label__'
11
-
12
- # Frontend modules are specially optimized for performance.
13
- # The resulting code may not look very elegant, but produces the best benchmarks
14
- module Frontend
15
- include UrlHelpers
16
-
17
- # Return a performance optimized lambda to generate the HTML anchor element (a tag)
18
- # Benchmarked on a 20 link nav: it is ~22x faster and uses ~18x less memory than rails' link_to
19
- def pagy_anchor(pagy, anchor_string: nil, **vars)
20
- anchor_string &&= %( #{anchor_string})
21
- left, right = %(<a#{anchor_string} href="#{pagy_url_for(pagy, PAGE_TOKEN, **vars)}").split(PAGE_TOKEN, 2)
22
- # lambda used by all the helpers
23
- lambda do |page, text = pagy.label_for(page), classes: nil, aria_label: nil|
24
- classes = %( class="#{classes}") if classes
25
- aria_label = %( aria-label="#{aria_label}") if aria_label
26
- %(#{left}#{page}#{right}#{classes}#{aria_label}>#{text}</a>)
27
- end
28
- end
29
-
30
- # Return examples: "Displaying items 41-60 of 324 in total" or "Displaying Products 41-60 of 324 in total"
31
- def pagy_info(pagy, id: nil, item_name: nil)
32
- id = %( id="#{id}") if id
33
- p_count = pagy.count
34
- key = if p_count.zero?
35
- 'pagy.info.no_items'
36
- elsif pagy.pages == 1
37
- 'pagy.info.single_page'
38
- else
39
- 'pagy.info.multiple_pages'
40
- end
41
-
42
- %(<span#{id} class="pagy info">#{
43
- pagy_t key, item_name: item_name || pagy_t('pagy.item_name', count: p_count),
44
- count: p_count, from: pagy.from, to: pagy.to
45
- }</span>)
46
- end
47
-
48
- # Generic pagination: it returns the html with the series of links to the pages
49
- def pagy_nav(pagy, id: nil, aria_label: nil, **vars)
50
- id = %( id="#{id}") if id
51
- a = pagy_anchor(pagy, **vars)
52
-
53
- html = %(<nav#{id} class="pagy nav" #{nav_aria_label(pagy, aria_label:)}>#{
54
- prev_a(pagy, a)})
55
- pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
56
- html << case item
57
- when Integer
58
- a.(item)
59
- when String
60
- %(<a role="link" aria-disabled="true" aria-current="page" class="current">#{pagy.label_for(item)}</a>)
61
- when :gap
62
- %(<a role="link" aria-disabled="true" class="gap">#{pagy_t('pagy.gap')}</a>)
63
- else
64
- raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
65
- end
66
- end
67
- html << %(#{next_a(pagy, a)}</nav>)
68
- end
69
-
70
- # Similar to I18n.t: just ~18x faster using ~10x less memory
71
- # (@pagy_locale explicitly initialized in order to avoid warning)
72
- def pagy_t(key, **opts)
73
- Pagy::I18n.translate(@pagy_locale ||= nil, key, **opts)
74
- end
75
-
76
- private
77
-
78
- def nav_aria_label(pagy, aria_label: nil)
79
- aria_label ||= pagy_t('pagy.aria_label.nav', count: pagy.pages)
80
- %(aria-label="#{aria_label}")
81
- end
82
-
83
- def prev_a(pagy, a, text: pagy_t('pagy.prev'), aria_label: pagy_t('pagy.aria_label.prev'))
84
- if (p_prev = pagy.prev)
85
- a.(p_prev, text, aria_label:)
86
- else
87
- %(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
88
- end
89
- end
90
-
91
- def next_a(pagy, a, text: pagy_t('pagy.next'), aria_label: pagy_t('pagy.aria_label.next'))
92
- if (p_next = pagy.next)
93
- a.(p_next, text, aria_label:)
94
- else
95
- %(<a role="link" aria-disabled="true" aria-label="#{aria_label}">#{text}</a>)
96
- end
97
- end
98
- end
99
- end
data/lib/pagy/i18n.rb DELETED
@@ -1,167 +0,0 @@
1
- # See Pagy::I18n API documentation https://ddnexus.github.io/pagy/docs/api/i18n
2
- # frozen_string_literal: true
3
-
4
- require 'yaml'
5
-
6
- class Pagy
7
- # Pagy i18n implementation, compatible with the I18n gem, just a lot faster and lighter
8
- module I18n
9
- extend self
10
-
11
- # Pluralization rules
12
- module P11n
13
- # Pluralization variables
14
- from0to1 = (0..1).to_a.freeze
15
- from2to4 = (2..4).to_a.freeze
16
- from3to10 = (3..10).to_a.freeze
17
- from5to9 = (5..9).to_a.freeze
18
- from11to14 = (11..14).to_a.freeze
19
- from11to99 = (11..99).to_a.freeze
20
- from12to14 = (12..14).to_a.freeze
21
-
22
- from0to1_from5to9 = from0to1 + from5to9
23
-
24
- # Store the proc defining each pluralization RULE
25
- # Logic adapted from https://github.com/svenfuchs/rails-i18n
26
- RULE = {
27
- arabic:
28
- lambda do |n = 0|
29
- mod100 = n % 100
30
- case
31
- when n == 0 then 'zero' # rubocop:disable Style/NumericPredicate
32
- when n == 1 then 'one'
33
- when n == 2 then 'two'
34
- when from3to10.include?(mod100) then 'few'
35
- when from11to99.include?(mod100) then 'many'
36
- else 'other'
37
- end
38
- end,
39
-
40
- east_slavic:
41
- lambda do |n = 0|
42
- mod10 = n % 10
43
- mod100 = n % 100
44
- case
45
- when mod10 == 1 && mod100 != 11 then 'one'
46
- when from2to4.include?(mod10) && !from12to14.include?(mod100) then 'few'
47
- when mod10 == 0 || from5to9.include?(mod10) || from11to14.include?(mod100) then 'many' # rubocop:disable Style/NumericPredicate
48
- else 'other'
49
- end
50
- end,
51
-
52
- one_other:
53
- ->(n) { n == 1 ? 'one' : 'other' }, # default RULE
54
-
55
- one_two_other:
56
- lambda do |n|
57
- case n
58
- when 1 then 'one'
59
- when 2 then 'two'
60
- else 'other'
61
- end
62
- end,
63
-
64
- one_upto_two_other:
65
- ->(n) { n && n >= 0 && n < 2 ? 'one' : 'other' },
66
-
67
- other:
68
- ->(*) { 'other' },
69
-
70
- polish:
71
- lambda do |n = 0|
72
- mod10 = n % 10
73
- mod100 = n % 100
74
- case
75
- when n == 1 then 'one'
76
- when from2to4.include?(mod10) && !from12to14.include?(mod100) then 'few'
77
- when from0to1_from5to9.include?(mod10) || from12to14.include?(mod100) then 'many'
78
- else 'other'
79
- end
80
- end,
81
-
82
- west_slavic:
83
- lambda do |n|
84
- case n
85
- when 1 then 'one'
86
- when *from2to4 then 'few'
87
- else 'other'
88
- end
89
- end
90
-
91
- }.freeze
92
-
93
- # Store the RULE to apply to each LOCALE
94
- # the :one_other RULE is the default for locales missing from this list
95
- LOCALE = Hash.new(RULE[:one_other]).tap do |hash|
96
- hash['ar'] = RULE[:arabic]
97
- hash['be'] = RULE[:east_slavic]
98
- hash['bs'] = RULE[:east_slavic]
99
- hash['ckb'] = RULE[:other]
100
- hash['cs'] = RULE[:west_slavic]
101
- hash['id'] = RULE[:other]
102
- hash['fr'] = RULE[:one_upto_two_other]
103
- hash['hr'] = RULE[:east_slavic]
104
- hash['ja'] = RULE[:other]
105
- hash['km'] = RULE[:other]
106
- hash['ko'] = RULE[:other]
107
- hash['pl'] = RULE[:polish]
108
- hash['ru'] = RULE[:east_slavic]
109
- hash['sk'] = RULE[:west_slavic]
110
- hash['sr'] = RULE[:east_slavic]
111
- hash['tr'] = RULE[:other]
112
- hash['uk'] = RULE[:east_slavic]
113
- hash['vi'] = RULE[:other]
114
- hash['zh-CN'] = RULE[:other]
115
- hash['zh-HK'] = RULE[:other]
116
- hash['zh-TW'] = RULE[:other]
117
- hash['dz'] = RULE[:other]
118
- end.freeze
119
- end
120
-
121
- # Stores the i18n DATA structure for each loaded locale
122
- # default on the first locale DATA
123
- DATA = Hash.new { |hash,| hash.first[1] }
124
-
125
- private
126
-
127
- # Create a flat hash with dotted notation keys
128
- def flatten(initial, prefix = '')
129
- initial.each.reduce({}) do |hash, (key, value)|
130
- hash.merge!(value.is_a?(Hash) ? flatten(value, "#{prefix}#{key}.") : { "#{prefix}#{key}" => value })
131
- end
132
- end
133
-
134
- # Build the DATA hash out of the passed locales
135
- def build(*locales)
136
- locales.each do |locale|
137
- locale[:filepath] ||= Pagy.root.join('locales', "#{locale[:locale]}.yml")
138
- locale[:pluralize] ||= P11n::LOCALE[locale[:locale]]
139
- dictionary = YAML.safe_load(File.read(locale[:filepath], encoding: 'UTF-8'))
140
- raise I18nError, %(expected :locale "#{locale[:locale]}" not found in :filepath "#{locale[:filepath].inspect}") \
141
- unless dictionary.key?(locale[:locale])
142
-
143
- DATA[locale[:locale]] = [flatten(dictionary[locale[:locale]]), locale[:pluralize]]
144
- end
145
- end
146
- # Build the default at require time
147
- build(locale: 'en')
148
-
149
- public
150
-
151
- # Public method to configure the locales: overrides the default, build the DATA and freezes it
152
- def load(*locales)
153
- DATA.clear
154
- build(*locales)
155
- DATA.freeze
156
- end
157
-
158
- # Translate and pluralize the key with the locale DATA
159
- def translate(locale, key, **opts)
160
- data, pluralize = DATA[locale]
161
- translation = data[key] || (opts[:count] && data[key += ".#{pluralize.call(opts[:count])}"]) \
162
- or return %([translation missing: "#{key}"])
163
- translation.gsub(/%{[^}]+?}/) { |match| opts[:"#{match[2..-2]}"] || match }
164
- end
165
- alias t translate
166
- end
167
- end
@@ -1,44 +0,0 @@
1
- # See Pagy API documentation: https://ddnexus.github.io/pagy/docs/api/keyset
2
- # frozen_string_literal: true
3
-
4
- class Pagy
5
- class Keyset
6
- # Keyset adapter for ActiveRecord
7
- class ActiveRecord < Keyset
8
- protected
9
-
10
- # Get the keyset attributes from the record
11
- def keyset_attributes_from(record) = record.slice(*@keyset.keys)
12
-
13
- # Get the hash of quoted keyset identifiers
14
- def quoted_identifiers(table)
15
- connection = @set.connection
16
- @keyset.to_h { |column| [column, "#{connection.quote_table_name(table)}.#{connection.quote_column_name(column)}"] }
17
- end
18
-
19
- # Extract the keyset from the set
20
- def extract_keyset
21
- @set.order_values.each_with_object({}) do |node, keyset|
22
- keyset[node.value.name.to_sym] = node.direction
23
- end
24
- end
25
-
26
- # Filter the newest records
27
- def filter_newest = @set.where(filter_newest_query, **@latest)
28
-
29
- # Append the missing keyset keys if the set is restricted by select
30
- def apply_select
31
- @set.select(*@keyset.keys)
32
- end
33
-
34
- # Set with selected columns?
35
- def select? = !@set.select_values.empty?
36
-
37
- # Typecast the latest attributes
38
- def typecast_latest(latest)
39
- @set.model.new(latest).slice(latest.keys)
40
- .to_hash.transform_keys(&:to_sym)
41
- end
42
- end
43
- end
44
- end
@@ -1,57 +0,0 @@
1
- # See Pagy API documentation: https://ddnexus.github.io/pagy/docs/api/keyset
2
- # frozen_string_literal: true
3
-
4
- class Pagy
5
- class Keyset
6
- # Keyset adapter for sequel
7
- class Sequel < Keyset
8
- protected
9
-
10
- # Get the keyset attributes from the record
11
- def keyset_attributes_from(record) = record.to_hash.slice(*@keyset.keys)
12
-
13
- # Get the hash of quoted keyset identifiersAdd commentMore actions
14
- def quoted_identifiers(table)
15
- db = @set.db
16
- @keyset.to_h { |column| [column, "#{db.quote_identifier(table)}.#{db.quote_identifier(column)}"] }
17
- end
18
-
19
- # Extract the keyset from the set
20
- def extract_keyset
21
- return {} unless @set.opts[:order]
22
-
23
- @set.opts[:order].each_with_object({}) do |item, keyset|
24
- case item
25
- when Symbol
26
- keyset[item] = :asc
27
- when ::Sequel::SQL::OrderedExpression
28
- keyset[item.expression] = item.descending ? :desc : :asc
29
- else
30
- raise TypeError, "#{item.class.inspect} is not a supported Sequel::SQL::OrderedExpression"
31
- end
32
- end
33
- end
34
-
35
- # Filter the newest records
36
- def filter_newest = @set.where(::Sequel.lit(filter_newest_query, **@latest))
37
-
38
- # Append the missing keyset keys if the set is restricted by select
39
- def apply_select
40
- selected = @set.opts[:select]
41
- @set.select_append(*@keyset.keys.reject { |c| selected.include?(c) })
42
- end
43
-
44
- # Set with selected columns?
45
- def select? = !@set.opts[:select].nil?
46
-
47
- # Typecast the latest attributes
48
- def typecast_latest(latest)
49
- model = @set.opts[:model]
50
- model.unrestrict_primary_key if (restricted_pk = model.restrict_primary_key?)
51
- latest = model.new(latest).to_hash.slice(*latest.keys.map(&:to_sym))
52
- model.restrict_primary_key if restricted_pk
53
- latest
54
- end
55
- end
56
- end
57
- end