pagy 9.4.0 → 43.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/apps/calendar.ru +566 -562
  4. data/apps/demo.ru +246 -182
  5. data/apps/enable_rails_page_segment.rb +56 -0
  6. data/apps/index.rb +4 -2
  7. data/apps/keynav+root_key.ru +316 -0
  8. data/apps/keynav.ru +255 -0
  9. data/apps/{keyset_ar.ru → keyset.ru} +33 -42
  10. data/apps/{keyset_s.ru → keyset_sequel.ru} +32 -40
  11. data/apps/rails.ru +57 -58
  12. data/apps/repro.ru +54 -51
  13. data/bin/pagy +2 -95
  14. data/config/pagy.rb +33 -207
  15. data/javascripts/ai_widget.js +90 -0
  16. data/javascripts/pagy.js +166 -0
  17. data/javascripts/pagy.min.js +2 -4
  18. data/javascripts/pagy.mjs +126 -67
  19. data/javascripts/wand.js +1172 -0
  20. data/lib/pagy/classes/calendar/calendar.rb +101 -0
  21. data/lib/pagy/{calendar → classes/calendar}/day.rb +7 -11
  22. data/lib/pagy/{calendar → classes/calendar}/month.rb +5 -10
  23. data/lib/pagy/{calendar → classes/calendar}/quarter.rb +10 -15
  24. data/lib/pagy/classes/calendar/unit.rb +93 -0
  25. data/lib/pagy/{calendar → classes/calendar}/week.rb +5 -9
  26. data/lib/pagy/{calendar → classes/calendar}/year.rb +5 -6
  27. data/lib/pagy/classes/exceptions.rb +26 -0
  28. data/lib/pagy/classes/keyset/adapters/active_record.rb +50 -0
  29. data/lib/pagy/classes/keyset/adapters/sequel.rb +62 -0
  30. data/lib/pagy/classes/keyset/keynav.rb +85 -0
  31. data/lib/pagy/classes/keyset/keyset.rb +155 -0
  32. data/lib/pagy/classes/offset/countish.rb +17 -0
  33. data/lib/pagy/classes/offset/countless.rb +63 -0
  34. data/lib/pagy/classes/offset/offset.rb +63 -0
  35. data/lib/pagy/classes/offset/search.rb +34 -0
  36. data/lib/pagy/classes/request.rb +48 -0
  37. data/lib/pagy/cli.rb +122 -0
  38. data/lib/pagy/console.rb +5 -20
  39. data/lib/pagy/deprecated.rb +84 -0
  40. data/lib/pagy/modules/abilities/configurable.rb +37 -0
  41. data/lib/pagy/modules/abilities/countable.rb +23 -0
  42. data/lib/pagy/modules/abilities/linkable.rb +72 -0
  43. data/lib/pagy/modules/abilities/rangeable.rb +14 -0
  44. data/lib/pagy/modules/abilities/shiftable.rb +12 -0
  45. data/lib/pagy/{b64.rb → modules/b64.rb} +4 -2
  46. data/lib/pagy/modules/console.rb +33 -0
  47. data/lib/pagy/modules/i18n/i18n.rb +72 -0
  48. data/lib/pagy/modules/i18n/p11n/arabic.rb +30 -0
  49. data/lib/pagy/modules/i18n/p11n/east_slavic.rb +27 -0
  50. data/lib/pagy/modules/i18n/p11n/one_other.rb +15 -0
  51. data/lib/pagy/modules/i18n/p11n/one_upto_two_other.rb +15 -0
  52. data/lib/pagy/modules/i18n/p11n/other.rb +13 -0
  53. data/lib/pagy/modules/i18n/p11n/polish.rb +27 -0
  54. data/lib/pagy/modules/i18n/p11n/west_slavic.rb +22 -0
  55. data/lib/pagy/modules/i18n/p11n.rb +16 -0
  56. data/lib/pagy/modules/searcher.rb +20 -0
  57. data/lib/pagy/next.rb +25 -0
  58. data/lib/pagy/tasks/sync.rb +20 -0
  59. data/lib/pagy/toolbox/helpers/anchor_tags.rb +19 -0
  60. data/lib/pagy/toolbox/helpers/bootstrap/input_nav_js.rb +28 -0
  61. data/lib/pagy/toolbox/helpers/bootstrap/previous_next_html.rb +19 -0
  62. data/lib/pagy/toolbox/helpers/bootstrap/series_nav.rb +32 -0
  63. data/lib/pagy/toolbox/helpers/bootstrap/series_nav_js.rb +24 -0
  64. data/lib/pagy/toolbox/helpers/bulma/input_nav_js.rb +25 -0
  65. data/lib/pagy/toolbox/helpers/bulma/previous_next_html.rb +20 -0
  66. data/lib/pagy/toolbox/helpers/bulma/series_nav.rb +31 -0
  67. data/lib/pagy/toolbox/helpers/bulma/series_nav_js.rb +23 -0
  68. data/lib/pagy/toolbox/helpers/data_hash.rb +29 -0
  69. data/lib/pagy/toolbox/helpers/headers_hash.rb +30 -0
  70. data/lib/pagy/toolbox/helpers/info_tag.rb +30 -0
  71. data/lib/pagy/toolbox/helpers/input_nav_js.rb +22 -0
  72. data/lib/pagy/toolbox/helpers/limit_tag_js.rb +25 -0
  73. data/lib/pagy/toolbox/helpers/loaders.rb +58 -0
  74. data/lib/pagy/toolbox/helpers/page_url.rb +16 -0
  75. data/lib/pagy/toolbox/helpers/series_nav.rb +30 -0
  76. data/lib/pagy/toolbox/helpers/series_nav_js.rb +20 -0
  77. data/lib/pagy/toolbox/helpers/support/a_lambda.rb +36 -0
  78. data/lib/pagy/toolbox/helpers/support/data_pagy_attribute.rb +19 -0
  79. data/lib/pagy/toolbox/helpers/support/nav_aria_label_attribute.rb +10 -0
  80. data/lib/pagy/toolbox/helpers/support/series.rb +37 -0
  81. data/lib/pagy/toolbox/helpers/support/wrap_input_nav_js.rb +18 -0
  82. data/lib/pagy/toolbox/helpers/support/wrap_series_nav.rb +15 -0
  83. data/lib/pagy/toolbox/helpers/support/wrap_series_nav_js.rb +38 -0
  84. data/lib/pagy/toolbox/helpers/urls_hash.rb +12 -0
  85. data/lib/pagy/toolbox/paginators/calendar.rb +34 -0
  86. data/lib/pagy/toolbox/paginators/countish.rb +38 -0
  87. data/lib/pagy/toolbox/paginators/countless.rb +22 -0
  88. data/lib/pagy/toolbox/paginators/elasticsearch_rails.rb +56 -0
  89. data/lib/pagy/toolbox/paginators/keynav_js.rb +26 -0
  90. data/lib/pagy/toolbox/paginators/keyset.rb +15 -0
  91. data/lib/pagy/toolbox/paginators/meilisearch.rb +34 -0
  92. data/lib/pagy/toolbox/paginators/method.rb +38 -0
  93. data/lib/pagy/toolbox/paginators/offset.rb +24 -0
  94. data/lib/pagy/toolbox/paginators/searchkick.rb +34 -0
  95. data/lib/pagy/toolbox/paginators/typesense_rails.rb +34 -0
  96. data/lib/pagy.rb +79 -89
  97. data/locales/ar.yml +9 -6
  98. data/locales/be.yml +9 -6
  99. data/locales/bg.yml +9 -6
  100. data/locales/bs.yml +9 -6
  101. data/locales/ca.yml +9 -6
  102. data/locales/ckb.yml +8 -6
  103. data/locales/cs.yml +9 -6
  104. data/locales/da.yml +9 -6
  105. data/locales/de.yml +9 -6
  106. data/locales/dz.yml +9 -6
  107. data/locales/en.yml +9 -6
  108. data/locales/es.yml +9 -6
  109. data/locales/fr.yml +9 -6
  110. data/locales/hr.yml +9 -6
  111. data/locales/hu.yml +24 -0
  112. data/locales/id.yml +10 -9
  113. data/locales/it.yml +9 -6
  114. data/locales/ja.yml +10 -9
  115. data/locales/km.yml +10 -9
  116. data/locales/ko.yml +9 -6
  117. data/locales/nb.yml +9 -6
  118. data/locales/nl.yml +9 -6
  119. data/locales/nn.yml +9 -6
  120. data/locales/pl.yml +9 -6
  121. data/locales/pt-BR.yml +10 -7
  122. data/locales/pt.yml +10 -7
  123. data/locales/ru.yml +9 -6
  124. data/locales/sk.yml +9 -6
  125. data/locales/sr.yml +9 -6
  126. data/locales/sv-SE.yml +9 -6
  127. data/locales/sv.yml +9 -6
  128. data/locales/sw.yml +12 -9
  129. data/locales/ta.yml +9 -6
  130. data/locales/tr.yml +17 -12
  131. data/locales/uk.yml +9 -6
  132. data/locales/vi.yml +9 -6
  133. data/locales/zh-CN.yml +9 -6
  134. data/locales/zh-HK.yml +9 -6
  135. data/locales/zh-TW.yml +9 -6
  136. data/stylesheets/pagy-tailwind.css +68 -0
  137. data/stylesheets/pagy.css +64 -27
  138. metadata +134 -53
  139. data/javascripts/pagy.min.js.map +0 -10
  140. data/lib/optimist.rb +0 -1022
  141. data/lib/pagy/backend.rb +0 -44
  142. data/lib/pagy/calendar/unit.rb +0 -103
  143. data/lib/pagy/calendar.rb +0 -84
  144. data/lib/pagy/countless.rb +0 -38
  145. data/lib/pagy/exceptions.rb +0 -25
  146. data/lib/pagy/extras/arel.rb +0 -28
  147. data/lib/pagy/extras/array.rb +0 -19
  148. data/lib/pagy/extras/bootstrap.rb +0 -97
  149. data/lib/pagy/extras/bulma.rb +0 -93
  150. data/lib/pagy/extras/calendar.rb +0 -79
  151. data/lib/pagy/extras/countless.rb +0 -32
  152. data/lib/pagy/extras/elasticsearch_rails.rb +0 -71
  153. data/lib/pagy/extras/gearbox.rb +0 -55
  154. data/lib/pagy/extras/headers.rb +0 -54
  155. data/lib/pagy/extras/i18n.rb +0 -26
  156. data/lib/pagy/extras/js_tools.rb +0 -70
  157. data/lib/pagy/extras/jsonapi.rb +0 -88
  158. data/lib/pagy/extras/keyset.rb +0 -30
  159. data/lib/pagy/extras/limit.rb +0 -63
  160. data/lib/pagy/extras/meilisearch.rb +0 -57
  161. data/lib/pagy/extras/metadata.rb +0 -42
  162. data/lib/pagy/extras/overflow.rb +0 -81
  163. data/lib/pagy/extras/pagy.rb +0 -82
  164. data/lib/pagy/extras/searchkick.rb +0 -59
  165. data/lib/pagy/extras/size.rb +0 -40
  166. data/lib/pagy/extras/standalone.rb +0 -60
  167. data/lib/pagy/extras/trim.rb +0 -29
  168. data/lib/pagy/frontend.rb +0 -99
  169. data/lib/pagy/i18n.rb +0 -167
  170. data/lib/pagy/keyset/active_record.rb +0 -44
  171. data/lib/pagy/keyset/sequel.rb +0 -57
  172. data/lib/pagy/keyset.rb +0 -118
  173. data/lib/pagy/shared_methods.rb +0 -26
  174. data/lib/pagy/url_helpers.rb +0 -26
  175. data/stylesheets/pagy.scss +0 -48
  176. data/stylesheets/pagy.tailwind.css +0 -21
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ################# IMPORTANT WARNING #################
4
+ # This setup forces Pagy to use the Rails `url_for` method,
5
+ # which is significantly slower (~20x) than Pagy's native URL generation.
6
+
7
+ # Use this file ONLY if you absolutely want to support the page param as a dynamic segment.
8
+ # (e.g. get '/comments(/:page)', to: 'comments#index').
9
+ # #####################################################
10
+
11
+ # USAGE
12
+
13
+ # initializers/pagy.rb
14
+ # require Pagy::ROOT.join('apps/enable_rails_page_segment.rb')
15
+
16
+ # config/routes.rb (example)
17
+ # get '/comments(/:page)', to: 'comments#index'
18
+
19
+ # Require the pagy sources to override
20
+ require_relative '../lib/pagy/toolbox/paginators/method'
21
+ require_relative '../lib/pagy/modules/abilities/linkable'
22
+
23
+ class Pagy
24
+ # Use plain Strings tokens instead of Pagy::EscapedValue strings
25
+ remove_const(:PAGE_TOKEN)
26
+ PAGE_TOKEN = '___PAGY_PAGE___'
27
+
28
+ remove_const(:LIMIT_TOKEN)
29
+ LIMIT_TOKEN = '___PAGY_LIMIT___'
30
+
31
+ # Switch to the `request.params` to get access to rails-added path parameters
32
+ module RequestOverride
33
+ def get_params(request)
34
+ request.params
35
+ end
36
+ end
37
+ Request.prepend RequestOverride
38
+
39
+ # Inject the caller context into the Pagy instance
40
+ module MethodOverride
41
+ def pagy(...)
42
+ super.tap { _1[0].instance_variable_set(:@context, self) }
43
+ end
44
+ end
45
+ Method.prepend MethodOverride
46
+
47
+ # Compose the final URL using `url_for`.
48
+ module LinkableOverride
49
+ def compose_url(absolute, _path, params, fragment)
50
+ params[:anchor] = fragment if fragment
51
+ params[:only_path] = !absolute
52
+ @context.url_for(params)
53
+ end
54
+ end
55
+ Linkable.prepend LinkableOverride
56
+ end
data/apps/index.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # PagyApp module
3
+ # PagyApps module
4
4
  module PagyApps
