pagy 8.4.5 → 8.6.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.
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require_relative '../calendar'
5
- require_relative '../calendar/helper'
6
5
 
7
6
  class Pagy # :nodoc:
8
7
  # Add pagination filtering by calendar unit (:year, :quarter, :month, :week, :day) to the regular pagination
@@ -20,8 +19,13 @@ class Pagy # :nodoc:
20
19
 
21
20
  conf[:pagy] ||= {}
22
21
  unless conf.key?(:active) && !conf[:active]
23
- calendar, from, to = Calendar::Helper.send(:init, conf, pagy_calendar_period(collection), params)
24
- collection = pagy_calendar_filter(collection, from, to)
22
+ calendar, from, to = Calendar.send(:init, conf, pagy_calendar_period(collection), params)
23
+ if respond_to?(:pagy_calendar_counts)
24
+ calendar.each_key do |unit|
25
+ calendar[unit].vars[:counts] = pagy_calendar_counts(collection, unit, *calendar[unit].vars[:period])
26
+ end
27
+ end
28
+ collection = pagy_calendar_filter(collection, from, to)
25
29
  end
26
30
  pagy, results = send(conf[:pagy][:backend] || :pagy, collection, conf[:pagy]) # use backend: :pagy when omitted
27
31
  [calendar, pagy, results]
@@ -40,6 +44,32 @@ class Pagy # :nodoc:
40
44
  end
41
45
  end
42
46
 
