pagy 7.0.11 → 8.6.3

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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/apps/calendar.ru +745 -0
  3. data/apps/demo.ru +435 -0
  4. data/apps/rails.ru +212 -0
  5. data/apps/repro.ru +177 -0
  6. data/apps/tmp/calendar.sqlite3 +0 -0
  7. data/apps/tmp/calendar.sqlite3-shm +0 -0
  8. data/apps/tmp/calendar.sqlite3-wal +0 -0
  9. data/apps/tmp/local_secret.txt +1 -0
  10. data/apps/tmp/pagy-keyset.sqlite3 +0 -0
  11. data/apps/tmp/pagy-keyset.sqlite3-shm +0 -0
  12. data/apps/tmp/pagy-keyset.sqlite3-wal +0 -0
  13. data/bin/pagy +100 -0
  14. data/{lib/config → config}/pagy.rb +31 -73
  15. data/javascripts/pagy-module.js +100 -0
  16. data/javascripts/pagy.js +4 -0
  17. data/javascripts/pagy.min.js +4 -0
  18. data/javascripts/pagy.min.js.map +10 -0
  19. data/javascripts/pagy.mjs +100 -0
  20. data/lib/optimist.rb +1022 -0
  21. data/lib/pagy/backend.rb +8 -3
  22. data/lib/pagy/calendar/day.rb +4 -3
  23. data/lib/pagy/calendar/month.rb +4 -3
  24. data/lib/pagy/calendar/quarter.rb +4 -3
  25. data/lib/pagy/calendar/unit.rb +106 -0
  26. data/lib/pagy/calendar/week.rb +3 -3
  27. data/lib/pagy/calendar/year.rb +4 -3
  28. data/lib/pagy/calendar.rb +55 -99
  29. data/lib/pagy/console.rb +2 -2
  30. data/lib/pagy/countless.rb +15 -10
  31. data/lib/pagy/extras/arel.rb +1 -1
  32. data/lib/pagy/extras/array.rb +1 -1
  33. data/lib/pagy/extras/bootstrap.rb +52 -63
  34. data/lib/pagy/extras/bulma.rb +49 -64
  35. data/lib/pagy/extras/calendar.rb +35 -5
  36. data/lib/pagy/extras/countless.rb +2 -2
  37. data/lib/pagy/extras/foundation.rb +52 -62
  38. data/lib/pagy/extras/gearbox.rb +28 -27
  39. data/lib/pagy/extras/headers.rb +1 -1
  40. data/lib/pagy/extras/i18n.rb +1 -1
  41. data/lib/pagy/extras/items.rb +21 -18
  42. data/lib/pagy/extras/{frontend_helpers.rb → js_tools.rb} +9 -6
  43. data/lib/pagy/extras/jsonapi.rb +2 -2
  44. data/lib/pagy/extras/materialize.rb +56 -52
  45. data/lib/pagy/extras/metadata.rb +6 -2
  46. data/lib/pagy/extras/overflow.rb +5 -4
  47. data/lib/pagy/extras/pagy.rb +82 -0
  48. data/lib/pagy/extras/semantic.rb +50 -51
  49. data/lib/pagy/extras/size.rb +40 -0
  50. data/lib/pagy/extras/standalone.rb +2 -2
  51. data/lib/pagy/extras/trim.rb +12 -12
  52. data/lib/pagy/extras/uikit.rb +51 -50
  53. data/lib/pagy/frontend.rb +39 -53
  54. data/lib/pagy/url_helpers.rb +9 -10
  55. data/lib/pagy.rb +51 -82
  56. data/{lib/locales → locales}/ar.yml +10 -11
  57. data/{lib/locales → locales}/be.yml +5 -5
  58. data/{lib/locales → locales}/bg.yml +5 -5
  59. data/{lib/locales → locales}/bs.yml +5 -5
  60. data/locales/ca.yml +21 -0
  61. data/locales/ckb.yml +18 -0
  62. data/{lib/locales → locales}/cs.yml +5 -5
  63. data/locales/da.yml +21 -0
  64. data/{lib/locales → locales}/de.yml +5 -5
  65. data/{lib/locales → locales}/en.yml +5 -5
  66. data/{lib/locales → locales}/es.yml +3 -3
  67. data/{lib/locales → locales}/fr.yml +5 -5
  68. data/{lib/locales → locales}/hr.yml +5 -5
  69. data/{lib/locales → locales}/id.yml +5 -5
  70. data/{lib/locales → locales}/it.yml +5 -5
  71. data/{lib/locales → locales}/ja.yml +5 -5
  72. data/{lib/locales → locales}/km.yml +5 -5
  73. data/locales/ko.yml +17 -0
  74. data/{lib/locales → locales}/nb.yml +5 -5
  75. data/{lib/locales → locales}/nl.yml +5 -5
  76. data/{lib/locales → locales}/nn.yml +5 -5
  77. data/{lib/locales → locales}/pl.yml +5 -5
  78. data/{lib/locales → locales}/pt-BR.yml +3 -3
  79. data/{lib/locales → locales}/pt.yml +3 -3
  80. data/locales/ru.yml +25 -0
  81. data/{lib/locales → locales}/sr.yml +5 -5
  82. data/{lib/locales → locales}/sv-SE.yml +5 -5
  83. data/{lib/locales → locales}/sv.yml +5 -5
  84. data/{lib/locales → locales}/sw.yml +5 -5
  85. data/{lib/locales → locales}/ta.yml +5 -5
  86. data/{lib/locales → locales}/tr.yml +5 -5
  87. data/{lib/locales → locales}/uk.yml +5 -5
  88. data/locales/vi.yml +17 -0
  89. data/{lib/locales → locales}/zh-CN.yml +5 -5
  90. data/{lib/locales → locales}/zh-HK.yml +5 -5
  91. data/{lib/locales → locales}/zh-TW.yml +5 -5
  92. data/{lib/stylesheets → stylesheets}/pagy.css +19 -34
  93. data/{lib/stylesheets → stylesheets}/pagy.scss +17 -19
  94. data/stylesheets/pagy.tailwind.css +21 -0
  95. metadata +76 -53
  96. data/lib/javascripts/pagy-dev.js +0 -112
  97. data/lib/javascripts/pagy-module.js +0 -111
  98. data/lib/javascripts/pagy.js +0 -1
  99. data/lib/locales/ca.yml +0 -23
  100. data/lib/locales/ckb.yml +0 -18
  101. data/lib/locales/da.yml +0 -23
  102. data/lib/locales/ko.yml +0 -19
  103. data/lib/locales/ru.yml +0 -27
  104. data/lib/locales/vi.yml +0 -17
  105. data/lib/pagy/calendar/helper.rb +0 -65
  106. data/lib/pagy/extras/navs.rb +0 -51
  107. data/lib/pagy/extras/support.rb +0 -40
  108. data/lib/stylesheets/pagy.tailwind.scss +0 -24
  109. /data/{lib/javascripts/pagy-module.d.ts → javascripts/pagy.d.ts} +0 -0