5
5
  # Return the hash of app name/path
6
- INDEX = Dir[File.expand_path('./*.ru', __dir__)].to_h { |f| [File.basename(f, '.ru'), f] }.freeze
6
+ INDEX = Dir[File.expand_path('./*.ru', __dir__)].to_h do |f|
7
+ [File.basename(f, '.ru'), f]
8
+ end.freeze
7
9
  end
@@ -0,0 +1,316 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DESCRIPTION
4
+ # Showcase the Keynav pagination with independent instances
5
+ #
6
+ # DOC
7
+ # https://ddnexus.github.io/pagy/playground/#keyset-apps
8
+ #
9
+ # BIN HELP
10
+ # pagy -h
11
+ #
12
+ # DEV USAGE
13
+ # pagy clone keynav
14
+ # pagy ./keynav.ru
15
+ #
16
+ # URL
17
+ # http://127.0.0.1:8000
18
+
19
+ VERSION = '43.5.5'
20
+
21
+ if VERSION != Pagy::VERSION
22
+ Warning.warn("\n>>> WARNING! '#{File.basename(__FILE__)}-#{VERSION}' running with 'pagy-#{Pagy::VERSION}'! <<< \n\n")
23
+ end
24
+
25
+ # Bundle
26
+ require 'bundler/inline'
27
+ gemfile(!Pagy::ROOT.join('pagy.gemspec').exist?) do
28
+ source 'https://rubygems.org'
29
+ gem 'activerecord'
30
+ gem 'puma'
31
+ gem 'sinatra'
32
+ gem 'sqlite3'
33
+ end
34
+
35
+ # Sinatra setup
36
+ require 'sinatra/base'
37
+ # Sinatra application
38
+ class PagyKeynav < Sinatra::Base
39
+ include Pagy::Method
40
+
41
+ get('/javascripts/:file') do
42
+ format = params[:file].split('.').last
43
+ if format == 'js'
44
+ content_type 'application/javascript'
45
+ elsif format == 'map'
46
+ content_type 'application/json'
47
+ end
48
+ send_file Pagy::ROOT.join('javascripts', params[:file])
49
+ end
50
+
51
+ # Root route/action
52
+ get '/' do
53
+ Time.zone = 'UTC'
54
+
55
+ @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc }.freeze
56
+ @pagy1, @pets1 = pagy(:keynav_js, Pet.order(@order), limit: 4, root_key: 'animal1')
57
+ @ids1 = @pets1.pluck(:id)
58
+ @pagy2, @pets2 = pagy(:keynav_js, Pet.order(@order), limit: 4, root_key: 'animal2')
59
+ @ids2 = @pets2.pluck(:id)
60
+ erb :main
61
+ end
62
+
63
+ helpers do
64
+ def order_symbol(dir)
65
+ { asc: '&#x2197;', desc: '&#x2198;' }[dir]
66
+ end
67
+ end
68
+
69
+ # Views
70
+ template :layout do
71
+ <<~ERB
72
+ <!DOCTYPE html>
73
+ <html lang="en">
74
+ <head>
75
+ <title>Pagy Keynav (root_key) App</title>
76
+ <script src="javascripts/pagy.js"></script>
77
+ <script>
78
+ window.addEventListener("load", Pagy.init);
79
+ </script>
80
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
81
+ <style type="text/css">
82
+ @media screen { html, body {
83
+ font-size: 1rem;
84
+ line-height: 1.2;
85
+ padding: 0;
86
+ margin: 0;
87
+ } }
88
+ body {
89
+ background: white !important;
90
+ margin: 0 !important;
91
+ font-family: sans-serif !important;
92
+ }
93
+ .main-content {
94
+ padding: 1rem 1.5rem 2rem !important;
95
+ }
96
+ #content {
97
+ display: flex; /* Enables Flexbox */
98
+ flex-wrap: wrap; /* Allows stacking */
99
+ gap: 20px; /* Gap between items */
100
+ /* Just for visual clarity of the container boundaries */
101
+ /*padding: 10px;
102
+ background-color: #e0e7ff;
103
+ border-radius: 8px;
104
+ border: 2px dashed #6366f1; */
105
+ }
106
+ .box {
107
+ flex: 1; /* Grow to fill space */
108
+ min-width: 300px; /* Stack if space < 300px */
109
+ /* Visual Styling */
110
+ background-color: white;
111
+ padding: 25px;
112
+ border-radius: 6px;
113
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
114
+ border: 1px solid #ddd;
115
+ }
116
+ .pagy {
117
+ padding: .5em;
118
+ margin: .3em 0;
119
+ width: fit-content;
120
+ box-shadow: 5px 5px 10px 0px rgba(0,0,0,0.2);
121
+ }
122
+ <%= Pagy::ROOT.join('stylesheets/pagy.css').read %>
123
+ </style>
124
+ </head>
125
+ <body>
126
+ <%= yield %>
127
+ </body>
128
+ </html>
129
+ ERB
130
+ end
131
+
132
+ template :main do
133
+ <<~ERB
134
+ <div class="main-content">
135
+ <h1>Pagy Keynav App</h1>
136
+ <p>Self-contained, standalone app usable to easily reproduce any Keynav related pagy issue
137
+ with ActiveRecord sets.</p>
138
+ <p>The panels below show how to use the <code>:root_key</code> option for independent instances in the same request.</p>
139
+
140
+ <p>Notice that Keynav works also with Sequel sets.</p>
141
+
142
+ <h2>Versions</h2>
143
+ <ul>
144
+ <li>Ruby: <%= RUBY_VERSION %></li>
145
+ <li>Rack: <%= Rack::RELEASE %></li>
146
+ <li>Sinatra: <%= Sinatra::VERSION %></li>
147
+ <li>Pagy: <%= Pagy::VERSION %></li>
148
+ </ul>
149
+
150
+ <div id="content">
151
+ <div class="box">
152
+ <h3>Collection 1</h3>
153
+ <p id="records">@ids: <%= @ids1.join(',') %></p>
154
+ <div class="collection">
155
+ <table border="1" style="border-collapse: collapse; border-spacing: 0; padding: 0.2rem;">
156
+ <tr>
157
+ <th scope="col">animal <%= order_symbol(@order[:animal]) %></th>
158
+ <th scope="col">name <%= order_symbol(@order[:name]) %></th>
159
+ <th scope="col">birthdate <%= order_symbol(@order[:birthdate]) %></th>
160
+ <th scope="col">id <%= order_symbol(@order[:id]) %></th>
161
+ </tr>
162
+ <% @pets1.each do |pet| %>
163
+ <tr>
164
+ <td><%= pet.animal %></td>
165
+ <td><%= pet.name %></td>
166
+ <td><%= pet.birthdate %></td>
167
+ <td><%= pet.id %></td>
168
+ </tr>
169
+ <% end %>
170
+ </table>
171
+ </div>
172
+
173
+ <h4>@pagy.series_nav</h4>
174
+ <%= @pagy1.series_nav(id: 'series-nav',
175
+ aria_label: 'Pages (nav)') %>
176
+
177
+ <h4>@pagy.series_nav_js (responsive)</h4>
178
+ <%= @pagy1.series_nav_js(id: 'series-nav-js-responsive',
179
+ aria_label: 'Pages (nav_js_responsive)',
180
+ steps: { 0 => 5, 500 => 7, 750 => 9, 1000 => 11 }) %>
181
+ <h4>@pagy.input_nav_js</h4>
182
+ <%= @pagy1.input_nav_js(id: 'input-nav-js',
183
+ aria_label: 'Pages (input_nav_js)') %>
184
+
185
+ <h4>@pagy.info_tag</h4>
186
+ <%= @pagy1.info_tag(id: 'pagy-info') %>
187
+ </div>
188
+
189
+ <div class="box">
190
+ <h3>Collection 2</h3>
191
+ <p id="records">@ids: <%= @ids2.join(',') %></p>
192
+ <div class="collection">
193
+ <table border="1" style="border-collapse: collapse; border-spacing: 0; padding: 0.2rem;">
194
+ <tr>
195
+ <th scope="col">animal <%= order_symbol(@order[:animal]) %></th>
196
+ <th scope="col">name <%= order_symbol(@order[:name]) %></th>
197
+ <th scope="col">birthdate <%= order_symbol(@order[:birthdate]) %></th>
198
+ <th scope="col">id <%= order_symbol(@order[:id]) %></th>
199
+ </tr>
200
+ <% @pets2.each do |pet| %>
201
+ <tr>
202
+ <td><%= pet.animal %></td>
203
+ <td><%= pet.name %></td>
204
+ <td><%= pet.birthdate %></td>
205
+ <td><%= pet.id %></td>
206
+ </tr>
207
+ <% end %>
208
+ </table>
209
+ </div>
210
+
211
+ <h4>@pagy.series_nav</h4>
212
+ <%= @pagy2.series_nav(id: 'series-nav',
213
+ aria_label: 'Pages (nav)') %>
214
+
215
+ <h4>@pagy.series_nav_js (responsive)</h4>
216
+ <%= @pagy2.series_nav_js(id: 'series-nav-js-responsive',
217
+ aria_label: 'Pages (nav_js_responsive)',
218
+ steps: { 0 => 5, 500 => 7, 750 => 9, 1000 => 11 }) %>
219
+ <h4>@pagy.input_nav_js</h4>
220
+ <%= @pagy2.input_nav_js(id: 'input-nav-js',
221
+ aria_label: 'Pages (input_nav_js)') %>
222
+
223
+ <h4>@pagy.info_tag</h4>
224
+ <%= @pagy2.info_tag(id: 'pagy-info') %>
225
+ </div>
226
+ </div>
227
+ </div>
228
+ ERB
229
+ end
230
+ end
231
+
232
+ # ActiveRecord setup
233
+ require 'active_record'
234
+
235
+ # Match the microsecods with the strings stored into the time columns of SQLite
236
+ # ActiveSupport::JSON::Encoding.time_precision = 6
237
+
238
+ # Log
239
+ output = ENV['APP_ENV'].equal?('showcase') ? IO::NULL : $stdout
240
+ ActiveRecord::Base.logger = Logger.new(output)
241
+ # Connection
242
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'file:memdb1?mode=memory&cache=shared')
243
+ # Schema
244
+ ActiveRecord::Schema.define do
245
+ create_table :pets, force: true do |t|
246
+ t.string :animal
247
+ t.string :name
248
+ t.date :birthdate
249
+ end
250
+ end
251
+
252
+ # Models
253
+ class Pet < ActiveRecord::Base; end
254
+
255
+ data = <<~DATA
256
+ Luna | dog | 2018-03-10
257
+ Coco | cat | 2019-05-15
258
+ Dodo | dog | 2020-06-25
259
+ Wiki | bird | 2018-03-12
260
+ Baby | rabbit | 2020-01-13
261
+ Neki | horse | 2021-07-20
262
+ Tino | donkey | 2019-06-18
263
+ Plot | cat | 2022-09-21
264
+ Riki | cat | 2018-09-14
265
+ Susi | horse | 2018-10-26
266
+ Coco | pig | 2020-08-29
267
+ Momo | bird | 2023-08-25
268
+ Lili | cat | 2021-07-22
269
+ Beli | pig | 2020-07-26
270
+ Rocky | bird | 2022-08-19
271
+ Vyvy | dog | 2018-05-16
272
+ Susi | horse | 2024-01-25
273
+ Ella | cat | 2020-02-20
274
+ Rocky | dog | 2019-09-19
275
+ Juni | rabbit | 2020-08-24
276
+ Coco | bird | 2021-03-17
277
+ Susi | dog | 2021-07-28
278
+ Luna | horse | 2023-05-14
279
+ Gigi | pig | 2022-05-19
280
+ Coco | cat | 2020-02-20
281
+ Nino | donkey | 2019-06-17
282
+ Luna | cat | 2022-02-09
283
+ Popi | dog | 2020-09-26
284
+ Lili | pig | 2022-06-18
285
+ Mina | horse | 2021-04-21
286
+ Susi | rabbit | 2023-05-18
287
+ Toni | donkey | 2018-06-22
288
+ Rocky | horse | 2019-09-28
289
+ Lili | cat | 2019-03-18
290
+ Roby | cat | 2022-06-19
291
+ Anto | horse | 2022-08-18
292
+ Susi | pig | 2021-04-21
293
+ Boly | bird | 2020-03-29
294
+ Sky | cat | 2023-07-19
295
+ Lili | dog | 2020-01-28
296
+ Fami | snake | 2023-04-27
297
+ Lopi | pig | 2019-06-19
298
+ Rocky | snake | 2022-03-13
299
+ Denis | dog | 2022-06-19
300
+ Maca | cat | 2022-06-19
301
+ Luna | dog | 2022-08-15
302
+ Jeme | horse | 2019-08-08
303
+ Sary | bird | 2023-04-29
304
+ Rocky | bird | 2023-05-14
305
+ Coco | dog | 2023-05-27
306
+ DATA
307
+
308
+ # DB seed
309
+ pets = []
310
+ data.each_line(chomp: true) do |pet|
311
+ name, animal, birthdate = pet.split('|').map(&:strip)
312
+ pets << { name:, animal:, birthdate: }
313
+ end
314
+ Pet.insert_all(pets)
315
+
316
+ run PagyKeynav
data/apps/keynav.ru ADDED
@@ -0,0 +1,255 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DESCRIPTION
4
+ # Showcase the Keynav pagination (ActiveRecord example)
5
+ #
6
+ # DOC
7
+ # https://ddnexus.github.io/pagy/playground/#keyset-apps
8
+ #
9
+ # BIN HELP
10
+ # pagy -h
11
+ #
12
+ # DEV USAGE
13
+ # pagy clone keynav
14
+ # pagy ./keynav.ru
15
+ #
16
+ # URL
17
+ # http://127.0.0.1:8000
18
+
19
+ VERSION = '43.5.5'
20
+
21
+ if VERSION != Pagy::VERSION
22
+ Warning.warn("\n>>> WARNING! '#{File.basename(__FILE__)}-#{VERSION}' running with 'pagy-#{Pagy::VERSION}'! <<< \n\n")
23
+ end
24
+
25
+ # Bundle
26
+ require 'bundler/inline'
27
+ gemfile(!Pagy::ROOT.join('pagy.gemspec').exist?) do
28
+ source 'https://rubygems.org'
29
+ gem 'activerecord'
30
+ gem 'puma'
31
+ gem 'sinatra'
32
+ gem 'sqlite3'
33
+ end
34
+
35
+ # Sinatra setup
36
+ require 'sinatra/base'
37
+ # Sinatra application
38
+ class PagyKeynav < Sinatra::Base
39
+ include Pagy::Method
40
+
41
+ get('/javascripts/:file') do
42
+ format = params[:file].split('.').last
43
+ if format == 'js'
44
+ content_type 'application/javascript'
45
+ elsif format == 'map'
46
+ content_type 'application/json'
47
+ end
48
+ send_file Pagy::ROOT.join('javascripts', params[:file])
49
+ end
50
+
51
+ # Root route/action
52
+ get '/' do
53
+ Time.zone = 'UTC'
54
+
55
+ @order = { animal: :asc, name: :asc, birthdate: :desc, id: :asc }.freeze
56
+ @pagy, @pets = pagy(:keynav_js, Pet.order(@order), limit: 4, max_limit: 100)
57
+ # Support also root_key for replacing url in javascript
58
+ # @pagy, @pets = pagy(:keynav_js, Pet.order(@order), limit: 4, max_limit: 100, root_key: 'animal')
59
+ @ids = @pets.pluck(:id)
60
+ erb :main
61
+ end
62
+
63
+ helpers do
64
+ def order_symbol(dir)
65
+ { asc: '&#x2197;', desc: '&#x2198;' }[dir]
66
+ end
67
+ end
68
+
69
+ # Views
70
+ template :layout do
71
+ <<~ERB
72
+ <!DOCTYPE html>
73
+ <html lang="en">
74
+ <head>
75
+ <title>Pagy Keynav App</title>
76
+ <script src="javascripts/pagy.js"></script>
77
+ <script>
78
+ window.addEventListener("load", Pagy.init);
79
+ </script>
80
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
81
+ <style type="text/css">
82
+ @media screen { html, body {
83
+ font-size: 1rem;
84
+ line-height: 1.2;
85
+ padding: 0;
86
+ margin: 0;
87
+ } }
88
+ body {
89
+ background: white !important;
90
+ margin: 0 !important;
91
+ font-family: sans-serif !important;
92
+ }
93
+ .main-content {
94
+ padding: 1rem 1.5rem 2rem !important;
95
+ }
96
+ .pagy {
97
+ padding: .5em;
98
+ margin: .3em 0;
99
+ width: fit-content;
100
+ box-shadow: 5px 5px 10px 0px rgba(0,0,0,0.2);
101
+ }
102
+
103
+
104
+ <%= Pagy::ROOT.join('stylesheets/pagy.css').read %>
105
+ </style>
106
+ </head>
107
+ <body>
108
+ <%= yield %>
109
+ </body>
110
+ </html>
111
+ ERB
112
+ end
113
+
114
+ template :main do
115
+ <<~ERB
116
+ <div class="main-content">
117
+ <h1>Pagy Keynav App</h1>
118
+ <p>Self-contained, standalone app usable to easily reproduce any Keynav related pagy issue
119
+ with ActiveRecord sets.</p>
120
+
121
+ <p>Notice that Keynav works also with Sequel sets.</p>
122
+
123
+ <h2>Versions</h2>
124
+ <ul>
125
+ <li>Ruby: <%= RUBY_VERSION %></li>
126
+ <li>Rack: <%= Rack::RELEASE %></li>
127
+ <li>Sinatra: <%= Sinatra::VERSION %></li>
128
+ <li>Pagy: <%= Pagy::VERSION %></li>
129
+ </ul>
130
+
131
+ <h3>Collection</h3>
132
+ <p id="records">@ids: <%= @ids.join(',') %></p>
133
+ <div class="collection">
134
+ <table border="1" style="border-collapse: collapse; border-spacing: 0; padding: 0.2rem;">
135
+ <tr>
136
+ <th scope="col">animal <%= order_symbol(@order[:animal]) %></th>
137
+ <th scope="col">name <%= order_symbol(@order[:name]) %></th>
138
+ <th scope="col">birthdate <%= order_symbol(@order[:birthdate]) %></th>
139
+ <th scope="col">id <%= order_symbol(@order[:id]) %></th>
140
+ </tr>
141
+ <% @pets.each do |pet| %>
142
+ <tr>
143
+ <td><%= pet.animal %></td>
144
+ <td><%= pet.name %></td>
145
+ <td><%= pet.birthdate %></td>
146
+ <td><%= pet.id %></td>
147
+ </tr>
148
+ <% end %>
149
+ </table>
150
+ </div>
151
+
152
+ <h4>@pagy.series_nav</h4>
153
+ <%= @pagy.series_nav(id: 'series-nav',
154
+ aria_label: 'Pages (nav)') %>
155
+
156
+ <h4>@pagy.series_nav_js (responsive)</h4>
157
+ <%= @pagy.series_nav_js(id: 'series-nav-js-responsive',
158
+ aria_label: 'Pages (nav_js_responsive)',
159
+ steps: { 0 => 5, 500 => 7, 750 => 9, 1000 => 11 }) %>
160
+ <h4>@pagy.input_nav_js</h4>
161
+ <%= @pagy.input_nav_js(id: 'input-nav-js',
162
+ aria_label: 'Pages (input_nav_js)') %>
163
+
164
+ <h4>@pagy.info_tag</h4>
165
+ <%= @pagy.info_tag(id: 'pagy-info') %>
166
+ </div>
167
+ ERB
168
+ end
169
+ end
170
+
171
+ # ActiveRecord setup
172
+ require 'active_record'
173
+
174
+ # Match the microsecods with the strings stored into the time columns of SQLite
175
+ # ActiveSupport::JSON::Encoding.time_precision = 6
176
+
177
+ # Log
178
+ output = ENV['APP_ENV'].equal?('showcase') ? IO::NULL : $stdout
179
+ ActiveRecord::Base.logger = Logger.new(output)
180
+ # Connection
181
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'file:memdb1?mode=memory&cache=shared')
182
+ # Schema
183
+ ActiveRecord::Schema.define do
184
+ create_table :pets, force: true do |t|
185
+ t.string :animal
186
+ t.string :name
187
+ t.date :birthdate
188
+ end
189
+ end
190
+
191
+ # Models
192
+ class Pet < ActiveRecord::Base; end
193
+
194
+ data = <<~DATA
195
+ Luna | dog | 2018-03-10
196
+ Coco | cat | 2019-05-15
197
+ Dodo | dog | 2020-06-25
198
+ Wiki | bird | 2018-03-12
199
+ Baby | rabbit | 2020-01-13
200
+ Neki | horse | 2021-07-20
201
+ Tino | donkey | 2019-06-18
202
+ Plot | cat | 2022-09-21
203
+ Riki | cat | 2018-09-14
204
+ Susi | horse | 2018-10-26
205
+ Coco | pig | 2020-08-29
206
+ Momo | bird | 2023-08-25
207
+ Lili | cat | 2021-07-22
208
+ Beli | pig | 2020-07-26
209
+ Rocky | bird | 2022-08-19
210
+ Vyvy | dog | 2018-05-16
211
+ Susi | horse | 2024-01-25
212
+ Ella | cat | 2020-02-20
213
+ Rocky | dog | 2019-09-19
214
+ Juni | rabbit | 2020-08-24
215
+ Coco | bird | 2021-03-17
216
+ Susi | dog | 2021-07-28
217
+ Luna | horse | 2023-05-14
218
+ Gigi | pig | 2022-05-19
219
+ Coco | cat | 2020-02-20
220
+ Nino | donkey | 2019-06-17
221
+ Luna | cat | 2022-02-09
222
+ Popi | dog | 2020-09-26
223
+ Lili | pig | 2022-06-18
224
+ Mina | horse | 2021-04-21
225
+ Susi | rabbit | 2023-05-18
226
+ Toni | donkey | 2018-06-22
227
+ Rocky | horse | 2019-09-28
228
+ Lili | cat | 2019-03-18
229
+ Roby | cat | 2022-06-19
230
+ Anto | horse | 2022-08-18
231
+ Susi | pig | 2021-04-21
232
+ Boly | bird | 2020-03-29
233
+ Sky | cat | 2023-07-19
234
+ Lili | dog | 2020-01-28
235
+ Fami | snake | 2023-04-27
236
+ Lopi | pig | 2019-06-19
237
+ Rocky | snake | 2022-03-13
238
+ Denis | dog | 2022-06-19
239
+ Maca | cat | 2022-06-19
240
+ Luna | dog | 2022-08-15
241
+ Jeme | horse | 2019-08-08
242
+ Sary | bird | 2023-04-29
243
+ Rocky | bird | 2023-05-14
244
+ Coco | dog | 2023-05-27
245
+ DATA
246
+
247
+ # DB seed
248
+ pets = []
249
+ data.each_line(chomp: true) do |pet|
250
+ name, animal, birthdate = pet.split('|').map(&:strip)
251
+ pets << { name:, animal:, birthdate: }
252
+ end
253
+ Pet.insert_all(pets)
254
+
255
+ run PagyKeynav