pagy 9.3.5 → 43.0.0.rc2
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/apps/calendar.ru +547 -551
- data/apps/demo.ru +221 -178
- data/apps/index.rb +3 -1
- data/apps/keynav.ru +258 -0
- data/apps/{keyset_ar.ru → keyset.ru} +27 -32
- data/apps/{keyset_s.ru → keyset_sequel.ru} +24 -29
- data/apps/rails.ru +51 -48
- data/apps/repro.ru +46 -43
- data/bin/pagy +20 -21
- data/config/pagy.rb +35 -207
- data/javascripts/ai_widget.js +76 -0
- data/javascripts/pagy.js +151 -0
- data/javascripts/pagy.js.map +10 -0
- data/javascripts/pagy.min.js +1 -4
- data/javascripts/pagy.mjs +111 -63
- data/javascripts/wand.js +1166 -0
- data/lib/pagy/classes/calendar/calendar.rb +98 -0
- data/lib/pagy/{calendar → classes/calendar}/day.rb +7 -11
- data/lib/pagy/{calendar → classes/calendar}/month.rb +5 -10
- data/lib/pagy/{calendar → classes/calendar}/quarter.rb +10 -15
- data/lib/pagy/classes/calendar/unit.rb +92 -0
- data/lib/pagy/{calendar → classes/calendar}/week.rb +5 -9
- data/lib/pagy/{calendar → classes/calendar}/year.rb +5 -6
- data/lib/pagy/classes/exceptions.rb +33 -0
- data/lib/pagy/classes/keyset/active_record.rb +11 -0
- data/lib/pagy/classes/keyset/adapters/active_record.rb +50 -0
- data/lib/pagy/classes/keyset/adapters/sequel.rb +63 -0
- data/lib/pagy/classes/keyset/keynav/active_record.rb +13 -0
- data/lib/pagy/classes/keyset/keynav/sequel.rb +13 -0
- data/lib/pagy/classes/keyset/keynav.rb +77 -0
- data/lib/pagy/classes/keyset/keyset.rb +126 -0
- data/lib/pagy/classes/keyset/sequel.rb +11 -0
- data/lib/pagy/classes/offset/countless.rb +56 -0
- data/lib/pagy/classes/offset/offset.rb +54 -0
- data/lib/pagy/classes/offset/search.rb +38 -0
- data/lib/pagy/classes/request.rb +36 -0
- data/lib/pagy/modules/abilities/configurable.rb +36 -0
- data/lib/pagy/modules/abilities/linkable.rb +59 -0
- data/lib/pagy/modules/abilities/rangeable.rb +15 -0
- data/lib/pagy/modules/abilities/shiftable.rb +12 -0
- data/lib/pagy/{b64.rb → modules/b64.rb} +3 -7
- data/lib/pagy/modules/console.rb +38 -0
- data/lib/pagy/modules/i18n/i18n.rb +48 -0
- data/lib/pagy/modules/i18n/p11n/arabic.rb +29 -0
- data/lib/pagy/modules/i18n/p11n/east_slavic.rb +26 -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 +26 -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/toolbox/helpers/anchor_tags.rb +25 -0
- data/lib/pagy/toolbox/helpers/bootstrap/input_nav_js.rb +24 -0
- data/lib/pagy/toolbox/helpers/bootstrap/previous_next_html.rb +18 -0
- data/lib/pagy/toolbox/helpers/bootstrap/series_nav.rb +29 -0
- data/lib/pagy/toolbox/helpers/bootstrap/series_nav_js.rb +21 -0
- data/lib/pagy/toolbox/helpers/bulma/input_nav_js.rb +21 -0
- data/lib/pagy/toolbox/helpers/bulma/previous_next_html.rb +19 -0
- data/lib/pagy/toolbox/helpers/bulma/series_nav.rb +28 -0
- data/lib/pagy/toolbox/helpers/bulma/series_nav_js.rb +20 -0
- data/lib/pagy/toolbox/helpers/data_hash.rb +26 -0
- data/lib/pagy/toolbox/helpers/headers_hash.rb +20 -0
- data/lib/pagy/toolbox/helpers/info_tag.rb +26 -0
- data/lib/pagy/toolbox/helpers/input_nav_js.rb +19 -0
- data/lib/pagy/toolbox/helpers/limit_tag_js.rb +23 -0
- data/lib/pagy/toolbox/helpers/loader.rb +33 -0
- data/lib/pagy/toolbox/helpers/page_url.rb +23 -0
- data/lib/pagy/toolbox/helpers/series_nav.rb +29 -0
- data/lib/pagy/toolbox/helpers/series_nav_js.rb +19 -0
- data/lib/pagy/toolbox/helpers/support/a_lambda.rb +34 -0
- data/lib/pagy/toolbox/helpers/support/data_pagy_attribute.rb +15 -0
- data/lib/pagy/toolbox/helpers/support/nav_aria_label_attribute.rb +12 -0
- data/lib/pagy/toolbox/helpers/support/series.rb +38 -0
- data/lib/pagy/toolbox/helpers/support/wrap_input_nav_js.rb +20 -0
- data/lib/pagy/toolbox/helpers/support/wrap_series_nav.rb +17 -0
- data/lib/pagy/toolbox/helpers/support/wrap_series_nav_js.rb +36 -0
- data/lib/pagy/toolbox/helpers/urls_hash.rb +13 -0
- data/lib/pagy/toolbox/paginators/calendar.rb +30 -0
- data/lib/pagy/toolbox/paginators/countless.rb +25 -0
- data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +32 -0
- data/lib/pagy/toolbox/paginators/keynav_js.rb +29 -0
- data/lib/pagy/toolbox/paginators/keyset.rb +18 -0
- data/lib/pagy/toolbox/paginators/meilisearch.rb +32 -0
- data/lib/pagy/toolbox/paginators/method.rb +26 -0
- data/lib/pagy/toolbox/paginators/offset.rb +33 -0
- data/lib/pagy/toolbox/paginators/searchkick.rb +32 -0
- data/lib/pagy.rb +59 -97
- data/locales/ar.yml +9 -6
- data/locales/be.yml +9 -6
- data/locales/bg.yml +9 -6
- data/locales/bs.yml +9 -6
- data/locales/ca.yml +9 -6
- data/locales/ckb.yml +8 -6
- data/locales/cs.yml +9 -6
- data/locales/da.yml +9 -6
- data/locales/de.yml +9 -6
- data/locales/dz.yml +9 -6
- data/locales/en.yml +9 -6
- data/locales/es.yml +9 -6
- data/locales/fr.yml +9 -6
- data/locales/hr.yml +9 -6
- data/locales/id.yml +9 -6
- data/locales/it.yml +9 -6
- data/locales/ja.yml +9 -6
- data/locales/km.yml +9 -6
- data/locales/ko.yml +9 -6
- data/locales/nb.yml +9 -6
- data/locales/nl.yml +9 -6
- data/locales/nn.yml +9 -6
- data/locales/pl.yml +9 -6
- data/locales/pt-BR.yml +10 -7
- data/locales/pt.yml +10 -7
- data/locales/ru.yml +9 -6
- data/locales/sk.yml +26 -0
- data/locales/sr.yml +9 -6
- data/locales/sv-SE.yml +9 -6
- data/locales/sv.yml +9 -6
- data/locales/sw.yml +12 -9
- data/locales/ta.yml +12 -13
- data/locales/tr.yml +9 -6
- data/locales/uk.yml +9 -6
- data/locales/vi.yml +9 -6
- data/locales/zh-CN.yml +9 -6
- data/locales/zh-HK.yml +9 -6
- data/locales/zh-TW.yml +9 -6
- data/stylesheets/pagy-tailwind.css +64 -0
- data/stylesheets/pagy.css +63 -27
- metadata +113 -51
- data/javascripts/pagy.min.js.map +0 -10
- data/lib/pagy/backend.rb +0 -44
- data/lib/pagy/calendar/unit.rb +0 -103
- data/lib/pagy/calendar.rb +0 -84
- data/lib/pagy/console.rb +0 -23
- data/lib/pagy/countless.rb +0 -38
- data/lib/pagy/exceptions.rb +0 -25
- data/lib/pagy/extras/arel.rb +0 -28
- data/lib/pagy/extras/array.rb +0 -19
- data/lib/pagy/extras/bootstrap.rb +0 -97
- data/lib/pagy/extras/bulma.rb +0 -93
- data/lib/pagy/extras/calendar.rb +0 -79
- data/lib/pagy/extras/countless.rb +0 -32
- data/lib/pagy/extras/elasticsearch_rails.rb +0 -71
- data/lib/pagy/extras/gearbox.rb +0 -55
- data/lib/pagy/extras/headers.rb +0 -54
- data/lib/pagy/extras/i18n.rb +0 -26
- data/lib/pagy/extras/js_tools.rb +0 -70
- data/lib/pagy/extras/jsonapi.rb +0 -88
- data/lib/pagy/extras/keyset.rb +0 -30
- data/lib/pagy/extras/limit.rb +0 -63
- data/lib/pagy/extras/meilisearch.rb +0 -57
- data/lib/pagy/extras/metadata.rb +0 -42
- data/lib/pagy/extras/overflow.rb +0 -81
- data/lib/pagy/extras/pagy.rb +0 -82
- data/lib/pagy/extras/searchkick.rb +0 -59
- data/lib/pagy/extras/size.rb +0 -40
- data/lib/pagy/extras/standalone.rb +0 -60
- data/lib/pagy/extras/trim.rb +0 -29
- data/lib/pagy/frontend.rb +0 -99
- data/lib/pagy/i18n.rb +0 -166
- data/lib/pagy/keyset/active_record.rb +0 -44
- data/lib/pagy/keyset/sequel.rb +0 -57
- data/lib/pagy/keyset.rb +0 -118
- data/lib/pagy/shared_methods.rb +0 -26
- data/lib/pagy/url_helpers.rb +0 -26
- data/stylesheets/pagy.scss +0 -48
- data/stylesheets/pagy.tailwind.css +0 -21
@@ -0,0 +1,98 @@
|
|
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
|
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
|
+
# :nocov:
|
25
|
+
# Localize with rails-i18n in any env
|
26
|
+
def localize_with_rails_i18n_gem(*locales)
|
27
|
+
Unit.prepend(Module.new { def localize(...) = ::I18n.localize(...) })
|
28
|
+
raise RailsI18nLoadError, "Pagy: The gem 'rails-i18n' must be installed if you don't use Rails" \
|
29
|
+
unless (path = Gem.loaded_specs['rails-i18n']&.full_gem_path)
|
30
|
+
|
31
|
+
path = Pathname.new(path)
|
32
|
+
::I18n.load_path += locales.map { |locale| path.join("rails/locale/#{locale}.yml") }
|
33
|
+
end
|
34
|
+
# :nocov:
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Return calendar, from, to
|
39
|
+
def init(...)
|
40
|
+
new.send(:init, ...)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return the current time of the smallest time unit shown
|
45
|
+
def showtime
|
46
|
+
self[@units.last].from
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return the url for the calendar (shortest unit) page at time
|
50
|
+
def url_at(time, **)
|
51
|
+
conf = Marshal.load(Marshal.dump(@conf))
|
52
|
+
page_keys = {}
|
53
|
+
@units.inject(nil) do |object, unit|
|
54
|
+
conf[unit][:period] = object&.send(:active_period) || @period
|
55
|
+
conf[unit][:page] = page_keys["#{unit}_#{@page_key}"] \
|
56
|
+
= create(unit, **conf[unit]).send(:page_at, time, **)
|
57
|
+
conf[unit][:querify] = ->(query) { query.merge!(page_keys) }
|
58
|
+
create(unit, **conf[unit])
|
59
|
+
end.send(:compose_page_url, 1, **)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Create the calendar
|
65
|
+
def init(conf, period, query)
|
66
|
+
@conf = Marshal.load(Marshal.dump(conf)) # store a copy
|
67
|
+
@units = Calendar::UNITS & @conf.keys # get the units in time length desc order
|
68
|
+
@period = period
|
69
|
+
@query = query
|
70
|
+
@page_key = conf[:offset][:page_key] || DEFAULT[:page_key]
|
71
|
+
# set all the :page_key options for later deletion
|
72
|
+
@units.each { |unit| conf[unit][:page_key] = "#{unit}_#{@page_key}" }
|
73
|
+
calendar = {}
|
74
|
+
object = nil
|
75
|
+
@units.each_with_index do |unit, index|
|
76
|
+
params_to_delete = @units[(index + 1), @units.length].map { |sub| conf[sub][:page_key] } + [@page_key]
|
77
|
+
conf[unit][:querify] = ->(up) { up.except!(*params_to_delete.map(&:to_s)) }
|
78
|
+
conf[unit][:period] = object&.send(:active_period) || @period
|
79
|
+
conf[unit][:page] = @query["#{unit}_#{@page_key}"] # requested page
|
80
|
+
# :nocov:
|
81
|
+
# simplecov doesn't need to fail block_given?
|
82
|
+
conf[unit][:counts] = yield(unit, conf[unit][:period]) if block_given?
|
83
|
+
# :nocov:
|
84
|
+
calendar[unit] = object = create(unit, **conf[unit])
|
85
|
+
end
|
86
|
+
[replace(calendar), object.from, object.to]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Create a unit subclass instance by using the unit name (internal use)
|
90
|
+
def create(unit, **)
|
91
|
+
raise InternalError, "unit must be in #{UNITS.inspect}; got #{unit}" unless UNITS.include?(unit)
|
92
|
+
|
93
|
+
name = +unit.to_s
|
94
|
+
name[0] = name[0].capitalize
|
95
|
+
Pagy::Calendar.const_get(name).new(**, request: @conf[:request])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -1,19 +1,16 @@
|
|
1
|
-
# See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
|
-
class Pagy
|
5
|
-
class Calendar
|
6
|
-
# Day unit subclass
|
3
|
+
class Pagy
|
4
|
+
class Calendar
|
7
5
|
class Day < Unit
|
8
|
-
DEFAULT = {
|
9
|
-
|
10
|
-
order:
|
11
|
-
format:
|
6
|
+
DEFAULT = { slots: 31,
|
7
|
+
compact: true,
|
8
|
+
order: :asc,
|
9
|
+
format: '%d' }.freeze
|
12
10
|
|
13
11
|
protected
|
14
12
|
|
15
|
-
|
16
|
-
def assign_unit_vars
|
13
|
+
def assign_unit_variables
|
17
14
|
super
|
18
15
|
@initial = @starting.beginning_of_day
|
19
16
|
@final = @ending.tomorrow.beginning_of_day
|
@@ -22,7 +19,6 @@ class Pagy # :nodoc:
|
|
22
19
|
@to = @from.tomorrow
|
23
20
|
end
|
24
21
|
|
25
|
-
# Starting time for the page
|
26
22
|
def starting_time_for(page)
|
27
23
|
@initial.days_since(time_offset_for(page))
|
28
24
|
end
|
@@ -1,19 +1,16 @@
|
|
1
|
-
# See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
class Pagy # :nodoc:
|
5
4
|
class Calendar # :nodoc:
|
6
|
-
# Month unit subclass
|
7
5
|
class Month < Unit
|
8
|
-
DEFAULT = {
|
9
|
-
|
10
|
-
order:
|
11
|
-
format:
|
6
|
+
DEFAULT = { slots: 12,
|
7
|
+
compact: true,
|
8
|
+
order: :asc,
|
9
|
+
format: '%b' }.freeze
|
12
10
|
|
13
11
|
protected
|
14
12
|
|
15
|
-
|
16
|
-
def assign_unit_vars
|
13
|
+
def assign_unit_variables
|
17
14
|
super
|
18
15
|
@initial = @starting.beginning_of_month
|
19
16
|
@final = @ending.next_month.beginning_of_month
|
@@ -22,7 +19,6 @@ class Pagy # :nodoc:
|
|
22
19
|
@to = @from.next_month
|
23
20
|
end
|
24
21
|
|
25
|
-
# Starting time for the page
|
26
22
|
def starting_time_for(page)
|
27
23
|
@initial.months_since(time_offset_for(page))
|
28
24
|
end
|
@@ -33,7 +29,6 @@ class Pagy # :nodoc:
|
|
33
29
|
|
34
30
|
private
|
35
31
|
|
36
|
-
# Number of months in time
|
37
32
|
def months_in(time)
|
38
33
|
(time.year * 12) + time.month
|
39
34
|
end
|
@@ -1,26 +1,23 @@
|
|
1
|
-
# See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
class Pagy # :nodoc:
|
5
4
|
class Calendar # :nodoc:
|
6
|
-
# Quarter unit subclass
|
7
5
|
class Quarter < Unit
|
8
|
-
DEFAULT = {
|
9
|
-
|
10
|
-
order:
|
11
|
-
format:
|
6
|
+
DEFAULT = { slots: 4,
|
7
|
+
compact: true,
|
8
|
+
order: :asc,
|
9
|
+
format: 'Q%q' }.freeze # '%q' token
|
10
|
+
|
11
|
+
protected
|
12
12
|
|
13
13
|
# The label for any page, with the substitution of the '%q' token
|
14
|
-
def
|
14
|
+
def page_label(page, **options)
|
15
15
|
starting_time = starting_time_for(page.to_i) # page could be a string
|
16
|
-
|
17
|
-
localize(starting_time,
|
16
|
+
options[:format] = (options[:format] || @options[:format]).gsub('%q') { (starting_time.month / 3.0).ceil }
|
17
|
+
localize(starting_time, **options)
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
# Set up the calendar variables
|
23
|
-
def assign_unit_vars
|
20
|
+
def assign_unit_variables
|
24
21
|
super
|
25
22
|
@initial = @starting.beginning_of_quarter
|
26
23
|
@final = @ending.next_quarter.beginning_of_quarter
|
@@ -29,7 +26,6 @@ class Pagy # :nodoc:
|
|
29
26
|
@to = @from.next_quarter
|
30
27
|
end
|
31
28
|
|
32
|
-
# Starting time for the page
|
33
29
|
def starting_time_for(page)
|
34
30
|
@initial.months_since(time_offset_for(page) * 3)
|
35
31
|
end
|
@@ -40,7 +36,6 @@ class Pagy # :nodoc:
|
|
40
36
|
|
41
37
|
private
|
42
38
|
|
43
|
-
# Number of months in time
|
44
39
|
def months_in(time)
|
45
40
|
(time.year * 12) + time.month
|
46
41
|
end
|
@@ -0,0 +1,92 @@
|
|
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 durtion 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
|
+
|
20
|
+
def initialize(**) # rubocop:disable Lint/MissingSuper
|
21
|
+
assign_options(**)
|
22
|
+
assign_and_check(page: 1)
|
23
|
+
assign_unit_variables
|
24
|
+
return unless in_range? { @page <= @last }
|
25
|
+
|
26
|
+
assign_previous_and_next
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :order, :from, :to, :previous, :last
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def calendar? = true
|
34
|
+
|
35
|
+
# Called by false in_range?
|
36
|
+
def assign_empty_page_variables
|
37
|
+
@in = @from = @to = 0 # options relative to the actual page
|
38
|
+
edge = @order == :asc ? @final : @initial # get the edge of the range (neat, but any time would do)
|
39
|
+
@from = @to = edge # set both to the edge time (a >=&&< query will get no records)
|
40
|
+
@previous = @last
|
41
|
+
end
|
42
|
+
|
43
|
+
# The page that includes time
|
44
|
+
# In case of time out of range, the :fit_time option avoids the RangeError
|
45
|
+
# and returns the closest page to the passed time argument (first or last page)
|
46
|
+
def page_at(time, **options)
|
47
|
+
fit_time = time
|
48
|
+
fit_final = @final - 1
|
49
|
+
unless time.between?(@initial, fit_final)
|
50
|
+
raise RangeError.new(self, :time, "between #{@initial} and #{fit_final}", time) unless options[:fit_time]
|
51
|
+
|
52
|
+
if time < @final
|
53
|
+
fit_time = @initial
|
54
|
+
ordinal = 'first'
|
55
|
+
else
|
56
|
+
fit_time = fit_final
|
57
|
+
ordinal = 'last'
|
58
|
+
end
|
59
|
+
warn "Pagy::Calendar#page_at: Rescued #{time} out of range by returning the #{ordinal} page."
|
60
|
+
end
|
61
|
+
offset = page_offset_at(fit_time) # offset starts from 0
|
62
|
+
@order == :asc ? offset + 1 : @last - offset
|
63
|
+
end
|
64
|
+
|
65
|
+
# Base class method for the setup of the unit variables (subclasses must implement it and call super)
|
66
|
+
def assign_unit_variables
|
67
|
+
@order = @options[:order]
|
68
|
+
@starting, @ending = @options[:period]
|
69
|
+
raise OptionError.new(self, :period, 'to be a an Array of min and max TimeWithZone instances', @options[:period]) \
|
70
|
+
unless @starting.is_a?(ActiveSupport::TimeWithZone) \
|
71
|
+
&& @ending.is_a?(ActiveSupport::TimeWithZone) && @starting <= @ending
|
72
|
+
end
|
73
|
+
|
74
|
+
# Apply the strftime format to the time.
|
75
|
+
# Localization other than :en, require the rails-I18n gem.
|
76
|
+
def localize(time, **options)
|
77
|
+
time.strftime(options[:format])
|
78
|
+
end
|
79
|
+
|
80
|
+
# The number of time units to offset from the @initial time, in order to get the ordered starting time for the page.
|
81
|
+
# Used in starting_time_for(page) where page starts from 1 (e.g. page to starting_time means subtracting 1)
|
82
|
+
def time_offset_for(page)
|
83
|
+
@order == :asc ? page - 1 : @last - page
|
84
|
+
end
|
85
|
+
|
86
|
+
# Period of the active page (used internally for nested units)
|
87
|
+
def active_period
|
88
|
+
[[@starting, @from].max, [@to - 1, @ending].min] # -1 sec: include only last unit day
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -1,17 +1,14 @@
|
|
1
|
-
# See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
|
-
class Pagy
|
5
|
-
class Calendar
|
6
|
-
# Week unit subclass
|
3
|
+
class Pagy
|
4
|
+
class Calendar
|
7
5
|
class Week < Unit
|
8
|
-
DEFAULT = { order: :asc,
|
9
|
-
format: '%Y-%W' }
|
6
|
+
DEFAULT = { order: :asc,
|
7
|
+
format: '%Y-%W' }.freeze
|
10
8
|
|
11
9
|
protected
|
12
10
|
|
13
|
-
|
14
|
-
def assign_unit_vars
|
11
|
+
def assign_unit_variables
|
15
12
|
super
|
16
13
|
@initial = @starting.beginning_of_week
|
17
14
|
@final = @ending.next_week.beginning_of_week
|
@@ -20,7 +17,6 @@ class Pagy # :nodoc:
|
|
20
17
|
@to = @from.next_week
|
21
18
|
end
|
22
19
|
|
23
|
-
# Starting time for the page
|
24
20
|
def starting_time_for(page)
|
25
21
|
@initial.weeks_since(time_offset_for(page))
|
26
22
|
end
|
@@ -1,19 +1,18 @@
|
|
1
|
-
# See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/docs/api/calendar
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
class Pagy # :nodoc:
|
5
4
|
class Calendar # :nodoc:
|
6
5
|
# Year unit subclass
|
7
6
|
class Year < Unit
|
8
|
-
DEFAULT = {
|
9
|
-
|
10
|
-
order:
|
11
|
-
format:
|
7
|
+
DEFAULT = { slots: 10,
|
8
|
+
compact: true,
|
9
|
+
order: :asc,
|
10
|
+
format: '%Y' }.freeze
|
12
11
|
|
13
12
|
protected
|
14
13
|
|
15
14
|
# Set up the calendar variables
|
16
|
-
def
|
15
|
+
def assign_unit_variables
|
17
16
|
super
|
18
17
|
@initial = @starting.beginning_of_year
|
19
18
|
@final = @ending.next_year.beginning_of_year
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Pagy
|
4
|
+
# Generic option error
|
5
|
+
class OptionError < ArgumentError
|
6
|
+
attr_reader :pagy, :option, :value
|
7
|
+
|
8
|
+
# Set the options and prepare the message
|
9
|
+
def initialize(pagy, option, description, value)
|
10
|
+
@pagy = pagy
|
11
|
+
@option = option
|
12
|
+
@value = value
|
13
|
+
super("expected :#{@option} #{description}; got #{@value.inspect}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Specific range error
|
18
|
+
class RangeError < OptionError; end
|
19
|
+
|
20
|
+
# I18n localization error
|
21
|
+
class RailsI18nLoadError < LoadError; end
|
22
|
+
|
23
|
+
# Generic internal error
|
24
|
+
class InternalError < StandardError; end
|
25
|
+
|
26
|
+
# JsonApi :page param error
|
27
|
+
class JsonapiReservedParamError < StandardError
|
28
|
+
# Inform about the actual value
|
29
|
+
def initialize(value)
|
30
|
+
super("expected reserved :page param to be nil or Hash-like; got #{value.inspect}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
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
|
+
# Extract the keyset from the set
|
9
|
+
def extract_keyset
|
10
|
+
@set.order_values.each_with_object({}) do |node, keyset|
|
11
|
+
keyset[node.value.name.to_sym] = node.direction
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Get the keyset attributes from a record
|
16
|
+
def keyset_attributes_from(record) = record.slice(*@keyset.keys)
|
17
|
+
|
18
|
+
# Get the hash of quoted keyset identifiers
|
19
|
+
def quoted_identifiers(table)
|
20
|
+
connection = @set.connection
|
21
|
+
@keyset.to_h { |column| [column, "#{connection.quote_table_name(table)}.#{connection.quote_column_name(column)}"] }
|
22
|
+
end
|
23
|
+
|
24
|
+
# Typecast the attributes
|
25
|
+
def typecast(attributes)
|
26
|
+
@set.model.new(attributes).slice(attributes.keys)
|
27
|
+
.to_hash.transform_keys(&:to_sym)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Append the missing keyset keys, if the set is restricted by select
|
31
|
+
def ensure_select
|
32
|
+
return if @set.select_values.empty?
|
33
|
+
|
34
|
+
@set = @set.select(*@keyset.keys)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Apply the where predicate to the set
|
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,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Pagy
|
4
|
+
class Keyset
|
5
|
+
module Adapters
|
6
|
+
# Keyset adapter for sequel
|
7
|
+
module Sequel
|
8
|
+
# Extract the keyset from the set
|
9
|
+
def extract_keyset
|
10
|
+
return {} unless @set.opts[:order]
|
11
|
+
|
12
|
+
@set.opts[:order].each_with_object({}) do |item, keyset|
|
13
|
+
case item
|
14
|
+
when Symbol
|
15
|
+
keyset[item] = :asc
|
16
|
+
when ::Sequel::SQL::OrderedExpression
|
17
|
+
keyset[item.expression] = item.descending ? :desc : :asc
|
18
|
+
else
|
19
|
+
raise TypeError, "#{item.class.inspect} is not a supported Sequel::SQL::OrderedExpression"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get the keyset attributes from a record
|
25
|
+
def keyset_attributes_from(record) = record.to_hash.slice(*@keyset.keys)
|
26
|
+
|
27
|
+
# Get the hash of quoted keyset identifiers
|
28
|
+
def quoted_identifiers(table)
|
29
|
+
db = @set.db
|
30
|
+
@keyset.to_h { |column| [column, "#{db.quote_identifier(table)}.#{db.quote_identifier(column)}"] }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Typecast the attributes
|
34
|
+
def typecast(attributes)
|
35
|
+
model = @set.opts[:model]
|
36
|
+
model.unrestrict_primary_key if (primary_key_is_restricted = model.restrict_primary_key?)
|
37
|
+
attributes = model.new(attributes).to_hash.slice(*attributes.keys.map(&:to_sym))
|
38
|
+
model.restrict_primary_key if primary_key_is_restricted
|
39
|
+
attributes
|
40
|
+
end
|
41
|
+
|
42
|
+
# Append the missing keyset keys, if the set is restricted by select
|
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
|
+
# Apply the where predicate to the set
|
51
|
+
def apply_where(predicate, arguments)
|
52
|
+
@set = @set.where(::Sequel.lit(predicate, **arguments))
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.included(including)
|
56
|
+
instance_methods(false).each do |method_name|
|
57
|
+
including.send(:protected, method_name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Pagy
|
4
|
+
class Keyset
|
5
|
+
# Use keyset pagination with resources for all the frontend helpers
|
6
|
+
class Keynav < Keyset
|
7
|
+
# Avoid conflicts between filter arguments in composite SQL fragments
|
8
|
+
PRIOR_PREFIX = 'prior_'
|
9
|
+
PAGE_PREFIX = 'page_'
|
10
|
+
|
11
|
+
path = Pathname.new(__FILE__).sub_ext('')
|
12
|
+
autoload :ActiveRecord, path.join('active_record')
|
13
|
+
autoload :Sequel, path.join('sequel')
|
14
|
+
|
15
|
+
# Finalize the instance variables needed for the UI
|
16
|
+
def initialize(set, **)
|
17
|
+
super
|
18
|
+
# Ensure next is called, so the last page used by the UI helpers is known
|
19
|
+
self.next
|
20
|
+
@previous = @page - 1 unless @page == 1
|
21
|
+
@in = @records.size
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :update, :previous, :last
|
25
|
+
|
26
|
+
# Prepare the @update for the client when it's a new page, and return the next page number
|
27
|
+
def next
|
28
|
+
records
|
29
|
+
return if !@more || (@options[:max_pages] && @page >= @options[:max_pages])
|
30
|
+
|
31
|
+
@next ||= begin
|
32
|
+
unless @page_cutoff
|
33
|
+
@page_cutoff = extract_cutoff
|
34
|
+
@last += 1 # reflect the added cutoff
|
35
|
+
@update.push(@last, [@page, 1, @page_cutoff]) # last + splice arguments for the client cutoffs
|
36
|
+
end
|
37
|
+
@page + 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def keynav? = true
|
44
|
+
|
45
|
+
# Process the page array
|
46
|
+
def assign_page
|
47
|
+
if @options[:page]
|
48
|
+
storage_key, @page, @last, @prior_cutoff, @page_cutoff = @options[:page]
|
49
|
+
else
|
50
|
+
@page = @last = 1
|
51
|
+
end
|
52
|
+
@update = [storage_key, @options[:page_key]]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Use a compound predicate to fetch the records
|
56
|
+
def fetch_records
|
57
|
+
return super unless @page_cutoff # last page
|
58
|
+
|
59
|
+
# Compound predicate for visited pages
|
60
|
+
predicate = +''
|
61
|
+
arguments = {}
|
62
|
+
if @prior_cutoff # not the first page
|
63
|
+
# Include the records after @prior_cutoff
|
64
|
+
predicate << "(#{compose_predicate(PRIOR_PREFIX)}) AND "
|
65
|
+
arguments.merge!(arguments_from(@prior_cutoff, PRIOR_PREFIX))
|
66
|
+
end
|
67
|
+
# Exclude the records after @page_cutoff
|
68
|
+
predicate << "NOT (#{compose_predicate(PAGE_PREFIX)})"
|
69
|
+
arguments.merge!(arguments_from(@page_cutoff, PAGE_PREFIX))
|
70
|
+
apply_where(predicate, arguments)
|
71
|
+
|
72
|
+
@more = true # not the last page
|
73
|
+
@set.limit(nil).to_a # replaced by the compound predicate
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|