data/apps/demo.ru ADDED
@@ -0,0 +1,435 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Interactive showcase for all the pagy helpers and CSS styles
4
+
5
+ # DEMO USAGE
6
+ # pagy demo
7
+
8
+ # DEV USAGE
9
+ # pagy clone demo
10
+ # pagy ./demo.ru
11
+
12
+ # URL
13
+ # http://0.0.0.0:8000
14
+
15
+ # HELP
16
+ # pagy -h
17
+
18
+ # DOC
19
+ # https://ddnexus.github.io/pagy/playground/#3-demo-app
20
+
21
+ VERSION = '8.6.3'
22
+
23
+ require 'bundler/inline'
24
+ require 'bundler'
25
+ Bundler.configure
26
+ gemfile(ENV['PAGY_INSTALL_BUNDLE'] == 'true') do
27
+ source 'https://rubygems.org'
28
+ gem 'oj'
29
+ gem 'puma'
30
+ gem 'rouge'
31
+ gem 'sinatra'
32
+ gem 'sinatra-contrib'
33
+ end
34
+
35
+ # pagy initializer
36
+ STYLES = { pagy: { extra: 'pagy', prefix: '', css_anchor: 'pagy-scss' },
37
+ bootstrap: {},
38
+ bulma: {},
39
+ tailwind: { extra: 'pagy', prefix: '', css_anchor: 'pagy-tailwind-css' } }.freeze
40
+
41
+ STYLES.each_key do |style|
42
+ require "pagy/extras/#{STYLES[style][:extra] || style}"
43
+ end
44
+ require 'pagy/extras/items'
45
+ require 'pagy/extras/trim'
46
+ Pagy::DEFAULT[:trim_extra] = false # opt-in trim
47
+
48
+ # sinatra setup
49
+ require 'sinatra/base'
50
+
51
+ # sinatra application
52
+ class PagyDemo < Sinatra::Base
53
+ configure do
54
+ enable :inline_templates
55
+ end
56
+
57
+ include Pagy::Backend
58
+
59
+ get '/' do
60
+ redirect '/pagy'
61
+ end
62
+
63
+ get '/template' do
64
+ collection = MockCollection.new
65
+ @pagy, @records = pagy(collection, trim_extra: params['trim'])
66
+
67
+ erb :template, locals: { pagy: @pagy, style: 'pagy' }
68
+ end
69
+
70
+ get('/javascripts/:file') do
71
+ format = params[:file].split('.').last
72
+ if format == 'js'
73
+ content_type 'application/javascript'
74
+ elsif format == 'map'
75
+ content_type 'application/json'
76
+ end
77
+ send_file Pagy.root.join('javascripts', params[:file])
78
+ end
79
+
80
+ get('/stylesheets/:file') do
81
+ content_type 'text/css'
82
+ send_file Pagy.root.join('stylesheets', params[:file])
83
+ end
84
+
85
+ # one route/action per style
86
+ STYLES.each_key do |style|
87
+ prefix = STYLES[style][:prefix] || "_#{style}"
88
+
89
+ get("/#{style}/?:trim?") do
90
+ collection = MockCollection.new
91
+ @pagy, @records = pagy(collection, trim_extra: params['trim'])
92
+
93
+ erb :helpers, locals: { style:, prefix: }
94
+ end
95
+ end
96
+
97
+ helpers do
98
+ include Pagy::Frontend
99
+
100
+ def style_menu
101
+ html = +%(<div id="style-menu"> )
102
+ STYLES.each_key { |style| html << %(<a href="/#{style}">#{style}</a>) }
103
+ html << %(<a href="/template">template</a>)
104
+ html << %(</div>)
105
+ end
106
+
107
+ def highlight(html, format: :html)
108
+ if format == :html
109
+ html = html.gsub(/>[\s]*</, '><').strip # template single line no spaces around/between tags
110
+ html = Formatter.new.format(html)
111
+ end
112
+ lexer = Rouge::Lexers::ERB.new
113
+ formatter = Rouge::Formatters::HTMLInline.new('monokai.sublime')
114
+ summary = { html: 'Served HTML (pretty formatted)', erb: 'ERB Template' }
115
+ %(<details><summary>#{summary[format]}</summary><pre>\n#{
116
+ formatter.format(lexer.lex(html))
117
+ }</pre></details>)
118
+ end
119
+ end
120
+ end
121
+
122
+ # Cheap pagy formatter for helpers output
123
+ class Formatter
124
+ INDENT = ' '
125
+ TEXT_SPACE = "\u00B7"
126
+ TEXT = /^([^<>]+)(.*)/
127
+ UNPAIRED = /^(<(input|link).*?>)(.*)/
128
+ PAIRED = %r{^(<(head|nav|div|span|p|a|b|label|ul|li).*?>)(.*?)(</\2>)(.*)}
129
+ WRAPPER = /<.*?>/
130
+ DATA_PAGY = /(data-pagy="([^"]+)")/
131
+
132
+ def initialize
133
+ @formatted = +''
134
+ end
135
+
136
+ def format(input, level = 0)
137
+ process(input, level)
138
+ @formatted
139
+ end
140
+
141
+ private
142
+
143
+ def process(input, level)
144
+ push = ->(content) { @formatted << ((INDENT * level) << content << "\n") }
145
+ rest = nil
146
+ if (match = input.match(TEXT))
147
+ text, rest = match.captures
148
+ push.(text.gsub(' ', TEXT_SPACE))
149
+ elsif (match = input.match(UNPAIRED))
150
+ tag, _name, rest = match.captures
151
+ push.(tag)
152
+ elsif (match = input.match(PAIRED))
153
+ tag_start, name, block, tag_end, rest = match.captures
154
+ ## Handle incomplete same-tag nesting
155
+ while block.scan(/<#{name}.*?>/).size > block.scan(tag_end).size
156
+ more, rest = rest.split(tag_end, 2)
157
+ block << tag_end << more
158
+ end
159
+ if (match = tag_start.match(DATA_PAGY))
160
+ search, data = match.captures
161
+ formatted = data.scan(/.{1,76}/).join("\n")
162
+ replace = %(\n#{INDENT * (level + 1)}data-pagy="#{formatted}")
163
+ tag_start.sub!(search, replace)
164
+ end
165
+ if block.match(WRAPPER)
166
+ push.(tag_start)
167
+ process(block, level + 1)
168
+ push.(tag_end)
169
+ else
170
+ push.(tag_start << block << tag_end)
171
+ end
172
+ end
173
+ process(rest, level) if rest
174
+ end
175
+ end
176
+
177
+ # Simple array-based collection that acts as a standard DB collection.
178
+ class MockCollection < Array
179
+ def initialize(arr = Array(1..1000))
180
+ super
181
+ @collection = clone
182
+ end
183
+
184
+ def offset(value)
185
+ @collection = self[value..]
186
+ self
187
+ end
188
+
189
+ def limit(value)
190
+ @collection[0, value]
191
+ end
192
+
193
+ def count(*)
194
+ size
195
+ end
196
+ end
197
+
198
+ run PagyDemo
199
+
200
+ # We store the template in a constant instead of writing it inline, so we can highlight it more easily
201
+ TEMPLATE = <<~ERB
202
+ <%# IMPORTANT: use '<%== ... ' instead of '<%= ... ' if you run this in rails %>
203
+
204
+ <%# The a variable below is set to a lambda that generates the a tag %>
205
+ <%# Usage: a_tag = a.(page_number, text, classes: nil, aria_label: nil) %>
206
+ <% a = pagy_anchor(pagy) %>
207
+ <nav class="pagy nav" aria-label="Pages">
208
+ <%# Previous page link %>
209
+ <% if pagy.prev %>
210
+ <%= a.(pagy.prev, '&lt;', aria_label: 'Previous') %>
211
+ <% else %>
212
+ <a role="link" aria-disabled="true" aria-label="Previous">&lt;</a>
213
+ <% end %>
214
+ <%# Page links (series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]) %>
215
+ <% pagy.series.each do |item| %>
216
+ <% if item.is_a?(Integer) %>
217
+ <%= a.(item) %>
218
+ <% elsif item.is_a?(String) %>
219
+ <a role="link" aria-disabled="true" aria-current="page" class="current"><%= item %></a>
220
+ <% elsif item == :gap %>
221
+ <a role="link" aria-disabled="true" class="gap">&hellip;</a>
222
+ <% end %>
223
+ <% end %>
224
+ <%# Next page link %>
225
+ <% if pagy.next %>
226
+ <%= a.(pagy.next, '&gt;', aria_label: 'Next') %>
227
+ <% else %>
228
+ <a role="link" aria-disabled="true" aria-label="Next">&lt;</a>
229
+ <% end %>
230
+ </nav>
231
+ ERB
232
+
233
+ __END__
234
+
235
+ @@ layout
236
+ <!DOCTYPE html>
237
+ <html lang="en">
238
+ <head>
239
+ <title>Pagy Demo App</title>
240
+ <script src="/javascripts/pagy.min.js"></script>
241
+ <script>
242
+ window.addEventListener("load", Pagy.init);
243
+ </script>
244
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
245
+ <%= erb :"#{style}_head" if defined?(style) %>
246
+ <style type="text/css">
247
+ @media screen { html, body {
248
+ font-size: 1rem;
249
+ line-height: 1.2s;
250
+ padding: 0;
251
+ margin: 0;
252
+ } }
253
+ body {
254
+ background: white !important;
255
+ margin: 0 !important;
256
+ font-family: sans-serif !important;
257
+ }
258
+ h1, h2 {
259
+ font-size: 1.8rem !important;
260
+ font-weight: 600 !important;
261
+ margin-top: 1rem !important;
262
+ margin-bottom: 0.7rem !important;
263
+ line-height: 1.5 !important;
264
+ color: rgb(90 90 90) !important;
265
+ }
266
+ h2 {
267
+ font-family: monospace;
268
+ font-size: .9rem !important;
269
+ margin-top: 1.6rem !important;
270
+ }
271
+ summary, .notes {
272
+ font-size: .8rem;
273
+ color: gray;
274
+ margin-top: .6rem;
275
+ font-style: italic;
276
+ cursor: pointer;
277
+ }
278
+ .notes {
279
+ font-family: sans-serif;
280
+ font-weight: normal;
281
+ }
282
+ .notes code{
283
+ background-color: #E8E8E8;
284
+ padding: 0 0.3rem;
285
+ font-style: normal;
286
+ border-radius: 3px;
287
+ }
288
+ .description {
289
+ margin: 1rem 0;
290
+ }
291
+ .description a {
292
+ color: blue;
293
+ text-decoration: underline;
294
+ }
295
+ pre, pre code {
296
+ display: block;
297
+ margin-top: .3rem;
298
+ margin-bottom: 1rem;
299
+ font-size: .8rem !important;
300
+ line-height: 1rem !important;
301
+ color: white;
302
+ background-color: rgb(30 30 30);
303
+ padding: 1rem;
304
+ overflow: auto;
305
+ }
306
+ .content {
307
+ padding: 0 1.5rem 2rem !important;
308
+ }
309
+
310
+ #style-menu {
311
+ flex;
312
+ font-family: sans-serif;
313
+ font-size: 1.1rem;
314
+ line-height: 1.5rem;
315
+ white-space: nowrap;
316
+ color: white;
317
+ background-color: gray;
318
+ padding: .2rem 1.5rem;
319
+ }
320
+ #style-menu > :not([hidden]) ~ :not([hidden]) {
321
+ --space-reverse: 0;
322
+ margin-right: calc(0.5rem * var(--space-reverse));
323
+ margin-left: calc(0.5rem * calc(1 - var(--space-reverse)));
324
+ }
325
+ #style-menu a {
326
+ color: inherit;
327
+ text-decoration: none;
328
+ }
329
+ /* Quick demo for overriding the element style attribute of certain pagy helpers
330
+ .pagy input[style] {
331
+ width: 5rem !important;
332
+ }
333
+ */
334
+ </style>
335
+ </head>
336
+ <body>
337
+ <!-- each different class used by each style -->
338
+ <%= style_menu %>
339
+ <div class="content">
340
+ <%= yield %>
341
+ </div>
342
+ </body>
343
+ </html>
344
+
345
+
346
+ @@ pagy_head
347
+ <!-- copy and paste the pagy style in order to edit it -->
348
+ <link rel="stylesheet" href="/stylesheets/pagy.css">
349
+
350
+ @@ bootstrap_head
351
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
352
+
353
+ @@ bulma_head
354
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
355
+
356
+ @@ tailwind_head
357
+ <script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio"></script>
358
+ <!-- copy and paste the pagy.tailwind style in order to edit it -->
359
+ <style type="text/tailwindcss">
360
+ <%= Pagy.root.join('stylesheets', 'pagy.tailwind.css').read %>
361
+ </style>
362
+
363
+
364
+ @@ helpers
365
+ <h1><%= style %></h1>
366
+ <% extra = STYLES[style][:extra] || "#{style}" %>
367
+ <% css_anchor = STYLES[style][:css_anchor] %>
368
+
369
+ <p class="description">See the <a href="http://ddnexus.github.io/pagy/docs/extras/<%= extra %>" target="blank"><%= extra %> extra</a>
370
+ documentation
371
+ <% if css_anchor %>
372
+ and the <a href="http://ddnexus.github.io/pagy/docs/api/stylesheets/#<%= css_anchor %>" target="blank"><%= css_anchor.gsub('-', '.') %></a>
373
+ <% end %>
374
+ for details</p>
375
+
376
+ <h2>Collection</h2>
377
+ <p id="records">@records: <%= @records.join(',') %></p>
378
+
379
+ <h2>pagy<%= prefix %>_nav <span class="notes">Simple nav <code>size: 5</code></span></h2>
380
+ <%= html = send(:"pagy#{prefix}_nav", @pagy, id: 'simple-nav', aria_label: 'Pages simple-nav', size: 5) %>
381
+ <%= highlight(html) %>
382
+
383
+ <h2>pagy<%= prefix %>_nav <span class="notes">Fast nav <code>size: 7</code></span></h2>
384
+ <%= html = send(:"pagy#{prefix}_nav", @pagy, id: 'nav', aria_label: 'Pages nav') %>
385
+ <%= highlight(html) %>
386
+
387
+ <h2>pagy<%= prefix %>_nav_js <span class="notes">Fast nav <code>size: 7</code></span></h2>
388
+ <%= html = send(:"pagy#{prefix}_nav_js", @pagy, id: 'nav-js', aria_label: 'Pages nav_js') %>
389
+ <%= highlight(html) %>
390
+
391
+ <h2>pagy<%= prefix %>_nav_js <span class="notes">Responsive nav <code>steps: {...}</code> (Resize the window to see)</span></h2>
392
+ <%= html = send(:"pagy#{prefix}_nav_js", @pagy, id: 'nav-js-responsive',
393
+ aria_label: 'Pages nav_js_responsive',
394
+ steps: { 0 => 5, 500 => 7, 750 => 9, 1000 => 11 }) %>
395
+ <%= highlight(html) %>
396
+
397
+ <h2>pagy<%= prefix %>_combo_nav_js</h2>
398
+ <%= html = send(:"pagy#{prefix}_combo_nav_js", @pagy, id: 'combo-nav-js', aria_label: 'Pages combo_nav_js') %>
399
+ <%= highlight(html) %>
400
+
401
+ <h2>pagy_info</h2>
402
+ <%= html = pagy_info(@pagy, id: 'pagy-info') %>
403
+ <%= highlight(html) %>
404
+
405
+ <% if style.match(/pagy|tailwind/) %>
406
+ <h2>pagy_items_selector_js</h2>
407
+ <%= html = pagy_items_selector_js(@pagy, id: 'items-selector-js') %>
408
+ <%= highlight(html) %>
409
+
410
+ <h2>pagy_prev_a / pagy_next_a</h2>
411
+ <%= html = '<nav class="pagy" id="prev-next" aria-label="Pagy prev-next">' << pagy_prev_a(@pagy) << pagy_next_a(@pagy) << '</nav>' %>
412
+ <%= highlight(html) %>
413
+
414
+ <h2>pagy_prev_link / pagy_next_link <span class="notes">Link not rendered<span></h2>
415
+ <% html = '<head>' << (pagy_prev_link(@pagy)||'') << (pagy_next_link(@pagy)||'') << '</head>' %>
416
+ <%= highlight(html) %>
417
+ <% end %>
418
+
419
+
420
+ @@ template
421
+ <h1>Pagy Template Demo</h1>
422
+
423
+ <p class="description">
424
+ See the <a href="https://ddnexus.github.io/pagy/docs/how-to/#using-your-pagination-templates">
425
+ Custom Templates</a> documentation.
426
+ </p>
427
+ <h2>Collection</h2>
428
+ <p id="records">@records: <%= @records.join(',') %></p>
429
+
430
+ <h2>Rendered ERB template</h2>
431
+
432
+ <%# We don't inline the template here, so we can highlight it more easily %>
433
+ <%= html = ERB.new(TEMPLATE).result(binding) %>
434
+ <%= highlight(TEMPLATE, format: :erb) %>
435
+ <%= highlight(html) %>
data/apps/rails.ru ADDED
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Starting point to reproduce rails related pagy issues
4
+
5
+ # DEV USAGE
6
+ # pagy clone rails
7
+ # pagy ./rails.ru
8
+
9
+ # URL
10
+ # http://0.0.0.0:8000
11
+
12
+ # HELP
13
+ # pagy -h
14
+
15
+ # DOC
16
+ # https://ddnexus.github.io/pagy/playground/#2-rails-app
17
+
18
+ VERSION = '8.6.3'
19
+
20
+ # Gemfile
21
+ require 'bundler/inline'
22
+ require 'bundler'
23
+ Bundler.configure
24
+ gemfile(ENV['PAGY_INSTALL_BUNDLE'] == 'true') do
25
+ source 'https://rubygems.org'
26
+ gem 'oj'
27
+ gem 'puma'
28
+ gem 'rails'
29
+ # activerecord/sqlite3_adapter.rb probably useless) constraint !!!
30
+ # https://github.com/rails/rails/blame/v7.1.3.4/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L14
31
+ gem 'sqlite3', '~> 1.4.0'
32
+ end
33
+
34
+ # require 'rails/all' # too much stuff
35
+ require 'action_controller/railtie'
36
+ require 'active_record'
37
+
38
+ OUTPUT = Rails.env.showcase? ? IO::NULL : $stdout
39
+
40
+ # Rails config
41
+ class PagyRails < Rails::Application # :nodoc:
42
+ config.root = __dir__
43
+ config.session_store :cookie_store, key: 'cookie_store_key'
44
+ Rails.application.credentials.secret_key_base = 'absolute_secret'
45
+
46
+ config.logger = Logger.new(OUTPUT)
47
+ Rails.logger = config.logger
48
+
49
+ routes.draw do
50
+ root to: 'comments#index'
51
+ get '/javascripts/:file', to: 'pagy#javascripts', file: /.*/
52
+ end
53
+ end
54
+
55
+ # AR config
56
+ dir = Rails.env.development? ? '.' : Dir.pwd # app dir in dev or pwd otherwise
57
+ unless File.writable?(dir)
58
+ warn "ERROR: directory #{dir.inspect} is not writable (the pagy-rails-app needs to create DB files)"
59
+ exit 1
60
+ end
61
+
62
+ # Pagy initializer
63
+ require 'pagy/extras/pagy'
64
+ require 'pagy/extras/items'
65
+ require 'pagy/extras/overflow'
66
+ Pagy::DEFAULT[:items] = 10
67
+ Pagy::DEFAULT[:overflow] = :empty_page
68
+ Pagy::DEFAULT.freeze
69
+
70
+ # Activerecord initializer
71
+ ActiveRecord::Base.logger = Logger.new(OUTPUT)
72
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: "#{dir}/tmp/pagy-rails.sqlite3")
73
+ ActiveRecord::Schema.define do
74
+ create_table :posts, force: true do |t|
75
+ t.string :title
76
+ end
77
+
78
+ create_table :comments, force: true do |t|
79
+ t.string :body
80
+ t.integer :post_id
81
+ end
82
+ end
83
+
84
+ # Models
85
+ class Post < ActiveRecord::Base # :nodoc:
86
+ has_many :comments
87
+ end # :nodoc:
88
+
89
+ class Comment < ActiveRecord::Base # :nodoc:
90
+ belongs_to :post
91
+ end # :nodoc:
92
+
93
+ # DB seed
94
+ 1.upto(11) do |pi|
95
+ Post.create(title: "Post #{pi + 1}")
96
+ end
97
+ Post.all.each_with_index do |post, pi|
98
+ 2.times { |ci| Comment.create(post:, body: "Comment #{ci + 1} to Post #{pi + 1}") }
99
+ end
100
+
101
+ # Helpers
102
+ module CommentsHelper
103
+ include Pagy::Frontend
104
+ end
105
+
106
+ # Controllers
107
+ class CommentsController < ActionController::Base # :nodoc:
108
+ include Rails.application.routes.url_helpers
109
+ include Pagy::Backend
110
+
111
+ def index
112
+ @pagy, @comments = pagy(Comment.all)
113
+ render inline: TEMPLATE
114
+ end
115
+ end
116
+
117
+ # You don't need this in real rails apps (see https://ddnexus.github.io/pagy/docs/api/javascript/setup/#2-configure)
118
+ class PagyController < ActionController::Base
119
+ def javascripts
120
+ format = params[:file].split('.').last
121
+ if format == 'js'
122
+ render js: Pagy.root.join('javascripts', params[:file]).read
123
+ elsif format == 'map'
124
+ render json: Pagy.root.join('javascripts', params[:file]).read
125
+ end
126
+ end
127
+ end
128
+
129
+ run PagyRails
130
+
131
+ TEMPLATE = <<~ERB
132
+ <!DOCTYPE html>
133
+ <html lang="en">
134
+ <html>
135
+ <head>
136
+ <title>Pagy Rails App</title>
137
+ <script src="/javascripts/pagy.min.js"></script>
138
+ <script>
139
+ window.addEventListener("load", Pagy.init);
140
+ </script>
141
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
142
+ <style type="text/css">
143
+ @media screen { html, body {
144
+ font-size: 1rem;
145
+ line-height: 1.2s;
146
+ padding: 0;
147
+ margin: 0;
148
+ } }
149
+ body {
150
+ background: white !important;
151
+ margin: 0 !important;
152
+ font-family: sans-serif !important;
153
+ }
154
+ .content {
155
+ padding: 1rem 1.5rem 2rem !important;
156
+ }
157
+
158
+ /* Quick demo for overriding the element style attribute of certain pagy helpers
159
+ .pagy input[style] {
160
+ width: 5rem !important;
161
+ }
162
+ */
163
+
164
+ /*
165
+ If you want to customize the style,
166
+ please replace the line below with the actual file content
167
+ */
168
+ <%== Pagy.root.join('stylesheets', 'pagy.css').read %>
169
+ </style>
170
+ </head>
171
+
172
+ <body>
173
+
174
+ <div class="content">
175
+ <h1>Pagy Rails App</h1>
176
+ <p> Self-contained, standalone Rails app usable to easily reproduce any rails related pagy issue.</p>
177
+ <p>Please, report the following versions in any new issue.</p>
178
+ <h2>Versions</h2>
179
+ <ul>
180
+ <li>Ruby: <%== RUBY_VERSION %></li>
181
+ <li>Rack: <%== Rack::RELEASE %></li>
182
+ <li>Rails: <%== Rails.version %></li>
183
+ <li>Pagy: <%== Pagy::VERSION %></li>
184
+ </ul>
185
+
186
+ <h3>Collection</h3>
187
+ <div id="records" class="collection">
188
+ <% @comments.each do |comment| %>
189
+ <p style="margin: 0;"><%= comment.body %></p>
190
+ <% end %>
191
+ </div>
192
+ <hr>
193
+
194
+ <h4>pagy_nav</h4>
195
+ <%== pagy_nav(@pagy, id: 'nav', aria_label: 'Pages nav') %>
196
+
197
+ <h4>pagy_nav_js</h4>
198
+ <%== pagy_nav_js(@pagy, id: 'nav-js', aria_label: 'Pages nav_js') %>
199
+
200
+ <h4>pagy_combo_nav_js</h4>
201
+ <%== pagy_combo_nav_js(@pagy, id: 'combo-nav-js', aria_label: 'Pages combo_nav_js') %>
202
+
203
+ <h4>pagy_items_selector_js</h4>
204
+ <%== pagy_items_selector_js(@pagy, id: 'items-selector-js') %>
205
+
206
+ <h4>pagy_info</h4>
207
+ <%== pagy_info(@pagy, id: 'pagy-info') %>
208
+ </div>
209
+
210
+ </body>
211
+ </html>
212
+ ERB