47
+ # Override the pagy_anchor
48
+ module FrontendOverride
49
+ # Consider the vars[:count]
50
+ def pagy_anchor(pagy)
51
+ return super unless (counts = pagy.vars[:counts])
52
+
53
+ a_string = pagy.vars[:anchor_string]
54
+ a_string = %( #{a_string}) if a_string
55
+ left, right = %(<a#{a_string} href="#{pagy_url_for(pagy, PAGE_TOKEN)}").split(PAGE_TOKEN, 2)
56
+ # lambda used by all the helpers
57
+ lambda do |page, text = pagy.label_for(page), classes: nil, aria_label: nil|
58
+ count = counts[page - 1]
59
+ item_name = pagy_t('pagy.item_name', count:)
60
+ if count.zero?
61
+ classes = "#{classes && (classes + ' ')}empty-page"
62
+ title = %( title="#{pagy_t('pagy.info.no_items', item_name:, count:)}")
63
+ else
64
+ title = %( title="#{pagy_t('pagy.info.single_page', item_name:, count:)}")
65
+ end
66
+ classes = %( class="#{classes}") if classes
67
+ aria_label = %( aria-label="#{aria_label}") if aria_label
68
+ %(#{left}#{page}#{right}#{title}#{classes}#{aria_label}>#{text}</a>)
69
+ end
70
+ end
71
+ end
72
+
43
73
  # Additions for the Frontend module
44
74
  module UrlHelperAddOn
45
75
  # Return the url for the calendar page at time
@@ -49,5 +79,5 @@ class Pagy # :nodoc:
49
79
  end
50
80
  end
51
81
  Backend.prepend CalendarExtra::BackendAddOn, CalendarExtra::UrlHelperAddOn
52
- Frontend.prepend CalendarExtra::UrlHelperAddOn
82
+ Frontend.prepend CalendarExtra::UrlHelperAddOn, CalendarExtra::FrontendOverride
53
83
  end
@@ -19,7 +19,7 @@ class Pagy # :nodoc:
19
19
  end
20
20
  end
21
21
  end
22
- Calendar.prepend I18nExtra::CalendarOverride if defined?(Calendar)
22
+ Calendar::Unit.prepend I18nExtra::CalendarOverride if defined?(Calendar::Unit)
23
23
 
24
24
  # Add the pagy locales to the I18n.load_path
25
25
  ::I18n.load_path += Dir[Pagy.root.join('locales', '*.yml')]
@@ -42,7 +42,7 @@ class Pagy # :nodoc:
42
42
  end
43
43
  end
44
44
  end
45
- Calendar.prepend CalendarOverride if defined?(Calendar)
45
+ Calendar::Unit.prepend CalendarOverride if defined?(Calendar::Unit)
46
46
 
47
47
  # Additions for the Frontend
48
48
  module FrontendAddOn
@@ -17,7 +17,11 @@ class Pagy # :nodoc:
17
17
  def pagy_metadata(pagy, absolute: nil)
18
18
  scaffold_url = pagy_url_for(pagy, PAGE_TOKEN, absolute:)
19
19
  {}.tap do |metadata|
20
- keys = defined?(Calendar) && pagy.is_a?(Calendar) ? pagy.vars[:metadata] - %i[count items] : pagy.vars[:metadata]
20
+ keys = if defined?(Calendar::Unit) && pagy.is_a?(Calendar::Unit)
21
+ pagy.vars[:metadata] - %i[count items]
22
+ else
23
+ pagy.vars[:metadata]
24
+ end
21
25
  keys.each do |key|
22
26
  metadata[key] = case key
23
27
  when :scaffold_url then scaffold_url
@@ -28,7 +28,8 @@ class Pagy # :nodoc:
28
28
  @vars[:page] = requested_page # restore the requested page
29
29
  when :empty_page
30
30
  @offset = @items = @in = @from = @to = 0 # vars relative to the actual page
31
- if defined?(Calendar) && is_a?(Calendar) # only for Calendar instances
31
+ if defined?(Calendar::Unit) \
32
+ && is_a?(Calendar::Unit) # only for Calendar::Units instances
32
33
  edge = @order == :asc ? @final : @initial # get the edge of the overflow side (neat, but any time would do)
33
34
  @from = @to = edge # set both to the edge utc time (a >=&&< query will get no records)
34
35
  end
@@ -51,7 +52,7 @@ class Pagy # :nodoc:
51
52
  end
52
53
  end
53
54
  Pagy.prepend PagyOverride
54
- Pagy::Calendar.prepend PagyOverride if defined?(Calendar)
55
+ Pagy::Calendar::Unit.prepend PagyOverride if defined?(Calendar::Unit)
55
56
 
56
57
  # Support for Pagy::Countless class
57
58
  module CountlessOverride
@@ -0,0 +1,40 @@
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
+ # Setup @items based on the :gearbox_items variable
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 size 4 || 0', 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
data/lib/pagy/frontend.rb CHANGED
@@ -20,7 +20,7 @@ class Pagy
20
20
  a = pagy_anchor(pagy)
21
21
 
22
22
  html = %(<nav#{id} class="pagy nav" #{nav_aria_label(pagy, aria_label:)}>#{
23
- prev_a(pagy, a)})
23
+ prev_a(pagy, a)})
24
24
  pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
25
25
  html << case item
26
26
  when Integer
data/lib/pagy.rb CHANGED
@@ -5,7 +5,7 @@ require 'pathname'
5
5
 
6
6
  # Core class
7
7
  class Pagy
8
- VERSION = '8.4.5'
8
+ VERSION = '8.6.0'
9
9
 
10
10
  # Gem root pathname to get the path of Pagy files stylesheets, javascripts, apps, locales, etc.
11
11
  def self.root
@@ -17,6 +17,7 @@ class Pagy
17
17
  items: 20,
18
18
  outset: 0,
19
19
  size: 7,
20
+ ends: true,
20
21
  count_args: [:all], # AR friendly
21
22
  page_param: :page }
22
23
 
@@ -41,44 +42,33 @@ class Pagy
41
42
 
42
43
  # Return the array of page numbers and :gap items e.g. [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
43
44
  def series(size: @vars[:size], **_)
