pagy 43.0.0 → 43.3.0

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/apps/calendar.ru +11 -12
  4. data/apps/demo.ru +5 -5
  5. data/apps/enable_rails_page_segment.rb +54 -0
  6. data/apps/index.rb +1 -1
  7. data/apps/keynav+root_key.ru +316 -0
  8. data/apps/keynav.ru +10 -13
  9. data/apps/keyset.ru +5 -11
  10. data/apps/keyset_sequel.ru +10 -12
  11. data/apps/rails.ru +8 -12
  12. data/apps/repro.ru +11 -11
  13. data/bin/pagy +2 -94
  14. data/config/pagy.rb +8 -7
  15. data/javascripts/ai_widget.js +65 -51
  16. data/javascripts/pagy.js +20 -17
  17. data/javascripts/pagy.js.map +3 -3
  18. data/javascripts/pagy.min.js +2 -1
  19. data/javascripts/pagy.mjs +19 -16
  20. data/javascripts/wand.js +15 -9
  21. data/lib/pagy/classes/calendar/calendar.rb +36 -31
  22. data/lib/pagy/classes/calendar/day.rb +1 -1
  23. data/lib/pagy/classes/calendar/month.rb +1 -1
  24. data/lib/pagy/classes/calendar/quarter.rb +1 -1
  25. data/lib/pagy/classes/calendar/unit.rb +12 -13
  26. data/lib/pagy/classes/calendar/year.rb +1 -1
  27. data/lib/pagy/classes/exceptions.rb +1 -8
  28. data/lib/pagy/classes/keyset/adapters/active_record.rb +3 -1
  29. data/lib/pagy/classes/keyset/adapters/sequel.rb +3 -1
  30. data/lib/pagy/classes/keyset/keynav.rb +9 -4
  31. data/lib/pagy/classes/keyset/keyset.rb +57 -32
  32. data/lib/pagy/classes/offset/countish.rb +17 -0
  33. data/lib/pagy/classes/offset/countless.rb +26 -14
  34. data/lib/pagy/classes/offset/offset.rb +8 -2
  35. data/lib/pagy/classes/offset/search.rb +6 -10
  36. data/lib/pagy/classes/request.rb +29 -20
  37. data/lib/pagy/cli.rb +135 -0
  38. data/lib/pagy/console.rb +6 -0
  39. data/lib/pagy/modules/abilities/configurable.rb +2 -2
  40. data/lib/pagy/modules/abilities/countable.rb +24 -0
  41. data/lib/pagy/modules/abilities/linkable.rb +35 -24
  42. data/lib/pagy/modules/abilities/rangeable.rb +3 -3
  43. data/lib/pagy/modules/b64.rb +9 -3
  44. data/lib/pagy/modules/console.rb +15 -20
  45. data/lib/pagy/modules/i18n/i18n.rb +38 -14
  46. data/lib/pagy/modules/i18n/p11n/arabic.rb +1 -0
  47. data/lib/pagy/modules/i18n/p11n/east_slavic.rb +1 -0
  48. data/lib/pagy/modules/i18n/p11n/polish.rb +1 -0
  49. data/lib/pagy/modules/searcher.rb +9 -8
  50. data/lib/pagy/toolbox/helpers/anchor_tags.rb +11 -15
  51. data/lib/pagy/toolbox/helpers/bootstrap/input_nav_js.rb +3 -0
  52. data/lib/pagy/toolbox/helpers/bootstrap/series_nav.rb +3 -1
  53. data/lib/pagy/toolbox/helpers/bootstrap/series_nav_js.rb +2 -0
  54. data/lib/pagy/toolbox/helpers/bulma/input_nav_js.rb +3 -0
  55. data/lib/pagy/toolbox/helpers/bulma/previous_next_html.rb +1 -1
  56. data/lib/pagy/toolbox/helpers/bulma/series_nav.rb +3 -1
  57. data/lib/pagy/toolbox/helpers/bulma/series_nav_js.rb +2 -0
  58. data/lib/pagy/toolbox/helpers/data_hash.rb +19 -17
  59. data/lib/pagy/toolbox/helpers/headers_hash.rb +15 -9
  60. data/lib/pagy/toolbox/helpers/info_tag.rb +2 -0
  61. data/lib/pagy/toolbox/helpers/input_nav_js.rb +9 -6
  62. data/lib/pagy/toolbox/helpers/limit_tag_js.rb +4 -3
  63. data/lib/pagy/toolbox/helpers/loader.rb +3 -0
  64. data/lib/pagy/toolbox/helpers/page_url.rb +10 -16
  65. data/lib/pagy/toolbox/helpers/series_nav.rb +5 -4
  66. data/lib/pagy/toolbox/helpers/series_nav_js.rb +2 -1
  67. data/lib/pagy/toolbox/helpers/support/a_lambda.rb +8 -6
  68. data/lib/pagy/toolbox/helpers/support/data_pagy_attribute.rb +6 -1
  69. data/lib/pagy/toolbox/helpers/support/series.rb +1 -2
  70. data/lib/pagy/toolbox/helpers/support/wrap_input_nav_js.rb +1 -1
  71. data/lib/pagy/toolbox/helpers/support/wrap_series_nav.rb +2 -1
  72. data/lib/pagy/toolbox/helpers/support/wrap_series_nav_js.rb +10 -4
  73. data/lib/pagy/toolbox/helpers/urls_hash.rb +7 -7
  74. data/lib/pagy/toolbox/paginators/calendar.rb +13 -9
  75. data/lib/pagy/toolbox/paginators/countish.rb +39 -0
  76. data/lib/pagy/toolbox/paginators/countless.rb +13 -15
  77. data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +43 -18
  78. data/lib/pagy/toolbox/paginators/keynav_js.rb +14 -15
  79. data/lib/pagy/toolbox/paginators/keyset.rb +7 -9
  80. data/lib/pagy/toolbox/paginators/meilisearch.rb +21 -18
  81. data/lib/pagy/toolbox/paginators/method.rb +15 -3
  82. data/lib/pagy/toolbox/paginators/offset.rb +14 -22
  83. data/lib/pagy/toolbox/paginators/searchkick.rb +21 -18
  84. data/lib/pagy/toolbox/paginators/typesense_rails.rb +35 -0
  85. data/lib/pagy.rb +23 -10
  86. data/locales/id.yml +1 -3
  87. data/locales/ja.yml +1 -3
  88. data/locales/km.yml +1 -3
  89. data/locales/sw.yml +2 -2
  90. data/locales/tr.yml +10 -8
  91. data/stylesheets/pagy-tailwind.css +1 -1
  92. data/stylesheets/pagy.css +1 -6
  93. metadata +25 -8
  94. data/lib/optimist.rb +0 -1022
  95. data/lib/pagy/classes/keyset/active_record.rb +0 -11
  96. data/lib/pagy/classes/keyset/keynav/active_record.rb +0 -13
  97. data/lib/pagy/classes/keyset/keynav/sequel.rb +0 -13
  98. data/lib/pagy/classes/keyset/sequel.rb +0 -11
