coffeebrew_jekyll_paginate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3ea621dece76bb070ca7414e7ae18bba2b01729d3e4be3a58f16dfd040e091bf
4
+ data.tar.gz: ba842e477f22c13151198164443cafdca41be95ad8ced809d41655f124bcb9ee
5
+ SHA512:
6
+ metadata.gz: d4c427ece85c3bc8c60737ebc47d7c130a7de6907bda74abb164184fe735b3d16f5244e448191b17488798ff4573191b9a2c256739201ca50a21ebcdacc36e0f
7
+ data.tar.gz: 007e60e476b29d01456e5cf6675f1cd48c4c49d4ebb68f59317c4867e4fbf6e5524f10549107352940ebfdedbf880eef0844ec6e6df2d468ac76fadb08bfef9e
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## Version 0.1.0
4
+
5
+ [View version doc](https://github.com/coffeebrewapps/coffeebrew_jekyll_paginate/blob/v0.1.0/README.md)
6
+
7
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Coffee Brew Apps
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,704 @@
1
+ # Jekyll Paginate Plugin
2
+
3
+ A Jekyll plugin to generate site pagination for collections.
4
+
5
+ [![Continuous Integration](https://github.com/coffeebrewapps/coffeebrew_jekyll_paginate/actions/workflows/ruby.yml/badge.svg)](https://github.com/coffeebrewapps/coffeebrew_jekyll_paginate/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/coffeebrew_jekyll_paginate.svg)](https://badge.fury.io/rb/coffeebrew_jekyll_paginate)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your site's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'coffeebrew_jekyll_paginate'
13
+ ```
14
+
15
+ And then add this line to your site's `_config.yml`:
16
+
17
+ ```yml
18
+ plugins:
19
+ - coffeebrew_jekyll_paginate
20
+ ```
21
+
22
+ By default, the plugin doesn't generate any pagination. You need to specify which collections to be paginated. For
23
+ example,
24
+
25
+ ```yml
26
+ ---
27
+ coffeebrew_jekyll_paginate:
28
+ collections:
29
+ books:
30
+ ```
31
+
32
+ Assuming layouts have been setup (see more in [Layouts](#layouts)), then, the plugin will generate paginated
33
+ collections using the default config below:
34
+
35
+ ```yml
36
+ individual_page_pagination: false
37
+ frontmatter_defaults_key: "paginated_%{collection_type}"
38
+ first_page_as_root:
39
+ enabled: false
40
+ permalink: /:collection_type/:page_num_one_index
41
+ index_page: "index"
42
+ per_page: 5
43
+ sort_field: "date"
44
+ sort_reverse: true
45
+ page_num_label: "%{page_num_one_index}"
46
+ ```
47
+
48
+ An example of the generated directory structure is as such:
49
+
50
+ ```bash
51
+ _site/
52
+ ├── books
53
+ │   ├── 1
54
+ │   │   ├── index.html
55
+ │   └── 2
56
+ │   └── index.html
57
+ └── index.html
58
+ ```
59
+
60
+ ## Configuration
61
+
62
+ You can configure for as many collections as needed, and configure a site-wide pagination config, or configure for each
63
+ collection to use its own configuration. If there is no configuration for the site default, or the collection, the
64
+ plugin will use the default values as previously mentioned.
65
+
66
+ ### Site-wide defaults
67
+
68
+ For example, you can configure site-wide defaults that override the plugin's defaults, which will be used for all the
69
+ collections enabled.
70
+
71
+ ```yml
72
+ coffeebrew_jekyll_paginate:
73
+ defaults:
74
+ individual_page_pagination: true
75
+ first_page_as_root:
76
+ enabled: true
77
+ permalink: /
78
+ index_page: /:collection_type
79
+ per_page: 3
80
+ page_num_label: "Page %{page_num_one_index}"
81
+ collections:
82
+ books:
83
+ posts:
84
+ ```
85
+
86
+ If you do override the configuration, the plugin will perform a simple validation on your overrides according to these rules:
87
+
88
+ ### Frontmatter defaults key config
89
+
90
+ This tells the plugin what is the key used for setting defaults. For example, the plugin will use `paginated_posts` for
91
+ `posts` type by default, and this will allow Jekyll to lookup defaults for `type: "paginated_posts"` in `_config.yml`.
92
+
93
+ ```yml
94
+ defaults:
95
+ - scope:
96
+ path: ""
97
+ type: "paginated_posts"
98
+ values:
99
+ layout: "posts_index"
100
+ ```
101
+
102
+ ### Individual page pagination config
103
+
104
+ If set to `true`, this tells the plugin to generate previous/next pagination for individual collection pages. This is
105
+ useful if you want to have individual page-level pagination to navigate to the previous or next page. For example:
106
+
107
+ ```yml
108
+ ---
109
+ layout: default
110
+ ---
111
+ <h1>Posts</h1>
112
+ {% raw %}
113
+ {{ content }}
114
+
115
+ <div class="pagination">
116
+ <ul class="pager">
117
+ <li class="previous">
118
+ <a href="{{ page.paginator.previous_page_path }}">< {{ page.paginator.previous_page.title }}</a>
119
+ </li>
120
+ <li class="next">
121
+ <a href="{{ page.paginator.next_page_path }}">{{ page.paginator.next_page.title }} ></a>
122
+ </li>
123
+ </ul>
124
+ </div>
125
+ {% endraw %}
126
+ ```
127
+
128
+ ### First page as root config
129
+
130
+ This tells the plugin to generate the first page outside of the collection directory in the paginated set. For example,
131
+ if there are 4 pages in the paginated books collection, then the first page will be rendered in `/books.html`, and the
132
+ other pages will be rendered as `/books/2/index.html`, `/books/3/index.html` and `/books/4/index.html`.
133
+
134
+ This will be useful if you want to have the first page as part of your root pages and include it in the navigation.
135
+
136
+ | Key | Allowed Value(s) | Default | Remark |
137
+ | --- | --- | --- | --- |
138
+ | enabled | Boolean | false | If set to `true`, then the plugin will render the first page in a separate path as defined by the `permalink` and `index_page` below. |
139
+ | permalink | String | nil | The directory in which to generate the first page. Normally this will be `/` if you want it to be a root-level page. |
140
+ | index_page | String | nil | The filename of the first page. Normally this will be the name corresponding to the collection. |
141
+
142
+ If `enabled` is `false`, both `permalink` and `index_page` will be ignored, and the plugin will generate the first page
143
+ the same way as the remaining pages.
144
+
145
+ ### Pagination config
146
+
147
+ This tells the plugin how to generate the pagination pages.
148
+
149
+ | Key | Allowed Value(s) | Default | Remark |
150
+ | --- | --- | --- | --- |
151
+ | permalink | String | /:collection_type/:page_num_one_index | The directory in which to generate the page. |
152
+ | index_page | String | index | The filename of the page. |
153
+ | per_page | Integer | 5 | The number of collection items in each page. |
154
+ | sort_field | String | date | The field to be used to sort the collection for deterministic pagination. |
155
+ | sort_reverse | Boolean | true | The collection will be sorted in reverse if set to `true`. |
156
+ | page_num_label | String | %{page_num_one_index} | The format string for the page number label. |
157
+
158
+ A few placeholders are available to be used in the `permalink`, `index_page` and `page_num_label`:
159
+
160
+ | Field | Description |
161
+ | --- | --- |
162
+ | collection_type | This is the collection type current page, eg. `posts`. |
163
+ | page_num_zero_index | This will be the 0-index page number of the current page. |
164
+ | page_num_one_index | This will be the 1-index page number of the current page. |
165
+
166
+ ### Validation
167
+
168
+ If the config overrides have invalid structure, keys or values, the plugin will raise a
169
+ `Jekyll::Errors::InvalidConfigurationError` during build.
170
+
171
+ ## Layouts
172
+
173
+ The plugin does not provide a default layout. You will need to create your own layout in `_layouts` and configure the
174
+ defaults in `_config.yml`, for example, if you want to paginate the `books` collection, then you need to configure
175
+ the layout for the collection:
176
+
177
+ ```yml
178
+ ---
179
+ defaults:
180
+ - scope:
181
+ type: "paginated_books"
182
+ values:
183
+ layout: "books_pagination"
184
+ - scope:
185
+ type: "books"
186
+ values:
187
+ layout: "book"
188
+ permalink: /books/:name:output_ext
189
+ ```
190
+
191
+ As mentioned earlier, you need to set the `scope.type` here to match the `frontmatters_defaults_key` config of the
192
+ plugin. Note that there are 2 layouts configured here, `books_pagination` is used for the paginated collection page,
193
+ and `book` is used for the individual book page.
194
+
195
+ In addition to Jekyll's default page data, you can also use the additional page data and page's paginator data generated
196
+ by the plugin in the layout:
197
+
198
+ ### Paginated collection page
199
+
200
+ Use `page` to access the fields below.
201
+
202
+ | Field | Description |
203
+ | --- | --- |
204
+ | title | Current page's title. |
205
+ | collection | Current page's collection. |
206
+ | collection_type | Current page's collection type. |
207
+ | page_num_zero_index | Current page's 0-index page number. |
208
+ | page_num_one_index | Current page's 1-index page number. |
209
+ | page_num_label | Current page's page number label. |
210
+ | full_url | Current page full url. Can be used to match the current window url for highlighting purpose. |
211
+
212
+ ### Paginated collection paginator
213
+
214
+ Use `page.paginator` to acceess the fields below.
215
+
216
+ | Field | Description |
217
+ | --- | --- |
218
+ | total_pages | Total number of pages of the paginated collection. |
219
+ | pages | All the pages in the collection. |
220
+ | page_num_zero_index | Current page's 0-index page number. |
221
+ | page_num_one_index | Current page's 1-index page number. |
222
+ | page_num_label | Current page's page number label. |
223
+ | collection | Current page's collection. |
224
+ | collection_type | Current page's collection type. |
225
+ | current_page | Current page. |
226
+ | current_page_path | Current page full url. Can be used to match the current window url for highlighting purpose. |
227
+ | previous_page | Previous page. |
228
+ | previous_page_path | Previous page full url. Can be used to generate navigation link. |
229
+ | next_page | Next page. |
230
+ | next_page_path | Next page full url. Can be used to generate navigation link. |
231
+
232
+ An example of the paginated collection page layout `books_pagination`:
233
+
234
+ ```html
235
+ ---
236
+ layout: default
237
+ ---
238
+ <h1>Books</h1>
239
+ <div class="collection">
240
+ {% raw %}
241
+ {% for book in page.paginator.collection %}
242
+ <div class="item">
243
+ <div class="title">
244
+ <span>{{ book.title }}</span>
245
+ </div>
246
+ <p>{{ book.excerpt }}</p>
247
+ </div>
248
+ {% endfor %}
249
+ </div>
250
+
251
+ {% if page.paginator.total_pages > 1 %}
252
+ <div class="pagination">
253
+ <ul class="pager">
254
+ {% if page.paginator.previous_page %}
255
+ <li class="previous">
256
+ <a href="{{ page.paginator.previous_page_path }}"><i class="fa-solid fa-arrow-left"></i> Newer {{ page.paginator.collection_type | capitalize }}</a>
257
+ </li>
258
+ {% endif %}
259
+ {% if page.paginator.next_page %}
260
+ <li class="next">
261
+ <a href="{{ page.paginator.next_page_path }}">Older {{ page.paginator.collection_type | capitalize }} <i class="fa-solid fa-arrow-right"></i></a>
262
+ </li>
263
+ {% endif %}
264
+ </ul>
265
+ </div>
266
+ {% endif %}
267
+ {% endraw %}
268
+ ```
269
+
270
+ The resulting index pages using the example configuration and layout are as such:
271
+
272
+ ### Root page
273
+
274
+ Generated at: `_site/books.html`.
275
+
276
+ ```html
277
+ <!DOCTYPE html>
278
+ <html>
279
+ <head>
280
+ <meta charset="utf-8">
281
+ <title>Books</title>
282
+ </head>
283
+ <body>
284
+ <div class="container">
285
+ <h1>Books</h1>
286
+ <div class="collection">
287
+ <div class="item">
288
+ <div class="title">
289
+ <span>Harry Potter 7</span>
290
+ </div>
291
+ <p>This is the final book in the Harry Potter series.</p>
292
+ </div>
293
+
294
+ <div class="item">
295
+ <div class="title">
296
+ <span>Harry Potter 6</span>
297
+ </div>
298
+ <p>This is the sixth book in the Harry Potter series.</p>
299
+ </div>
300
+
301
+ <div class="item">
302
+ <div class="title">
303
+ <span>Harry Potter 5</span>
304
+ </div>
305
+ <p>This is the fifth book in the Harry Potter series.</p>
306
+ </div>
307
+ </div>
308
+
309
+ <div class="pagination">
310
+ <ul class="pager">
311
+ <li class="next">
312
+ <a href="/books/2/index.html">Older Books <i class="fa-solid fa-arrow-right"></i></a>
313
+ </li>
314
+ </ul>
315
+ </div>
316
+ </div>
317
+ </body>
318
+ </html>
319
+ ```
320
+
321
+ ### Next page
322
+
323
+ Generated at: `_site/books/2/index.html`.
324
+
325
+ Note: Header elements omitted for clarity.
326
+
327
+ ```html
328
+ <div class="container">
329
+ <h1>Books</h1>
330
+ <div class="collection">
331
+ <div class="item">
332
+ <div class="title">
333
+ <span>Harry Potter 4</span>
334
+ </div>
335
+ <p>This is the fourth book in the Harry Potter series.</p>
336
+ </div>
337
+
338
+ <div class="item">
339
+ <div class="title">
340
+ <span>Harry Potter 3</span>
341
+ </div>
342
+ <p>This is the third book in the Harry Potter series.</p>
343
+ </div>
344
+
345
+ <div class="item">
346
+ <div class="title">
347
+ <span>Harry Potter 2</span>
348
+ </div>
349
+ <p>This is the second book in the Harry Potter series.</p>
350
+ </div>
351
+ </div>
352
+
353
+ <div class="pagination">
354
+ <ul class="pager">
355
+ <li class="previous">
356
+ <a href="/books.html"><i class="fa-solid fa-arrow-left"></i> Newer Books</a>
357
+ </li>
358
+ <li class="next">
359
+ <a href="/books/3/index.html">Older Books <i class="fa-solid fa-arrow-right"></i></a>
360
+ </li>
361
+ </ul>
362
+ </div>
363
+ </div>
364
+ ```
365
+
366
+ ### Last page
367
+
368
+ Generated at: `_site/books/3/index.html`.
369
+
370
+ Note: Header elements omitted for clarity.
371
+
372
+ ```html
373
+ <div class="container">
374
+ <h1>Books</h1>
375
+ <div class="collection">
376
+ <div class="item">
377
+ <div class="title">
378
+ <span>Harry Potter 1</span>
379
+ </div>
380
+ <p>This is the first book in the Harry Potter series.</p>
381
+ </div>
382
+ </div>
383
+
384
+ <div class="pagination">
385
+ <ul class="pager">
386
+ <li class="previous">
387
+ <a href="/books/2/index.html"><i class="fa-solid fa-arrow-left"></i> Newer Books</a>
388
+ </li>
389
+ </ul>
390
+ </div>
391
+ </div>
392
+ ```
393
+
394
+ ### Individual page paginator
395
+
396
+ Use `page.paginator` to access the fields below.
397
+
398
+ | Field | Description |
399
+ | --- | --- |
400
+ | collection_type | Current page's collection type. |
401
+ | current_page | Current page. |
402
+ | current_page_path | Current page full url. Can be used to match the current window url for highlighting purpose. |
403
+ | previous_page | Previous page. |
404
+ | previous_page_path | Previous page full url. Can be used to generate navigation link. |
405
+ | next_page | Next page. |
406
+ | next_page_path | Next page full url. Can be used to generate navigation link. |
407
+
408
+ An example of the individual page layout `book`:
409
+
410
+ ```html
411
+ ---
412
+ layout: default
413
+ ---
414
+ <h1>{{ page.title }}</h1>
415
+ {% raw %}
416
+ <div class="title">
417
+ <span>{{ page.title }}</span>
418
+ </div>
419
+ <p>{{ content }}</p>
420
+
421
+ {% if page.paginator %}
422
+ <div class="pagination">
423
+ <ul class="pager">
424
+ {% if page.paginator.previous_page %}
425
+ <li class="previous">
426
+ <a href="{{ page.paginator.previous_page_path }}"><i class="fa-solid fa-arrow-left"></i> Previous Book</a>
427
+ </li>
428
+ {% endif %}
429
+ {% if page.paginator.next_page %}
430
+ <li class="next">
431
+ <a href="{{ page.paginator.next_page_path }}">Next Book <i class="fa-solid fa-arrow-right"></i></a>
432
+ </li>
433
+ {% endif %}
434
+ </ul>
435
+ </div>
436
+ {% endif %}
437
+ {% endraw %}
438
+ ```
439
+
440
+ The resulting page using the example configuration and layout are as such:
441
+
442
+ ### Individual page
443
+
444
+ Generated at: `_site/books/book-1.html`
445
+
446
+ ```html
447
+ <!DOCTYPE html>
448
+ <html>
449
+ <head>
450
+ <meta charset="utf-8">
451
+ <title>Books</title>
452
+ </head>
453
+ <body>
454
+ <div class="container">
455
+ <h1>Book 1</h1>
456
+ <div class="title">
457
+ <span>Book 1</span>
458
+ </div>
459
+ <p>This is book 1.</p>
460
+
461
+ <div class="pagination">
462
+ <ul class="pager">
463
+ <li class="next">
464
+ <a href="/books/book-2.html">Next Book <i class="fa-solid fa-arrow-right"></i></a>
465
+ </li>
466
+ </ul>
467
+ </div>
468
+ </div>
469
+ </body>
470
+ </html>
471
+ ```
472
+
473
+ ## Contributing
474
+
475
+ Contribution to the gem is very much welcome!
476
+
477
+ 1. Fork it (https://github.com/coffeebrewapps/coffeebrew_jekyll_paginate/fork).
478
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
479
+ 3. Make sure you have setup the repo correctly so that you can run RSpec and Rubocop on your changes. Read more under the [Development](#development) section.
480
+ 4. Commit your changes (`git commit -am 'Add some feature'`).
481
+ 5. If you have added something that is worth mentioning in the README, please also update the README.md accordingly and commit the changes.
482
+ 6. Push to the branch (`git push origin my-new-feature`).
483
+ 7. Create a new Pull Request.
484
+
485
+ The repo owner will try to respond to a new PR as soon as possible.
486
+
487
+ ## Development
488
+
489
+ We want to provide a robust gem as much as possible for the users, so writing test cases will be required for any new
490
+ feature.
491
+
492
+ If you are contributing to the gem, please make sure you have setup your development environment correctly so that
493
+ RSpec and Rubocop can run properly.
494
+
495
+ 1. After forking the repo, go into the repo directory (`cd coffeebrew_jekyll_paginate/`).
496
+ 2. Make sure you have the correct Ruby version installed. This gem requires Ruby >= 2.7.0.
497
+ 3. Once you have the correct Ruby version, install the development dependencies (`bundle install`).
498
+ 4. To test that you have everything installed correctly, run the test cases (`bundle exec rspec`).
499
+ 5. You should see all test cases pass successfully.
500
+
501
+ ### Source directory structure
502
+
503
+ All the gem logic lives in the `/lib` directory:
504
+
505
+ ```bash
506
+ lib
507
+ ├── coffeebrew_jekyll_paginate
508
+ │   ├── config.yml
509
+ │   ├── generator.rb
510
+ │   ├── individual_paginator.rb
511
+ │   ├── page.rb
512
+ │   ├── page_drop.rb
513
+ │   ├── paginator.rb
514
+ │   ├── validator.rb
515
+ │   └── version.rb
516
+ └── coffeebrew_jekyll_paginate.rb
517
+ ```
518
+
519
+ The files that are currently in the repo:
520
+
521
+ | File | Description |
522
+ | --- | --- |
523
+ | `lib/coffeebrew_jekyll_paginate/config.yml` | This contains the default configuration for the plugin to generate pagination. |
524
+ | `lib/coffeebrew_jekyll_paginate/generator.rb` | This is the generator that reads the configuration and generate pagination. |
525
+ | `lib/coffeebrew_jekyll_paginate/individual_paginator.rb` | This is the abstract model containing the pagination methods for individual pages. |
526
+ | `lib/coffeebrew_jekyll_paginate/page.rb` | This is the abstract model of the paginated collection pages. |
527
+ | `lib/coffeebrew_jekyll_paginate/page_drop.rb` | This is the page drop used by the paginator class so its methods can be used in Liquid template. |
528
+ | `lib/coffeebrew_jekyll_paginate/paginator.rb` | This is the abstract model containing the pagination methods for paginated collection pages. |
529
+ | `lib/coffeebrew_jekyll_paginate/validator.rb` | This validates the configuration. |
530
+ | `lib/coffeebrew_jekyll_paginate/version.rb` | This contains the version number of the gem. |
531
+ | `lib/coffeebrew_jekyll_paginate.rb` | This is the entry point of the gem, and it loads the dependencies. |
532
+
533
+ ### Test cases directory structure
534
+
535
+ All the test cases and fixtures live in the `/spec` directory:
536
+
537
+ Note: Some files have been omitted for clarity.
538
+
539
+ ```bash
540
+ spec
541
+ ├── coffeebrew_jekyll_paginate_spec.rb
542
+ ├── dest
543
+ ├── fixtures
544
+ │   ├── _books
545
+ │   │   ├── 1997-06-26-harry-potter-1.md
546
+ │   │   ├── 1998-07-02-harry-potter-2.md
547
+ │   │   ├── 1999-07-08-harry-potter-3.md
548
+ │   │   ├── 2000-07-08-harry-potter-4.md
549
+ │   │   ├── 2003-06-21-harry-potter-5.md
550
+ │   │   ├── 2005-07-16-harry-potter-6.md
551
+ │   │   └── 2007-07-21-harry-potter-7.md
552
+ │   ├── _layouts
553
+ │   │   ├── book.html
554
+ │   │   ├── default.html
555
+ │   │   ├── paginated_books.html
556
+ │   │   ├── paginated_posts.html
557
+ │   │   └── post.html
558
+ │   ├── _posts
559
+ │   │   ├── 2021-03-12-test-post-1.md
560
+ │   │   ├── 2021-03-28-test-post-2.md
561
+ │   │   ├── 2021-05-03-test-post-3.md
562
+ │   │   ├── 2021-05-03-test-post-4.md
563
+ │   │   ├── 2022-01-27-test-post-5.md
564
+ │   │   ├── 2022-03-12-test-post-6.md
565
+ │   │   ├── 2022-11-23-test-post-7.md
566
+ │   │   └── 2023-02-21-test-post-8.md
567
+ │   └── _config.yml
568
+ ├── scenarios
569
+ │   ├── default
570
+ │   │   ├── _site
571
+ │   │   │   ├── books
572
+ │   │   │   │   ├── 1
573
+ │   │   │   │   │   └── index.html
574
+ │   │   │   │   ├── 2
575
+ │   │   │   │   │   └── index.html
576
+ │   │   │   │   ├── 1997-06-26-harry-potter-1.html
577
+ │   │   │   │   ├── 1998-07-02-harry-potter-2.html
578
+ │   │   │   │   ├── 1999-07-08-harry-potter-3.html
579
+ │   │   │   │   ├── 2000-07-08-harry-potter-4.html
580
+ │   │   │   │   ├── 2003-06-21-harry-potter-5.html
581
+ │   │   │   │   ├── 2005-07-16-harry-potter-6.html
582
+ │   │   │   │   └── 2007-07-21-harry-potter-7.html
583
+ │   │   │   └── posts
584
+ │   │   │      ├── 1
585
+ │   │   │      │   └── index.html
586
+ │   │   │      ├── 2
587
+ │   │   │      │   └── index.html
588
+ │   │   │      ├── 2021-03-12-test-post-1.html
589
+ │   │   │      ├── 2021-03-28-test-post-2.html
590
+ │   │   │      ├── 2021-05-03-test-post-3.html
591
+ │   │   │      ├── 2021-05-03-test-post-4.html
592
+ │   │   │      ├── 2022-01-27-test-post-5.html
593
+ │   │   │      ├── 2022-03-12-test-post-6.html
594
+ │   │   │      ├── 2022-11-23-test-post-7.html
595
+ │   │   │      └── 2023-02-21-test-post-8.html
596
+ │   │   └── context.rb
597
+ │   └── invalid_config_keys
598
+ │   └── context.rb
599
+ └── spec_helper.rb
600
+ ```
601
+
602
+ The files that are currently in the repo:
603
+
604
+ | File | Description |
605
+ | --- | --- |
606
+ | `spec/coffeebrew_jekyll_paginate_spec.rb` | This is the main RSpec file to be executed. It contains all the contexts of various scenarios. |
607
+ | `spec/dest/` | This directory is where generated files are located. It will be deleted immediately after each context is executed. |
608
+ | `spec/fixtures/` | This directory follows the Jekyll site source structure and contains the minimal files required to generate the paginated pages. |
609
+ | `spec/fixtures/_books` | This directory contains the test books, you can add more to it to test your new feature. |
610
+ | `spec/fixtures/_posts` | This directory contains the test posts, you can add more to it to test your new feature. |
611
+ | `spec/scenarios/` | This directory contains the expected files of various test scenarios. |
612
+ | `spec/scenarios/<scenario>/` | This is the scenario name. |
613
+ | `spec/scenarios/<scenario>/_site/` | This directory contains the expected paginated pages. |
614
+ | `spec/scenarios/<scenario>/context.rb` | This is the file that sets up the context for the test case. |
615
+ | `spec/spec_helper.rb` | This contains RSpec configuration and certain convenience methods for the main RSpec file. |
616
+
617
+ ### Writing test cases
618
+
619
+ There is a certain convention to follow when writing new test scenarios. The recommendation is to use the rake tasks
620
+ provided in the gem to generate the scenario files.
621
+
622
+ For success scenarios, run:
623
+
624
+ ```bash
625
+ bundle exec rake coffeebrew:jekyll:paginate:test:create_success[test_scenario]
626
+ ```
627
+
628
+ This will generate a placeholder file and directory:
629
+
630
+ ```bash
631
+ spec
632
+ ├── coffeebrew_jekyll_paginate_spec.rb
633
+ ├── scenarios
634
+ │   └── test_scenario
635
+ │      ├── _site
636
+ │       └── context.rb
637
+ └── spec_helper.rb
638
+ ```
639
+
640
+ Where the `context.rb` file will be pre-populated:
641
+
642
+ ```ruby
643
+ # frozen_string_literal: true
644
+
645
+ CONTEXT_TEST_SCENARIO = "when using test_scenario config"
646
+
647
+ RSpec.shared_context CONTEXT_TEST_SCENARIO do
648
+ let(:scenario) { "test_scenario" }
649
+ let(:overrides) {} # TODO: remove if unused
650
+ let(:generated_files) {} # TODO: remove if unused
651
+ let(:expected_files) do
652
+ [
653
+ ]
654
+ end
655
+ end
656
+ ```
657
+
658
+ For failure scenarios, run:
659
+
660
+ ```bash
661
+ bundle exec rake coffeebrew:jekyll:paginate:test:create_failure[test_scenario]
662
+ ```
663
+
664
+ This will generate a placeholder file and directory:
665
+
666
+ ```bash
667
+ spec
668
+ ├── coffeebrew_jekyll_paginate_spec.rb
669
+ ├── scenarios
670
+ │   └── test_scenario
671
+ │       └── context.rb
672
+ └── spec_helper.rb
673
+ ```
674
+
675
+ Where the `context.rb` file will be pre-populated:
676
+
677
+ ```ruby
678
+ # frozen_string_literal: true
679
+
680
+ CONTEXT_TEST_SCENARIO = "when using test_scenario config"
681
+
682
+ RSpec.shared_context CONTEXT_TEST_SCENARIO do
683
+ let(:scenario) { "test_scenario" }
684
+ let(:generated_files) { [] }
685
+ let(:expected_files) { [] }
686
+ let(:overrides) do
687
+ {
688
+ }
689
+ end
690
+
691
+ let(:expected_errors) do
692
+ [
693
+ ]
694
+ end
695
+ end
696
+ ```
697
+
698
+ If you do write other test cases that are not asserting the generated files like above, you can initiate your
699
+ convention. The repo owner will evaluate the PR and accept the convention if it fits the repo existing convention, or
700
+ will recommend an alternative if it doesn't.
701
+
702
+ ## License
703
+
704
+ See the [LICENSE](LICENSE) file.
@@ -0,0 +1,13 @@
1
+ coffeebrew_jekyll_paginate:
2
+ defaults:
3
+ individual_page_pagination: false
4
+ frontmatter_defaults_key: "paginated_%{collection_type}"
5
+ first_page_as_root:
6
+ enabled: false
7
+ permalink: /:collection_type/:page_num_one_index
8
+ index_page: "index"
9
+ per_page: 5
10
+ sort_field: "date"
11
+ sort_reverse: true
12
+ page_num_label: "%{page_num_one_index}"
13
+ collections: []
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./page"
4
+ require_relative "./individual_paginator"
5
+ require_relative "./paginator"
6
+ require_relative "./validator"
7
+
8
+ module Coffeebrew
9
+ module Jekyll
10
+ module Paginate
11
+ class Generator < ::Jekyll::Generator
12
+ safe true
13
+ priority :lowest
14
+
15
+ def generate(site)
16
+ @site = site
17
+
18
+ validate!
19
+
20
+ user_collections_config.each do |collection_type, options|
21
+ collection_options = add_collections_defaults(options)
22
+ process_collection(collection_type.to_sym, collection_options)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def validate!
29
+ validator = Coffeebrew::Jekyll::Paginate::Validator.new(@site, user_config)
30
+ validator.validate!
31
+ end
32
+
33
+ def process_collection(collection_type, options)
34
+ sort_field = options["sort_field"].to_sym
35
+ sort_reverse = options["sort_reverse"]
36
+ records = @site.collections[collection_type.to_s].docs
37
+ records.sort_by!(&sort_field)
38
+ records.reverse! if sort_reverse
39
+
40
+ individual_pagination = options["individual_page_pagination"]
41
+ generate_individual_page_paginator(collection_type, records) if individual_pagination
42
+
43
+ pages = generate_pages(collection_type, records, options)
44
+ return if pages.empty?
45
+
46
+ generate_paginators(collection_type, pages)
47
+ end
48
+
49
+ def generate_individual_page_paginator(collection_type, records)
50
+ records.each_with_index do |record, index|
51
+ prev_record = index.positive? ? records[index - 1] : nil
52
+ next_record = index < records.length ? records[index + 1] : nil
53
+ paginator = Paginate::IndividualPaginator.new(collection_type, prev_record, record, next_record)
54
+ record.data["paginator"] = paginator
55
+ end
56
+ end
57
+
58
+ def generate_pages(collection_type, records, options)
59
+ per_page = options["per_page"].to_i
60
+ first_page_as_root = options["first_page_as_root"]
61
+
62
+ records.each_slice(per_page).with_index.map do |page_records, page_num|
63
+ page_options = options.clone
64
+ if first_page_as_root["enabled"] && page_num.zero?
65
+ page_options["permalink"] = first_page_as_root["permalink"]
66
+ page_options["index_page"] = first_page_as_root["index_page"]
67
+ end
68
+ Paginate::Page.new(@site, page_options, collection_type, page_records, page_num)
69
+ end
70
+ end
71
+
72
+ def generate_paginators(collection_type, pages)
73
+ pages.each do |page|
74
+ paginator = Paginate::Paginator.new(pages, collection_type, page.page_num_zero_index)
75
+ page.paginator = paginator
76
+ @site.pages << page
77
+ end
78
+ end
79
+
80
+ def add_collections_defaults(user_options)
81
+ ::Jekyll::Utils.deep_merge_hashes(
82
+ default_defaults_config,
83
+ ::Jekyll::Utils.deep_merge_hashes(user_defaults_config, user_options)
84
+ )
85
+ end
86
+
87
+ def user_collections_config
88
+ @user_collections_config ||= user_config["collections"].to_h.transform_values(&:to_h)
89
+ end
90
+
91
+ def user_defaults_config
92
+ @user_defaults_config ||= user_config["defaults"].to_h
93
+ end
94
+
95
+ def user_config
96
+ @user_config ||= @site.config["coffeebrew_jekyll_paginate"].to_h
97
+ end
98
+
99
+ def default_defaults_config
100
+ @default_defaults_config ||= default_config.dig("coffeebrew_jekyll_paginate", "defaults").to_h
101
+ end
102
+
103
+ def default_config
104
+ @default_config ||= YAML.safe_load(File.read(default_config_path))
105
+ end
106
+
107
+ def default_config_path
108
+ @default_config_path ||= File.expand_path("config.yml", __dir__)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./page_drop"
4
+
5
+ module Coffeebrew
6
+ module Jekyll
7
+ module Paginate
8
+ class IndividualPaginator
9
+ attr_reader :collection_type,
10
+ :current_page, :current_page_path,
11
+ :previous_page, :previous_page_path,
12
+ :next_page, :next_page_path
13
+
14
+ def initialize(collection_type, previous_page, current_page, next_page)
15
+ @collection_type = collection_type
16
+ @current_page = current_page
17
+ @current_page_path = full_url(@current_page)
18
+ @previous_page = previous_page
19
+ @previous_page_path = full_url(@previous_page)
20
+ @next_page = next_page
21
+ @next_page_path = full_url(@next_page)
22
+ end
23
+
24
+ def data
25
+ @data ||= {
26
+ "collection_type" => collection_type,
27
+ "current_page" => current_page,
28
+ "current_page_path" => current_page_path,
29
+ "previous_page" => previous_page,
30
+ "previous_page_path" => previous_page_path,
31
+ "next_page" => next_page,
32
+ "next_page_path" => next_page_path
33
+ }
34
+ end
35
+
36
+ def to_liquid
37
+ @to_liquid ||= Paginate::PageDrop.new(self)
38
+ end
39
+
40
+ private
41
+
42
+ def full_url(page)
43
+ return "" if page.nil?
44
+
45
+ page.url
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coffeebrew
4
+ module Jekyll
5
+ module Paginate
6
+ class Page < ::Jekyll::Page
7
+ attr_reader :paginator, :collection_type, :collection,
8
+ :page_num_zero_index, :page_num_one_index
9
+
10
+ def initialize(site, config, collection_type, collection, page_num_zero_index) # rubocop:disable Lint/MissingSuper
11
+ @site = site
12
+ @config = config
13
+ @collection_type = collection_type
14
+ @collection = collection
15
+ @page_num_zero_index = page_num_zero_index
16
+ @page_num_one_index = page_num_zero_index.to_i + 1
17
+
18
+ @base = site.source
19
+ @ext = ".html"
20
+
21
+ build_data
22
+ end
23
+
24
+ def paginator=(paginator)
25
+ @paginator = paginator
26
+ @data["paginator"] = paginator
27
+
28
+ data.default_proc = proc do |_, key|
29
+ site.frontmatter_defaults.find(relative_path, frontmatter_defaults_key, key)
30
+ end
31
+ end
32
+
33
+ def frontmatter_defaults_key
34
+ @frontmatter_defaults_key ||= format(@config["frontmatter_defaults_key"], collection_type: collection_type)
35
+ end
36
+
37
+ def basename
38
+ @basename ||= ::Jekyll::URL.new(template: nil,
39
+ placeholders: basename_placeholders,
40
+ permalink: @config["index_page"])
41
+ .to_s
42
+ end
43
+
44
+ def name
45
+ @name ||= "#{basename}#{ext}"
46
+ end
47
+
48
+ def dir
49
+ @dir ||= ::Jekyll::URL.new(template: nil,
50
+ placeholders: page_dir_placeholders,
51
+ permalink: @config["permalink"])
52
+ .to_s
53
+ end
54
+
55
+ def page_dir_placeholders
56
+ @page_dir_placeholders ||= {
57
+ collection_type: collection_type.to_s,
58
+ page_num_zero_index: page_num_zero_index.to_s,
59
+ page_num_one_index: page_num_one_index.to_s,
60
+ basename: basename,
61
+ output_ext: output_ext
62
+ }
63
+ end
64
+
65
+ def basename_placeholders
66
+ @basename_placeholders ||= {
67
+ collection_type: collection_type.to_s,
68
+ page_num_zero_index: page_num_zero_index.to_s,
69
+ page_num_one_index: page_num_one_index.to_s
70
+ }
71
+ end
72
+
73
+ def url_placeholders
74
+ {
75
+ path: dir,
76
+ basename: basename,
77
+ output_ext: output_ext
78
+ }
79
+ end
80
+
81
+ def page_num_label
82
+ @page_num_label ||= format(@config["page_num_label"],
83
+ page_num_zero_index: page_num_zero_index, page_num_one_index: page_num_one_index)
84
+ end
85
+
86
+ def title
87
+ @title ||= collection_type.capitalize
88
+ end
89
+
90
+ def full_url
91
+ @full_url ||= [dir, name].join("/").squeeze("/")
92
+ end
93
+
94
+ private
95
+
96
+ def build_data
97
+ @data = {
98
+ "title" => title,
99
+ "collection" => collection,
100
+ "collection_type" => collection_type,
101
+ "page_num_zero_index" => page_num_zero_index,
102
+ "page_num_one_index" => page_num_one_index,
103
+ "page_num_label" => page_num_label,
104
+ "full_url" => full_url
105
+ }
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coffeebrew
4
+ module Jekyll
5
+ module Paginate
6
+ class PageDrop < ::Jekyll::Drops::Drop
7
+ extend Forwardable
8
+
9
+ mutable false
10
+
11
+ def_delegators :@obj, :posts, :type, :title, :date, :name, :path, :url, :permalink
12
+ def_delegator :@obj, :data, :fallback_data
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./page_drop"
4
+
5
+ module Coffeebrew
6
+ module Jekyll
7
+ module Paginate
8
+ class Paginator
9
+ attr_reader :total_pages, :pages,
10
+ :page_num_zero_index, :page_num_one_index, :page_num_label,
11
+ :collection, :collection_type,
12
+ :current_page, :current_page_path,
13
+ :previous_page, :previous_page_path,
14
+ :next_page, :next_page_path
15
+
16
+ def initialize(pages, collection_type, page_num_zero_index = 0)
17
+ @total_pages = pages.size
18
+ @pages = pages
19
+ @page_num_zero_index = page_num_zero_index
20
+ @collection_type = collection_type
21
+
22
+ set_current_page
23
+ set_prev_page
24
+ set_next_page
25
+ end
26
+
27
+ def data # rubocop:disable Metrics/MethodLength
28
+ @data ||= {
29
+ "total_pages" => total_pages,
30
+ "pages" => pages,
31
+ "page_num_zero_index" => page_num_zero_index,
32
+ "page_num_one_index" => page_num_one_index,
33
+ "page_num_label" => page_num_label,
34
+ "collection" => collection,
35
+ "collection_type" => collection_type,
36
+ "current_page" => current_page,
37
+ "current_page_path" => current_page_path,
38
+ "previous_page" => previous_page,
39
+ "previous_page_path" => previous_page_path,
40
+ "next_page" => next_page,
41
+ "next_page_path" => next_page_path
42
+ }
43
+ end
44
+
45
+ def to_liquid
46
+ @to_liquid ||= Paginate::PageDrop.new(self)
47
+ end
48
+
49
+ private
50
+
51
+ def set_current_page
52
+ @current_page = pages[page_num_zero_index]
53
+ @current_page_path = full_url(@current_page)
54
+
55
+ @collection = current_page&.collection || []
56
+ @page_num_one_index = @current_page&.page_num_one_index
57
+ @page_num_label = @current_page&.page_num_label
58
+ end
59
+
60
+ def set_prev_page
61
+ return if page_num_zero_index.zero?
62
+
63
+ @previous_page = pages[page_num_zero_index - 1]
64
+ @previous_page_path = full_url(@previous_page)
65
+ end
66
+
67
+ def set_next_page
68
+ return unless page_num_zero_index < total_pages - 1
69
+
70
+ @next_page = pages[page_num_zero_index + 1]
71
+ @next_page_path = full_url(@next_page)
72
+ end
73
+
74
+ def full_url(page)
75
+ return "" if page.nil?
76
+
77
+ page.full_url
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coffeebrew
4
+ module Jekyll
5
+ module Paginate
6
+ class Validator
7
+ ROOT_KEY = "coffeebrew_jekyll_paginate"
8
+ COLLECTIONS_KEY = "collections"
9
+ DEFAULTS_KEY = "defaults"
10
+
11
+ ALLOWED_ROOT_KEYS = [
12
+ COLLECTIONS_KEY,
13
+ DEFAULTS_KEY
14
+ ].freeze
15
+
16
+ ALLOWED_CONFIG_KEYS = {
17
+ "frontmatter_defaults_key" => [],
18
+ "individual_page_pagination" => [true, false],
19
+ "first_page_as_root" => {
20
+ "enabled" => [true, false],
21
+ "permalink" => [],
22
+ "index_page" => []
23
+ },
24
+ "permalink" => [],
25
+ "index_page" => [],
26
+ "per_page" => [],
27
+ "sort_field" => [],
28
+ "sort_reverse" => [true, false],
29
+ "page_num_label" => []
30
+ }.freeze
31
+
32
+ def initialize(site, config)
33
+ @site = site
34
+ @config = config
35
+ @errors = []
36
+ end
37
+
38
+ def validate!
39
+ parse_root
40
+ parse_defaults
41
+ parse_collection_keys
42
+ parse_collections_config
43
+
44
+ return if @errors.empty?
45
+
46
+ ::Jekyll.logger.error "'coffeebrew_jekyll_paginate' config is set incorrectly."
47
+ ::Jekyll.logger.error "Errors:", @errors
48
+
49
+ raise ::Jekyll::Errors::InvalidConfigurationError, "'coffeebrew_jekyll_paginate' config is set incorrectly."
50
+ end
51
+
52
+ private
53
+
54
+ def parse_root
55
+ not_allowed_keys = @config.keys.reject do |key|
56
+ ALLOWED_ROOT_KEYS.include?(key)
57
+ end
58
+ return if not_allowed_keys.empty?
59
+
60
+ @errors << { key: ROOT_KEY, expected: ALLOWED_ROOT_KEYS, got: not_allowed_keys }
61
+ end
62
+
63
+ def defaults_config
64
+ @defaults_config ||= @config[DEFAULTS_KEY].to_h
65
+ end
66
+
67
+ def parse_defaults
68
+ parse(defaults_config, ALLOWED_CONFIG_KEYS, ["#{ROOT_KEY}.#{DEFAULTS_KEY}"])
69
+ end
70
+
71
+ def configured_collections
72
+ @configured_collections ||= collections_config.keys.sort
73
+ end
74
+
75
+ def allowed_collections
76
+ @allowed_collections ||= @site.collections.keys
77
+ end
78
+
79
+ def parse_collection_keys
80
+ not_allowed_collections = configured_collections.reject do |configured_collection|
81
+ allowed_collections.include?(configured_collection)
82
+ end
83
+ return if not_allowed_collections.empty?
84
+
85
+ @errors << {
86
+ key: "#{ROOT_KEY}.collections",
87
+ expected: allowed_collections,
88
+ got: not_allowed_collections
89
+ }
90
+ end
91
+
92
+ def collections_config
93
+ @collections_config ||= @config[COLLECTIONS_KEY].to_h.transform_values(&:to_h)
94
+ end
95
+
96
+ def parse_collections_config
97
+ collections_config.each do |collection_type, options|
98
+ parse(options, ALLOWED_CONFIG_KEYS, ["#{ROOT_KEY}.#{COLLECTIONS_KEY}.#{collection_type}"])
99
+ end
100
+ end
101
+
102
+ def parse(hash, allowed_keys, parent_key)
103
+ hash.each do |key, configured_value|
104
+ allowed_values = allowed_keys[key]
105
+ new_parent_key = parent_key + [key]
106
+
107
+ next if configured_in_allowed_values?(allowed_values, configured_value)
108
+
109
+ if same_hash_type?(allowed_values, configured_value)
110
+ next parse(configured_value, allowed_values, new_parent_key)
111
+ end
112
+
113
+ add_error(new_parent_key, allowed_values, configured_value)
114
+ end
115
+ end
116
+
117
+ def same_hash_type?(allowed_values, configured_value)
118
+ allowed_values.is_a?(Hash) && configured_value.is_a?(Hash)
119
+ end
120
+
121
+ def primitive?(value)
122
+ value.nil? || value.is_a?(String) || value.is_a?(Numeric) || value.is_a?(TrueClass) || value.is_a?(FalseClass)
123
+ end
124
+
125
+ def configured_in_allowed_values?(allowed_values, configured_value)
126
+ allowed_values.is_a?(Array) && primitive?(configured_value) &&
127
+ (allowed_values.empty? || allowed_values.include?(configured_value))
128
+ end
129
+
130
+ def add_error(parent_key, allowed_values, configured_value)
131
+ @errors << { key: parent_key.join("."), expected: allowed_values, got: configured_value }
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coffeebrew
4
+ module Jekyll
5
+ module Paginate
6
+ VERSION = "0.1.0"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll"
4
+ require "coffeebrew_jekyll_paginate/generator"
5
+
6
+ module Coffeebrew
7
+ module Jekyll
8
+ module Paginate
9
+ end
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coffeebrew_jekyll_paginate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Coffee Brew Apps
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-04-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jekyll
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '5.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '5.0'
33
+ description: A Jekyll plugin to generate pagination for site collections
34
+ email:
35
+ - coffeebrewapps@gmail.com
36
+ executables: []
37
+ extensions: []
38
+ extra_rdoc_files:
39
+ - README.md
40
+ - CHANGELOG.md
41
+ - LICENSE
42
+ files:
43
+ - CHANGELOG.md
44
+ - LICENSE
45
+ - README.md
46
+ - lib/coffeebrew_jekyll_paginate.rb
47
+ - lib/coffeebrew_jekyll_paginate/config.yml
48
+ - lib/coffeebrew_jekyll_paginate/generator.rb
49
+ - lib/coffeebrew_jekyll_paginate/individual_paginator.rb
50
+ - lib/coffeebrew_jekyll_paginate/page.rb
51
+ - lib/coffeebrew_jekyll_paginate/page_drop.rb
52
+ - lib/coffeebrew_jekyll_paginate/paginator.rb
53
+ - lib/coffeebrew_jekyll_paginate/validator.rb
54
+ - lib/coffeebrew_jekyll_paginate/version.rb
55
+ homepage: https://coffeebrewapps.com/coffeebrew_jekyll_paginate
56
+ licenses:
57
+ - MIT
58
+ metadata:
59
+ allowed_push_host: https://rubygems.org
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib/coffeebrew_jekyll_paginate
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 2.7.0
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubygems_version: 3.4.10
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: A Jekyll plugin to generate pagination for site collections
80
+ test_files: []