pagy 7.0.11 → 43.5.1
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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/apps/calendar.ru +741 -0
- data/apps/demo.ru +513 -0
- data/apps/enable_rails_page_segment.rb +54 -0
- data/apps/index.rb +9 -0
- data/apps/keynav+root_key.ru +316 -0
- data/apps/keynav.ru +255 -0
- data/apps/keyset.ru +219 -0
- data/apps/keyset_sequel.ru +212 -0
- data/apps/rails.ru +216 -0
- data/apps/repro.ru +185 -0
- data/bin/pagy +5 -0
- data/config/pagy.rb +46 -0
- data/javascripts/ai_widget.js +90 -0
- data/javascripts/pagy.js +168 -0
- data/javascripts/pagy.min.js +2 -0
- data/javascripts/pagy.mjs +161 -0
- data/javascripts/wand.js +1172 -0
- data/lib/pagy/classes/calendar/calendar.rb +101 -0
- data/lib/pagy/{calendar → classes/calendar}/day.rb +9 -12
- data/lib/pagy/{calendar → classes/calendar}/month.rb +7 -11
- data/lib/pagy/{calendar → classes/calendar}/quarter.rb +12 -16
- data/lib/pagy/classes/calendar/unit.rb +93 -0
- data/lib/pagy/{calendar → classes/calendar}/week.rb +7 -11
- data/lib/pagy/{calendar → classes/calendar}/year.rb +9 -9
- data/lib/pagy/classes/exceptions.rb +26 -0
- data/lib/pagy/classes/keyset/adapters/active_record.rb +50 -0
- data/lib/pagy/classes/keyset/adapters/sequel.rb +62 -0
- data/lib/pagy/classes/keyset/keynav.rb +85 -0
- data/lib/pagy/classes/keyset/keyset.rb +150 -0
- data/lib/pagy/classes/offset/countish.rb +17 -0
- data/lib/pagy/classes/offset/countless.rb +63 -0
- data/lib/pagy/classes/offset/offset.rb +63 -0
- data/lib/pagy/classes/offset/search.rb +34 -0
- data/lib/pagy/classes/request.rb +48 -0
- data/lib/pagy/cli.rb +122 -0
- data/lib/pagy/console.rb +5 -20
- data/lib/pagy/deprecated.rb +84 -0
- data/lib/pagy/modules/abilities/configurable.rb +37 -0
- data/lib/pagy/modules/abilities/countable.rb +23 -0
- data/lib/pagy/modules/abilities/linkable.rb +72 -0
- data/lib/pagy/modules/abilities/rangeable.rb +14 -0
- data/lib/pagy/modules/abilities/shiftable.rb +12 -0
- data/lib/pagy/modules/b64.rb +35 -0
- data/lib/pagy/modules/console.rb +33 -0
- data/lib/pagy/modules/i18n/i18n.rb +72 -0
- data/lib/pagy/modules/i18n/p11n/arabic.rb +30 -0
- data/lib/pagy/modules/i18n/p11n/east_slavic.rb +27 -0
- data/lib/pagy/modules/i18n/p11n/one_other.rb +15 -0
- data/lib/pagy/modules/i18n/p11n/one_upto_two_other.rb +15 -0
- data/lib/pagy/modules/i18n/p11n/other.rb +13 -0
- data/lib/pagy/modules/i18n/p11n/polish.rb +27 -0
- data/lib/pagy/modules/i18n/p11n/west_slavic.rb +22 -0
- data/lib/pagy/modules/i18n/p11n.rb +16 -0
- data/lib/pagy/modules/searcher.rb +20 -0
- data/lib/pagy/next.rb +25 -0
- data/lib/pagy/tasks/sync.rb +20 -0
- data/lib/pagy/toolbox/helpers/anchor_tags.rb +21 -0
- data/lib/pagy/toolbox/helpers/bootstrap/input_nav_js.rb +28 -0
- data/lib/pagy/toolbox/helpers/bootstrap/previous_next_html.rb +19 -0
- data/lib/pagy/toolbox/helpers/bootstrap/series_nav.rb +32 -0
- data/lib/pagy/toolbox/helpers/bootstrap/series_nav_js.rb +24 -0
- data/lib/pagy/toolbox/helpers/bulma/input_nav_js.rb +25 -0
- data/lib/pagy/toolbox/helpers/bulma/previous_next_html.rb +20 -0
- data/lib/pagy/toolbox/helpers/bulma/series_nav.rb +31 -0
- data/lib/pagy/toolbox/helpers/bulma/series_nav_js.rb +23 -0
- data/lib/pagy/toolbox/helpers/data_hash.rb +29 -0
- data/lib/pagy/toolbox/helpers/headers_hash.rb +30 -0
- data/lib/pagy/toolbox/helpers/info_tag.rb +30 -0
- data/lib/pagy/toolbox/helpers/input_nav_js.rb +22 -0
- data/lib/pagy/toolbox/helpers/limit_tag_js.rb +25 -0
- data/lib/pagy/toolbox/helpers/loaders.rb +55 -0
- data/lib/pagy/toolbox/helpers/page_url.rb +16 -0
- data/lib/pagy/toolbox/helpers/series_nav.rb +30 -0
- data/lib/pagy/toolbox/helpers/series_nav_js.rb +20 -0
- data/lib/pagy/toolbox/helpers/support/a_lambda.rb +36 -0
- data/lib/pagy/toolbox/helpers/support/data_pagy_attribute.rb +19 -0
- data/lib/pagy/toolbox/helpers/support/nav_aria_label_attribute.rb +10 -0
- data/lib/pagy/toolbox/helpers/support/series.rb +37 -0
- data/lib/pagy/toolbox/helpers/support/wrap_input_nav_js.rb +19 -0
- data/lib/pagy/toolbox/helpers/support/wrap_series_nav.rb +17 -0
- data/lib/pagy/toolbox/helpers/support/wrap_series_nav_js.rb +42 -0
- data/lib/pagy/toolbox/helpers/urls_hash.rb +12 -0
- data/lib/pagy/toolbox/paginators/calendar.rb +34 -0
- data/lib/pagy/toolbox/paginators/countish.rb +38 -0
- data/lib/pagy/toolbox/paginators/countless.rb +22 -0
- data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +56 -0
- data/lib/pagy/toolbox/paginators/keynav_js.rb +26 -0
- data/lib/pagy/toolbox/paginators/keyset.rb +15 -0
- data/lib/pagy/toolbox/paginators/meilisearch.rb +34 -0
- data/lib/pagy/toolbox/paginators/method.rb +38 -0
- data/lib/pagy/toolbox/paginators/offset.rb +24 -0
- data/lib/pagy/toolbox/paginators/searchkick.rb +34 -0
- data/lib/pagy/toolbox/paginators/typesense_rails.rb +34 -0
- data/lib/pagy.rb +67 -131
- data/locales/ar.yml +32 -0
- data/locales/be.yml +28 -0
- data/locales/bg.yml +24 -0
- data/locales/bs.yml +28 -0
- data/locales/ca.yml +24 -0
- data/locales/ckb.yml +20 -0
- data/locales/cs.yml +26 -0
- data/locales/da.yml +24 -0
- data/locales/de.yml +24 -0
- data/locales/dz.yml +20 -0
- data/locales/en.yml +24 -0
- data/{lib/locales → locales}/es.yml +9 -6
- data/locales/fr.yml +24 -0
- data/locales/hr.yml +28 -0
- data/locales/id.yml +20 -0
- data/locales/it.yml +24 -0
- data/locales/ja.yml +20 -0
- data/locales/km.yml +20 -0
- data/locales/ko.yml +20 -0
- data/locales/nb.yml +24 -0
- data/locales/nl.yml +24 -0
- data/locales/nn.yml +24 -0
- data/locales/pl.yml +28 -0
- data/{lib/locales → locales}/pt-BR.yml +10 -7
- data/{lib/locales → locales}/pt.yml +10 -7
- data/locales/ru.yml +28 -0
- data/locales/sk.yml +26 -0
- data/locales/sr.yml +28 -0
- data/locales/sv-SE.yml +24 -0
- data/locales/sv.yml +24 -0
- data/locales/sw.yml +28 -0
- data/locales/ta.yml +24 -0
- data/locales/tr.yml +24 -0
- data/locales/uk.yml +28 -0
- data/locales/vi.yml +20 -0
- data/locales/zh-CN.yml +20 -0
- data/locales/zh-HK.yml +20 -0
- data/locales/zh-TW.yml +20 -0
- data/stylesheets/pagy-tailwind.css +68 -0
- data/stylesheets/pagy.css +83 -0
- metadata +185 -94
- data/lib/config/pagy.rb +0 -258
- data/lib/javascripts/pagy-dev.js +0 -112
- data/lib/javascripts/pagy-module.js +0 -111
- data/lib/javascripts/pagy.js +0 -1
- data/lib/locales/ar.yml +0 -30
- data/lib/locales/be.yml +0 -25
- data/lib/locales/bg.yml +0 -21
- data/lib/locales/bs.yml +0 -25
- data/lib/locales/ca.yml +0 -23
- data/lib/locales/ckb.yml +0 -18
- data/lib/locales/cs.yml +0 -23
- data/lib/locales/da.yml +0 -23
- data/lib/locales/de.yml +0 -21
- data/lib/locales/en.yml +0 -21
- data/lib/locales/fr.yml +0 -21
- data/lib/locales/hr.yml +0 -25
- data/lib/locales/id.yml +0 -19
- data/lib/locales/it.yml +0 -21
- data/lib/locales/ja.yml +0 -19
- data/lib/locales/km.yml +0 -19
- data/lib/locales/ko.yml +0 -19
- data/lib/locales/nb.yml +0 -21
- data/lib/locales/nl.yml +0 -21
- data/lib/locales/nn.yml +0 -21
- data/lib/locales/pl.yml +0 -25
- data/lib/locales/ru.yml +0 -27
- data/lib/locales/sr.yml +0 -25
- data/lib/locales/sv-SE.yml +0 -21
- data/lib/locales/sv.yml +0 -21
- data/lib/locales/sw.yml +0 -23
- data/lib/locales/ta.yml +0 -23
- data/lib/locales/tr.yml +0 -19
- data/lib/locales/uk.yml +0 -25
- data/lib/locales/vi.yml +0 -17
- data/lib/locales/zh-CN.yml +0 -19
- data/lib/locales/zh-HK.yml +0 -19
- data/lib/locales/zh-TW.yml +0 -19
- data/lib/pagy/backend.rb +0 -39
- data/lib/pagy/calendar/helper.rb +0 -65
- data/lib/pagy/calendar.rb +0 -126
- data/lib/pagy/countless.rb +0 -37
- data/lib/pagy/exceptions.rb +0 -25
- data/lib/pagy/extras/arel.rb +0 -36
- data/lib/pagy/extras/array.rb +0 -24
- data/lib/pagy/extras/bootstrap.rb +0 -108
- data/lib/pagy/extras/bulma.rb +0 -105
- data/lib/pagy/extras/calendar.rb +0 -53
- data/lib/pagy/extras/countless.rb +0 -37
- data/lib/pagy/extras/elasticsearch_rails.rb +0 -80
- data/lib/pagy/extras/foundation.rb +0 -105
- data/lib/pagy/extras/frontend_helpers.rb +0 -67
- data/lib/pagy/extras/gearbox.rb +0 -54
- data/lib/pagy/extras/headers.rb +0 -53
- data/lib/pagy/extras/i18n.rb +0 -26
- data/lib/pagy/extras/items.rb +0 -61
- data/lib/pagy/extras/jsonapi.rb +0 -79
- data/lib/pagy/extras/materialize.rb +0 -96
- data/lib/pagy/extras/meilisearch.rb +0 -65
- data/lib/pagy/extras/metadata.rb +0 -38
- data/lib/pagy/extras/navs.rb +0 -51
- data/lib/pagy/extras/overflow.rb +0 -80
- data/lib/pagy/extras/searchkick.rb +0 -67
- data/lib/pagy/extras/semantic.rb +0 -95
- data/lib/pagy/extras/standalone.rb +0 -60
- data/lib/pagy/extras/support.rb +0 -40
- data/lib/pagy/extras/trim.rb +0 -29
- data/lib/pagy/extras/uikit.rb +0 -97
- data/lib/pagy/frontend.rb +0 -114
- data/lib/pagy/i18n.rb +0 -165
- data/lib/pagy/url_helpers.rb +0 -27
- data/lib/stylesheets/pagy.css +0 -61
- data/lib/stylesheets/pagy.scss +0 -50
- data/lib/stylesheets/pagy.tailwind.scss +0 -24
- /data/{lib/javascripts/pagy-module.d.ts → javascripts/pagy.d.ts} +0 -0
|
@@ -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,27 +1,24 @@
|
|
|
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
|
|
5
|
-
class Calendar
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
order:
|
|
10
|
-
format:
|
|
3
|
+
class Pagy
|
|
4
|
+
class Calendar
|
|
5
|
+
class Day < Unit
|
|
6
|
+
DEFAULT = { slots: 31,
|
|
7
|
+
compact: true,
|
|
8
|
+
order: :asc,
|
|
9
|
+
format: '%d' }.freeze
|
|
11
10
|
|
|
12
11
|
protected
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
def setup_unit_vars
|
|
13
|
+
def assign_unit_variables
|
|
16
14
|
super
|
|
17
15
|
@initial = @starting.beginning_of_day
|
|
18
16
|
@final = @ending.tomorrow.beginning_of_day
|
|
19
|
-
@
|
|
17
|
+
@last = page_offset(@initial, @final)
|
|
20
18
|
@from = starting_time_for(@page)
|
|
21
19
|
@to = @from.tomorrow
|
|
22
20
|
end
|
|
23
21
|
|
|
24
|
-
# Starting time for the page
|
|
25
22
|
def starting_time_for(page)
|
|
26
23
|
@initial.days_since(time_offset_for(page))
|
|
27
24
|
end
|
|
@@ -1,27 +1,24 @@
|
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
order:
|
|
10
|
-
format:
|
|
5
|
+
class Month < Unit
|
|
6
|
+
DEFAULT = { slots: 12,
|
|
7
|
+
compact: true,
|
|
8
|
+
order: :asc,
|
|
9
|
+
format: '%b' }.freeze
|
|
11
10
|
|
|
12
11
|
protected
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
def setup_unit_vars
|
|
13
|
+
def assign_unit_variables
|
|
16
14
|
super
|
|
17
15
|
@initial = @starting.beginning_of_month
|
|
18
16
|
@final = @ending.next_month.beginning_of_month
|
|
19
|
-
@
|
|
17
|
+
@last = (months_in(@final) - months_in(@initial))
|
|
20
18
|
@from = starting_time_for(@page)
|
|
21
19
|
@to = @from.next_month
|
|
22
20
|
end
|
|
23
21
|
|
|
24
|
-
# Starting time for the page
|
|
25
22
|
def starting_time_for(page)
|
|
26
23
|
@initial.months_since(time_offset_for(page))
|
|
27
24
|
end
|
|
@@ -32,7 +29,6 @@ class Pagy # :nodoc:
|
|
|
32
29
|
|
|
33
30
|
private
|
|
34
31
|
|
|
35
|
-
# Number of months in time
|
|
36
32
|
def months_in(time)
|
|
37
33
|
(time.year * 12) + time.month
|
|
38
34
|
end
|
|
@@ -1,34 +1,31 @@
|
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
order:
|
|
10
|
-
format:
|
|
5
|
+
class Quarter < Unit
|
|
6
|
+
DEFAULT = { slots: 4,
|
|
7
|
+
compact: true,
|
|
8
|
+
order: :asc,
|
|
9
|
+
format: 'Q%q' }.freeze # '%q' token
|
|
10
|
+
|
|
11
|
+
protected
|
|
11
12
|
|
|
12
13
|
# The label for any page, with the substitution of the '%q' token
|
|
13
|
-
def
|
|
14
|
+
def page_label(page, **options)
|
|
14
15
|
starting_time = starting_time_for(page.to_i) # page could be a string
|
|
15
|
-
|
|
16
|
-
localize(starting_time,
|
|
16
|
+
options[:format] = (options[:format] || @options[:format]).gsub('%q') { (starting_time.month / 3.0).ceil }
|
|
17
|
+
localize(starting_time, **options)
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
# Setup the calendar variables
|
|
22
|
-
def setup_unit_vars
|
|
20
|
+
def assign_unit_variables
|
|
23
21
|
super
|
|
24
22
|
@initial = @starting.beginning_of_quarter
|
|
25
23
|
@final = @ending.next_quarter.beginning_of_quarter
|
|
26
|
-
@
|
|
24
|
+
@last = (months_in(@final) - months_in(@initial)) / 3
|
|
27
25
|
@from = starting_time_for(@page)
|
|
28
26
|
@to = @from.next_quarter
|
|
29
27
|
end
|
|
30
28
|
|
|
31
|
-
# Starting time for the page
|
|
32
29
|
def starting_time_for(page)
|
|
33
30
|
@initial.months_since(time_offset_for(page) * 3)
|
|
34
31
|
end
|
|
@@ -39,7 +36,6 @@ class Pagy # :nodoc:
|
|
|
39
36
|
|
|
40
37
|
private
|
|
41
38
|
|
|
42
|
-
# Number of months in time
|
|
43
39
|
def months_in(time)
|
|
44
40
|
(time.year * 12) + time.month
|
|
45
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,26 +1,22 @@
|
|
|
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
|
|
5
|
-
class Calendar
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
format: '%Y-%W' }
|
|
3
|
+
class Pagy
|
|
4
|
+
class Calendar
|
|
5
|
+
class Week < Unit
|
|
6
|
+
DEFAULT = { order: :asc,
|
|
7
|
+
format: '%Y-%W' }.freeze
|
|
10
8
|
|
|
11
9
|
protected
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
def setup_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
|
|
18
|
-
@
|
|
15
|
+
@last = page_offset(@initial, @final)
|
|
19
16
|
@from = starting_time_for(@page)
|
|
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,22 +1,22 @@
|
|
|
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
|
-
#
|
|
7
|
-
class Year <
|
|
8
|
-
DEFAULT = {
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
# Year unit subclass
|
|
6
|
+
class Year < Unit
|
|
7
|
+
DEFAULT = { slots: 10,
|
|
8
|
+
compact: true,
|
|
9
|
+
order: :asc,
|
|
10
|
+
format: '%Y' }.freeze
|
|
11
11
|
|
|
12
12
|
protected
|
|
13
13
|
|
|
14
|
-
#
|
|
15
|
-
def
|
|
14
|
+
# Set up the calendar variables
|
|
15
|
+
def assign_unit_variables
|
|
16
16
|
super
|
|
17
17
|
@initial = @starting.beginning_of_year
|
|
18
18
|
@final = @ending.next_year.beginning_of_year
|
|
19
|
-
@
|
|
19
|
+
@last = @final.year - @initial.year
|
|
20
20
|
@from = starting_time_for(@page)
|
|
21
21
|
@to = @from.next_year
|
|
22
22
|
end
|
|
@@ -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
|