@@ -12,6 +12,7 @@ class Pagy
12
12
  # Build the nav tag, with the specific inner html for the style
13
13
  def wrap_series_nav(html, nav_classes, id: nil, aria_label: nil, **)
14
14
  data = %( #{data_pagy_attribute(:k, @update)}) if keynav?
15
- %(<nav#{id && %( id="#{id}")} class="#{nav_classes}" #{nav_aria_label_attribute(aria_label:)}#{data}>#{html}</nav>)
15
+
16
+ %(<nav#{%( id="#{id}") if id} class="#{nav_classes}" #{nav_aria_label_attribute(aria_label:)}#{data}>#{html}</nav>)
16
17
  end
17
18
  end
@@ -20,15 +20,21 @@ class Pagy
20
20
 
21
21
  # Support for the Calendar API
22
22
  def page_labels(series)
23
- series.map { |s| s.map { |item| item == :gap ? :gap : page_label(item) } } if calendar?
23
+ return unless calendar?
24
+
25
+ series.map do |s|
26
+ s.map { _1 == :gap ? :gap : page_label(_1) }
27
+ end
24
28
  end
25
29
 
26
30
  # Build the nav_js tag, with the specific tokens for the style
27
31
  def wrap_series_nav_js(tokens, nav_classes, id: nil, aria_label: nil, **)
28
- sequels = sequels(**)
29
- %(<nav#{id && %( id="#{id}")} class="#{'pagy-rjs ' if sequels[0].size > 1}#{nav_classes}" #{
32
+ sequels = sequels(**)
33
+ nav_classes = "pagy-rjs #{nav_classes}" if sequels[0].size > 1
34
+
35
+ %(<nav#{%( id="#{id}") if id} class="#{nav_classes}" #{
30
36
  nav_aria_label_attribute(aria_label:)} #{
31
- data = [:snj, tokens.values, sequels]
37
+ data = [:snj, tokens.values, PAGE_TOKEN, sequels]
32
38
  data.push(@update) if keynav?
33
39
  data_pagy_attribute(*data)
34
40
  }></nav>)
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Pagy
4
- # Generte hash of the pagination links
4
+ # Generate the hash of the pagination links
5
5
  def urls_hash(**)
6
- url = compose_page_url(PAGE_TOKEN, **)
7
- { first: compose_page_url(nil, **) }.tap do |urls| # first is present, but the URL omits the nil page param
8
- urls[:previous] = url.sub(PAGE_TOKEN, @previous.to_s) if @previous
9
- urls[:next] = url.sub(PAGE_TOKEN, @next.to_s) if @next
10
- urls[:last] = url.sub(PAGE_TOKEN, @last.to_s) if @count # offset and not countless
11
- end
6
+ template = compose_page_url(PAGE_TOKEN, **)
7
+
8
+ { first: compose_page_url(nil, **),
9
+ previous: @previous && template.sub(PAGE_TOKEN, @previous.to_s),
10
+ next: @next && template.sub(PAGE_TOKEN, @next.to_s),
11
+ last: @count && template.sub(PAGE_TOKEN, @last.to_s) }.compact
12
12
  end
13
13
  end
@@ -8,20 +8,24 @@ class Pagy
8
8
  def paginate(context, collection, config)
9
9
  context.instance_eval do
10
10
  allowed_options = Calendar::UNITS + %i[offset disabled request]
11
- raise ArgumentError, "keys must be in #{allowed_options.inspect}" \
12
- unless config.is_a?(Hash) && (config.keys - allowed_options).empty?
13
11
 
14
- config[:request] = Request.new(config[:request] || request)
12
+ unless config.is_a?(Hash) && (config.keys - allowed_options).empty?
13
+ raise ArgumentError, "keys must be in #{allowed_options.inspect}"
14
+ end
15
+
15
16
  config[:offset] ||= {}
17
+
16
18
  unless config[:disabled]
17
- calendar, from, to =
18
- Calendar.send(:init, config,
19
- pagy_calendar_period(collection),
20
- config[:request].query) do |unit, period|
21
- pagy_calendar_counts(collection, unit, *period) if respond_to?(:pagy_calendar_counts)
22
- end
19
+ period = pagy_calendar_period(collection)
20
+ params = config[:request].params
21
+
22
+ calendar, from, to = Calendar.send(:init, config, period, params) do |unit, unit_period|
23
+ pagy_calendar_counts(collection, unit, *unit_period) if respond_to?(:pagy_calendar_counts)
24
+ end
25
+
23
26
  collection = pagy_calendar_filter(collection, from, to)
24
27
  end
28
+
25
29
  pagy, records = pagy(:offset, collection, **config[:offset])
26
30
  [calendar, pagy, records]
27
31
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../modules/abilities/countable'
4
+
5
+ class Pagy
6
+ module CountishPaginator
7
+ module_function
8
+
9
+ # Return the Offset::Countish instance and records
10
+ def paginate(collection, options)
11
+ options[:page] ||= options[:request].resolve_page(force_integer: false)
12
+
13
+ if options[:page].is_a?(String)
14
+ page, count, epoch = options[:page].split.map(&:to_i)
15
+ options[:page] = page
16
+ end
17
+
18
+ options[:limit] = options[:request].resolve_limit
19
+ setup_options(count, epoch, collection, options)
20
+
21
+ pagy = Offset::Countish.new(**options)
22
+ [pagy, pagy.records(collection)]
23
+ end
24
+
25
+ # Get the count from the page and set epoch when ttl (Time To Live) requires it
26
+ def setup_options(count, epoch, collection, options)
27
+ now = Time.now.to_i
28
+ ongoing = !options[:ttl] || (epoch && epoch <= now && now < (epoch + options[:ttl]))
29
+
30
+ if !options[:count] && count && ongoing
31
+ options[:count] = count
32
+ options[:epoch] = epoch if options[:ttl]
33
+ else # recount
34
+ options[:count] ||= Countable.get_count(collection, options)
35
+ options[:epoch] = now if options[:ttl]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -4,22 +4,20 @@ class Pagy
4
4
  module CountlessPaginator
5
5
  module_function
6
6
 
7
- # Return Pagy object and records
8
- def paginate(context, collection, **options)
9
- context.instance_eval do
10
- request = Request.new(options[:request] || self.request, options)
11
- if options[:page].nil?
12
- page = request.resolve_page(options, force_integer: false) # accept nil and strings
13
- if page.is_a?(String)
14
- p, l = page.split(/ /, 2).map(&:to_i)
15
- options[:page] = p if p.positive?
16
- options[:last] = l if l&.positive?
17
- end
18
- end
19
- options[:limit] = request.resolve_limit(options)
20
- pagy = Offset::Countless.new(**options, request:)
21
- [pagy, pagy.records(collection)]
7
+ # Return the Offset::Countless instance and records
8
+ def paginate(collection, options)
9
+ options[:page] ||= options[:request].resolve_page(force_integer: false) # accept nil and strings
10
+
11
+ if options[:page].is_a?(String)
12
+ page, last = options[:page].split.map(&:to_i) # ' ' separator, (encoded as '+' by Countless#compose_page_param)
13
+ options[:page] = page
14
+ options[:last] = last if last&.positive?
22
15
  end
16
+
17
+ options[:limit] = options[:request].resolve_limit
18
+
19
+ pagy = Offset::Countless.new(**options)
20
+ [pagy, pagy.records(collection)]
23
21
  end
24
22
  end
25
23
  end
@@ -7,26 +7,51 @@ class Pagy
7
7
  module_function
8
8
 
9
9
  # Paginate from the search object
10
- def paginate(context, search, **options)
11
- context.instance_eval do
12
- if search.is_a?(Search::Arguments)
13
- # The search is the array of pagy_search arguments
14
- Searcher.wrap(self, search, options) do
15
- model, query_or_payload, search_options = search
16
- search_options[:size] = options[:limit]
17
- search_options[:from] = options[:limit] * ((options[:page] || 1) - 1)
18
- results = model.send(ElasticsearchRails::DEFAULT[:search_method], query_or_payload, **search_options)
19
- options[:count] = ElasticsearchRails.total_count(results)
20
- [ElasticsearchRails.new(**options), results]
21
- end
22
- else
23
- # The search is an elasticsearch_rails response
24
- options[:limit] = search.search.options[:size] || 10
25
- options[:page] = ((search.search.options[:from] || 0) / options[:limit]) + 1
26
- options[:count] = ElasticsearchRails.total_count(search)
27
- ElasticsearchRails.new(**options)
10
+ def paginate(search, options)
11
+ if search.is_a?(Search::Arguments) # Active mode
12
+
13
+ Searcher.wrap(search, options) do
14
+ model, arguments, search_options = search
15
+
16
+ search_options[:size] = options[:limit]
17
+ search_options[:from] = options[:limit] * ((options[:page] || 1) - 1)
18
+
19
+ method = options[:search_method] || ElasticsearchRails::DEFAULT[:search_method]
20
+ response_object = model.send(method, *arguments, **search_options)
21
+ options[:count] = total_count_from(response_object)
22
+
23
+ [ElasticsearchRails.new(**options), response_object]
28
24
  end
25
+
26
+ else # Passive mode
27
+ from, size = pagination_params_from(search)
28
+ options[:limit] = size
29
+ options[:page] = ((from || 0) / options[:limit]) + 1
30
+ options[:count] = total_count_from(search)
31
+
32
+ ElasticsearchRails.new(**options)
29
33
  end
30
34
  end
35
+
36
+ # Get from and size params from the response object, supporting different versions of ElasticsearchRails
37
+ def pagination_params_from(response_object)
38
+ definition = response_object.search.definition
39
+ definition = definition.to_hash if definition.respond_to?(:to_hash)
40
+ container = (definition.is_a?(Hash) && (definition[:body] || definition)) || response_object.search.options
41
+ from = (container[:from] || container['from']).to_i
42
+ size = (container[:size] || container['size']).to_i
43
+ size = 10 if size.zero?
44
+
45
+ [from, size]
46
+ end
47
+
48
+ # Get the count from the response object, supporting different versions of ElasticsearchRails
49
+ def total_count_from(response_object)
50
+ total = response_object.instance_eval do
51
+ respond_to?(:response) ? response['hits']['total'] : raw_response['hits']['total']
52
+ end
53
+
54
+ total.is_a?(Hash) ? total['value'] : total
55
+ end
31
56
  end
32
57
  end
@@ -6,24 +6,23 @@ class Pagy
6
6
  module KeynavJsPaginator
7
7
  module_function
8
8
 
9
- # Return Pagy::Keyset::Keynav object and paginated records.
9
+ # Return the Pagy::Keyset::Keynav instance and paginated records.
10
10
  # Fall back to :countless if the :page has no client data.
11
- def paginate(context, set, **options)
12
- context.instance_eval do
13
- request = Request.new(options[:request] || self.request, options)
14
- page = request.resolve_page(options, force_integer: false) # allow nil
15
- if page&.match(' ') # countless page -> no augmentation -> fallback
16
- return pagy(:countless, set, page:, **options)
17
- elsif page.is_a?(String) # keynav page param
18
- page_arguments = JSON.parse(B64.urlsafe_decode(page))
19
- # Restart the pagination from page 1 if the url has been requested from another browser
20
- options[:page] = page_arguments if request.cookie == page_arguments.shift
21
- end
11
+ def paginate(set, options)
12
+ page = options[:request].resolve_page(force_integer: false) # allow nil
22
13
 
23
- options[:limit] = request.resolve_limit(options)
24
- pagy = Keyset::Keynav.new(set, **options, request:)
25
- [pagy, pagy.records]
14
+ return CountlessPaginator.paginate(set, page:, **options) if page&.match(' ') # countless fallback
15
+
16
+ if page.is_a?(String) # keynav page param
17
+ page_arguments = JSON.parse(B64.urlsafe_decode(page))
18
+ # Restart the pagination from page 1/nil if the url has been requested from another browser
19
+ options[:page] = page_arguments if options[:request].cookie == page_arguments.shift
26
20
  end
21
+
22
+ options[:limit] = options[:request].resolve_limit
23
+
24
+ pagy = Keyset::Keynav.new(set, **options)
25
+ [pagy, pagy.records]
27
26
  end
28
27
  end
29
28
  end
@@ -4,15 +4,13 @@ class Pagy
4
4
  module KeysetPaginator
5
5
  module_function
6
6
 
7
- # Return Pagy::Keyset object and paginated records
8
- def paginate(context, set, **options)
9
- context.instance_eval do
10
- request = Request.new(options[:request] || self.request, options)
11
- options[:page] ||= request.resolve_page(options, force_integer: false) # allow nil
12
- options[:limit] = request.resolve_limit(options)
13
- pagy = Keyset.new(set, **options, request:)
14
- [pagy, pagy.records]
15
- end
7
+ # Return Pagy::Keyset instance and paginated records
8
+ def paginate(set, options)
9
+ options[:page] ||= options[:request].resolve_page(force_integer: false) # allow nil
10
+ options[:limit] = options[:request].resolve_limit
11
+
12
+ pagy = Keyset.new(set, **options)
13
+ [pagy, pagy.records]
16
14
  end
17
15
  end
18
16
  end
@@ -7,25 +7,28 @@ class Pagy
7
7
  module_function
8
8
 
9
9
  # Paginate from the search object
10
- def paginate(context, search, **options)
11
- context.instance_eval do
12
- if search.is_a?(Search::Arguments)
13
- # The search is the array of pagy_search arguments
14
- Searcher.wrap(self, search, options) do
15
- model, term, search_options = search
16
- search_options[:hits_per_page] = options[:limit]
17
- search_options[:page] = options[:page]
18
- results = model.send(Meilisearch::DEFAULT[:search_method], term, search_options)
19
- options[:count] = results.raw_answer['totalHits']
20
- [Meilisearch.new(**options), results]
21
- end
22
- else
23
- # The search is a meilisearch results object
24
- options[:limit] = search.raw_answer['hitsPerPage']
25
- options[:page] = search.raw_answer['page']
26
- options[:count] = search.raw_answer['totalHits']
27
- Meilisearch.new(**options)
10
+ def paginate(search, options)
11
+ if search.is_a?(Search::Arguments) # Active mode
12
+
13
+ Searcher.wrap(search, options) do
14
+ model, arguments, search_options = search
15
+
16
+ search_options[:hits_per_page] = options[:limit]
17
+ search_options[:page] = options[:page]
18
+
19
+ method = options[:search_method] || Meilisearch::DEFAULT[:search_method]
20
+ results = model.send(method, *arguments, search_options)
21
+ options[:count] = results.raw_answer['totalHits']
22
+
23
+ [Meilisearch.new(**options), results]
28
24
  end
25
+
26
+ else # Passive mode
27
+ options[:limit] = search.raw_answer['hitsPerPage']
28
+ options[:page] = search.raw_answer['page']
29
+ options[:count] = search.raw_answer['totalHits']
30
+
31
+ Meilisearch.new(**options)
29
32
  end
30
33
  end
31
34
  end
@@ -5,22 +5,34 @@ require_relative '../../classes/request'
5
5
  class Pagy
6
6
  paginators = { offset: :OffsetPaginator,
7
7
  countless: :CountlessPaginator,
8
+ countish: :CountishPaginator,
8
9
  keyset: :KeysetPaginator,
9
10
  keynav_js: :KeynavJsPaginator,
10
11
  calendar: :CalendarPaginator,
11
12
  elasticsearch_rails: :ElasticsearchRailsPaginator,
12
13
  meilisearch: :MeilisearchPaginator,
13
- searchkick: :SearchkickPaginator }.freeze
14
+ searchkick: :SearchkickPaginator,
15
+ typesense_rails: :TypesenseRailsPaginator }.freeze
14
16
 
15
17
  path = Pathname.new(__dir__)
16
18
  paginators.each { |symbol, name| autoload name, path.join(symbol.to_s) }
17
19
 
18
- # Defines the pagy method. Include in the app controller/view.
20
+ # Pagy::Method defines the #pagy method to be included in the app controller/view.
19
21
  Method = Module.new do
20
22
  protected
21
23
 
22
24
  define_method :pagy do |paginator = :offset, collection, **options|
23
- Pagy.const_get(paginators[paginator]).paginate(self, collection, **Pagy.options, **options)
25
+ arguments = if paginator == :calendar
26
+ [self, collection, options]
27
+ else
28
+ [collection, options = Pagy::OPTIONS.merge(options)]
29
+ end
30
+
31
+ options[:root_key] = 'page' if options[:jsonapi] # enforce 'page' root_key for JSON:API
32
+ options[:request] ||= request # user set request or self.request
33
+ options[:request] = Request.new(options) # Pagy::Request
34
+
35
+ Pagy.const_get(paginators[paginator]).paginate(*arguments)
24
36
  end
25
37
  end
26
38
  end
@@ -1,33 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../modules/abilities/countable'
4
+
3
5
  class Pagy
4
6
  module OffsetPaginator
5
7
  module_function
6
8
 
7
- # Return instance and page of results
8
- def paginate(context, collection, **options)
9
- context.instance_eval do
10
- request = Request.new(options[:request] || self.request, options)
11
- options[:page] ||= request.resolve_page(options)
12
- options[:limit] = request.resolve_limit(options)
13
- options[:count] ||= collection.instance_of?(Array) ? collection.size : OffsetPaginator.get_count(collection, options)
14
- pagy = Offset.new(**options, request:)
15
- [pagy, collection.instance_of?(Array) ? collection[pagy.offset, pagy.limit] : pagy.records(collection)]
16
- end
17
- end
9
+ # Return the Pagy::Offset instance and results
10
+ def paginate(collection, options)
11
+ options[:page] ||= options[:request].resolve_page
12
+ options[:limit] = options[:request].resolve_limit
13
+ options[:count] ||= Countable.get_count(collection, options)
18
14
 
19
- # Get the collection count
20
- def get_count(collection, options)
21
- return collection.count unless defined?(::ActiveRecord) && collection.is_a?(::ActiveRecord::Relation)
15
+ pagy = Offset.new(**options)
16
+ records = if collection.instance_of?(Array)
17
+ collection[pagy.offset, pagy.limit]
18
+ else
19
+ pagy.records(collection)
20
+ end
22
21
 
23
- count = if options[:count_over] && !collection.group_values.empty?
24
- # COUNT(*) OVER ()
25
- sql = Arel.star.count.over(Arel::Nodes::Grouping.new([]))
26
- collection.unscope(:order).pick(sql).to_i
27
- else
28
- collection.count(:all)
29
- end
30
- count.is_a?(Hash) ? count.size : count
22
+ [pagy, records]
31
23
  end
32
24
  end
33
25
  end
@@ -7,25 +7,28 @@ class Pagy
7
7
  module_function
8
8
 
9
9
  # Paginate from the search object
10
- def paginate(context, search, **options)
11
- context.instance_eval do
12
- if search.is_a?(Search::Arguments)
13
- # The search is the array of pagy_search arguments
14
- Searcher.wrap(self, search, options) do
15
- model, term, search_options, block = search
16
- search_options[:per_page] = options[:limit]
17
- search_options[:page] = options[:page]
18
- results = model.send(Searchkick::DEFAULT[:search_method], term || '*', **search_options, &block)
19
- options[:count] = results.total_count
20
- [Searchkick.new(**options), results]
21
- end
22
- else
23
- # The search is a searchkick results object
24
- options[:limit] = search.options[:per_page]
25
- options[:page] = search.options[:page]
26
- options[:count] = search.total_count
27
- Searchkick.new(**options)
10
+ def paginate(search, options)
11
+ if search.is_a?(Search::Arguments) # Active mode
12
+
13
+ Searcher.wrap(search, options) do
14
+ model, arguments, search_options, block = search
15
+
16
+ search_options[:per_page] = options[:limit]
17
+ search_options[:page] = options[:page]
18
+
19
+ method = options[:search_method] || Searchkick::DEFAULT[:search_method]
20
+ results = model.send(method, *arguments || '*', **search_options, &block)
21
+ options[:count] = results.total_count
22
+
23
+ [Searchkick.new(**options), results]
28
24
  end
25
+
26
+ else # Passive mode
27
+ options[:limit] = search.respond_to?(:options) ? search.options[:per_page] : search.per_page
28
+ options[:page] = search.respond_to?(:options) ? search.options[:page] : search.current_page
29
+ options[:count] = search.total_count
30
+
31
+ Searchkick.new(**options)
29
32
  end
30
33
  end
31
34
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../modules/searcher'
4
+
5
+ class Pagy
6
+ module TypesenseRailsPaginator
7
+ module_function
8
+
9
+ # Paginate from the search object
10
+ def paginate(search, options)
11
+ if search.is_a?(Search::Arguments) # Active mode
12
+
13
+ Searcher.wrap(search, options) do
14
+ model, arguments, search_options = search
15
+
16
+ search_options[:per_page] = options[:limit]
17
+ search_options[:page] = options[:page]
18
+
19
+ method = options[:search_method] || TypesenseRails::DEFAULT[:search_method]
20
+ results = model.send(method, *arguments, search_options)
21
+ options[:count] = results.raw_answer['found']
22
+
23
+ [TypesenseRails.new(**options), results]
24
+ end
25
+
26
+ else # Passive mode
27
+ options[:limit] = search.raw_answer['request_params']['per_page']
28
+ options[:page] = search.raw_answer['page']
29
+ options[:count] = search.raw_answer['found']
30
+
31
+ TypesenseRails.new(**options)
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/pagy.rb CHANGED
@@ -1,18 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'pathname'
4
+
4
5
  require_relative 'pagy/classes/exceptions'
5
6
  require_relative 'pagy/modules/abilities/linkable'
6
7
  require_relative 'pagy/modules/abilities/configurable'
7
8
  require_relative 'pagy/toolbox/helpers/loader'
8
9
 
9
10
  # Top superclass: it defines only what's common to all the subclasses
11
+ # noinspection RubyMismatchedArgumentType
10
12
  class Pagy
11
- VERSION = '43.0.0'
13
+ VERSION = '43.3.0'
12
14
  ROOT = Pathname.new(__dir__).parent.freeze
13
15
  DEFAULT = { limit: 20, limit_key: 'limit', page_key: 'page' }.freeze
14
- PAGE_TOKEN = 'P '
15
- LIMIT_TOKEN = 'L '
16
+ PAGE_TOKEN = EscapedValue.new('P ')
17
+ LIMIT_TOKEN = EscapedValue.new('L ')
16
18
  LABEL_TOKEN = 'L'
17
19
  A_TAG = '<a style="display: none;">#</a>'
18
20
 
@@ -26,9 +28,11 @@ class Pagy
26
28
  autoload :ElasticsearchRails, path.join('classes/offset/search')
27
29
  autoload :Meilisearch, path.join('classes/offset/search')
28
30
  autoload :Searchkick, path.join('classes/offset/search')
31
+ autoload :TypesenseRails, path.join('classes/offset/search')
29
32
  autoload :Keyset, path.join('classes/keyset/keyset')
30
33
 
31
- def self.options = @options ||= {}
34
+ OPTIONS = {} # rubocop:disable Style/MutableConstant
35
+ def self.options = OPTIONS
32
36
 
33
37
  extend Configurable
34
38
  include Linkable
@@ -46,11 +50,16 @@ class Pagy
46
50
  def keyset? = false
47
51
  def keynav? = false
48
52
 
49
- # Validates and assign the passed options: var must be present and value.to_i must be >= to min
53
+ # Validates and assign the passed options: they must be present and value.to_i must be >= min
50
54
  def assign_and_check(name_min)
51
55
  name_min.each do |name, min|
52
- raise OptionError.new(self, name, ">= #{min}", @options[name]) \
53
- unless @options[name].respond_to?(:to_i) && instance_variable_set(:"@#{name}", @options[name].to_i) >= min
56
+ value = @options[name]
57
+
58
+ if value.respond_to?(:to_i) && (integer = value.to_i) >= min
59
+ instance_variable_set(:"@#{name}", integer)
60
+ else
61
+ raise OptionError.new(self, name, ">= #{min}", value)
62
+ end
54
63
  end
55
64
  end
56
65
 
@@ -59,10 +68,14 @@ class Pagy
59
68
  @request = options.delete(:request) # internal object
60
69
  default = {}
61
70
  current = self.class
62
- begin
71
+
72
+ loop do
63
73
  default = current::DEFAULT.merge(default)
64
74
  current = current.superclass
65
- end until current == Object # rubocop:disable Lint/Loop -- see https://github.com/rubocop/rubocop-performance/issues/362
66
- @options = default.merge!(options.delete_if { |k, v| default.key?(k) && (v.nil? || v == '') }).freeze
75
+ break if current == Object
76
+ end
77
+
78
+ clean_options = options.delete_if { |k, v| default.key?(k) && (v.nil? || v == '') }
79
+ @options = default.merge!(clean_options).freeze
67
80
  end
68
81
  end
data/locales/id.yml CHANGED
@@ -4,9 +4,7 @@ id:
4
4
  pagy:
5
5
  p11n: 'Other'
6
6
  aria_label:
7
- # please add a comment in the https://github.com/ddnexus/pagy/issues/588
8
- # posting the translation of the following "Page"/"Pages" with the plurals for this locale
9
- nav: "Pages"
7
+ nav: "Halaman"
10
8
  previous: "Sebelumnya"
11
9
  next: "Selanjutnya"
12
10
  previous: "&lt;"
data/locales/ja.yml CHANGED
@@ -4,9 +4,7 @@ ja:
4
4
  pagy:
5
5
  p11n: 'Other'
6
6
  aria_label:
7
- # please add a comment in the https://github.com/ddnexus/pagy/issues/590
8
- # posting the translation of the following "Page"/"Pages" with the plurals for this locale
9
- nav: "Pages"
7
+ nav: "ページ"
10
8
  previous: "前へ"
11
9
  next: "次へ"
12
10
  previous: "&lt;"
data/locales/km.yml CHANGED
@@ -4,9 +4,7 @@ km:
4
4
  pagy:
5
5
  p11n: 'Other'
6
6
  aria_label:
7
- # please add a comment in the https://github.com/ddnexus/pagy/issues/591
8
- # posting the translation of the following "Page"/"Pages" with the plurals for this locale
9
- nav: "Pages"
7
+ nav: "ទំព័រ"
10
8
  previous: "មុន"
11
9
  next: "បន្ទាប់"
12
10
  previous: "&lt;"
data/locales/sw.yml CHANGED
@@ -9,8 +9,8 @@ sw:
9
9
  # after you make these changes.
10
10
  aria_label:
11
11
  nav:
12
- one: "Page"
13
- other: "Pages"
12
+ one: "Ukurasa"
13
+ other: "Kurasa"
14
14
  previous: "Awali"
15
15
  next: "Ifuatayo"
16
16
  previous: "&lt;"