44
- series = []
45
- if size.is_a?(Array) && size.size == 4 && size.all? { |num| !num.negative? rescue false } # rubocop:disable Style/RescueModifier
46
- # This algorithm is up to ~5x faster and ~2.3x lighter than the previous one (pagy < 4.3)
47
- left_gap_start = 1 + size[0]
48
- left_gap_end = @page - size[1] - 1
49
- right_gap_start = @page + size[2] + 1
50
- right_gap_end = @last - size[3]
51
- left_gap_end = right_gap_end if left_gap_end > right_gap_end
52
- right_gap_start = left_gap_start if left_gap_start > right_gap_start
53
- start = 1
54
- if (left_gap_end - left_gap_start).positive?
55
- series.push(*start...left_gap_start, :gap)
56
- start = left_gap_end + 1
45
+ raise VariableError.new(self, :size, 'to be a positive Integer or 0', size) \
46
+ unless size.is_a?(Integer)
47
+ return [] if size.zero?
48
+
49
+ [].tap do |series|
50
+ if size >= @last
51
+ series.push(*1..@last)
52
+ else
53
+ left = ((size - 1) / 2.0).floor # left half might be 1 page shorter for even size
54
+ start = if @page <= left # beginning pages
55
+ 1
56
+ elsif @page > @last - (size - left) # end pages
57
+ @last - size + 1
58
+ else # intermediate pages
59
+ @page - left
60
+ end
61
+ series.push(*start...start + size)
62
+ # Insert first and last ends plus gaps when needed
63
+ if vars[:ends] && size >= 7
64
+ series[0] = 1 unless series[0] == 1
65
+ series[1] = :gap unless series[1] == 2
66
+ series[-2] = :gap unless series[-2] == @last - 1
67
+ series[-1] = @last unless series[-1] == @last
68
+ end
57
69
  end
58
- if (right_gap_end - right_gap_start).positive?
59
- series.push(*start...right_gap_start, :gap)
60
- start = right_gap_end + 1
61
- end
62
- series.push(*start..@last)
63
- elsif size.is_a?(Integer) && size.positive? # only central series
64
- # The simplest and fastest algorithm
65
- size = @last if size > @last # reduce the max size to @last
66
- left = ((size - 1) / 2.0).floor # left half might be 1 page shorter for even size
67
- start = if @page <= left # beginning pages
68
- 1
69
- elsif @page > @last - (size - left) # end pages
70
- @last - size + 1
71
- else # intermediate pages
72
- @page - left
73
- end
74
- series = (start..start + size - 1).to_a
75
- else
76
- return [] if size.empty?
77
-
78
- raise VariableError.new(self, :size, 'to be a single positive Integer or an Array of 4', size)
70
+ series[series.index(@page)] = @page.to_s
79
71
  end
80
- series[series.index(@page)] = @page.to_s
81
- series
82
72
  end
83
73
 
84
74
  # Label for any page. Allow the customization of the output (overridden by the calendar extra)
@@ -124,6 +114,7 @@ class Pagy
124
114
  end
125
115
  end
126
116
 
117
+ require_relative 'pagy/extras/size' # will be opt in in v9.0
127
118
  require_relative 'pagy/backend'
128
119
  require_relative 'pagy/frontend'
129
120
  require_relative 'pagy/exceptions'
data/locales/ar.yml CHANGED
@@ -2,14 +2,12 @@
2
2
  ar:
3
3
  pagy:
4
4
  aria_label:
5
- # please add a comment in the https://github.com/ddnexus/pagy/issues/577
6
- # posting the translation of the following "Page"/"Pages" with the plurals for this locale
7
- nav: "Pages"
8
- # zero: ""
9
- # two: ""
10
- # few: ""
11
- # many: ""
12
- # other: ""
5
+ nav:
6
+ zero: "لا يوجد صفحات"
7
+ one: "صفحة"
8
+ two: "صفحتين"
9
+ few: "صفحات"
10
+ many: "صفحات"
13
11
  prev: "السابق"
14
12
  next: "التالي"
15
13
  prev: "&lt;"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pagy
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.4.5
4
+ version: 8.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Domizio Demichelis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-19 00:00:00.000000000 Z
11
+ date: 2024-06-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Agnostic pagination in plain ruby. It does it all. Better.
14
14
  email:
@@ -23,6 +23,11 @@ files:
23
23
  - apps/demo.ru
24
24
  - apps/rails.ru
25
25
  - apps/repro.ru
26
+ - apps/tmp/calendar.sqlite3
27
+ - apps/tmp/calendar.sqlite3-shm
28
+ - apps/tmp/calendar.sqlite3-wal
29
+ - apps/tmp/local_secret.txt
30
+ - apps/tmp/pagy-rails.sqlite3
26
31
  - bin/pagy
27
32
  - config/pagy.rb
28
33
  - javascripts/pagy-module.js
@@ -36,9 +41,9 @@ files:
36
41
  - lib/pagy/backend.rb
37
42
  - lib/pagy/calendar.rb
38
43
  - lib/pagy/calendar/day.rb
39
- - lib/pagy/calendar/helper.rb
40
44
  - lib/pagy/calendar/month.rb
41
45
  - lib/pagy/calendar/quarter.rb
46
+ - lib/pagy/calendar/unit.rb
42
47
  - lib/pagy/calendar/week.rb
43
48
  - lib/pagy/calendar/year.rb
44
49
  - lib/pagy/console.rb
@@ -65,6 +70,7 @@ files:
65
70
  - lib/pagy/extras/pagy.rb
66
71
  - lib/pagy/extras/searchkick.rb
67
72
  - lib/pagy/extras/semantic.rb
73
+ - lib/pagy/extras/size.rb
68
74
  - lib/pagy/extras/standalone.rb
69
75
  - lib/pagy/extras/trim.rb
70
76
  - lib/pagy/extras/uikit.rb
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Pagy # :nodoc:
4
- class Calendar # :nodoc:
5
- # Initializes the calendar objects, reducing complexity in the extra
6
- # The returned calendar is a simple hash of units/objects
7
- class Helper < Hash
8
- class << self
9
- private
10
-
11
- def init(conf, period, params)
12
- new.send(:init, conf, period, params)
13
- end
14
- end
15
-
16
- private
17
-
18
- # Create the calendar
19
- def init(conf, period, params)
20
- @conf = Marshal.load(Marshal.dump(conf)) # store a copy
21
- @units = Calendar::UNITS & @conf.keys # get the units in time length desc order
22
- raise ArgumentError, 'no calendar unit found in pagy_calendar @configuration' if @units.empty?
23
-
24
- @period = period
25
- @params = params
26
- @page_param = conf[:pagy][:page_param] || DEFAULT[:page_param]
27
- @units.each do |unit| # set all the :page_param vars for later deletion
28
- unit_page_param = :"#{unit}_#{@page_param}"
29
- conf[unit][:page_param] = unit_page_param
30
- conf[unit][:page] = @params[unit_page_param]
31
- end
32
- calendar = {}
33
- object = nil
34
- @units.each_with_index do |unit, index|
35
- params_to_delete = @units[(index + 1), @units.size].map { |sub| conf[sub][:page_param] } + [@page_param]
36
- conf[unit][:params] = lambda { |up| up.except(*params_to_delete.map(&:to_s)) } # rubocop:disable Style/Lambda
37
- conf[unit][:period] = object&.send(:active_period) || @period
38
- calendar[unit] = object = Calendar.send(:create, unit, conf[unit])
39
- end
40
- [replace(calendar), object.from, object.to]
41
- end
42
-
43
- # Return the calendar object at time
44
- def calendar_at(time, **opts)
45
- conf = Marshal.load(Marshal.dump(@conf))
46
- page_params = {}
47
- @units.inject(nil) do |object, unit|
48
- conf[unit][:period] = object&.send(:active_period) || @period
49
- conf[unit][:page] = page_params[:"#{unit}_#{@page_param}"] \
50
- = Calendar.send(:create, unit, conf[unit]).send(:page_at, time, **opts)
51
- conf[unit][:params] ||= {}
52
- conf[unit][:params].merge!(page_params)
53
- Calendar.send(:create, unit, conf[unit])
54
- end
55
- end
56
-
57
- public
58
-
59
- # Return the current time of the smallest time unit shown
60
- def showtime
61
- self[@units.last].from
62
- end
63
- end
64
- end
65
- end