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 +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE +21 -0
- data/README.md +704 -0
- data/lib/coffeebrew_jekyll_paginate/config.yml +13 -0
- data/lib/coffeebrew_jekyll_paginate/generator.rb +113 -0
- data/lib/coffeebrew_jekyll_paginate/individual_paginator.rb +50 -0
- data/lib/coffeebrew_jekyll_paginate/page.rb +110 -0
- data/lib/coffeebrew_jekyll_paginate/page_drop.rb +16 -0
- data/lib/coffeebrew_jekyll_paginate/paginator.rb +82 -0
- data/lib/coffeebrew_jekyll_paginate/validator.rb +136 -0
- data/lib/coffeebrew_jekyll_paginate/version.rb +9 -0
- data/lib/coffeebrew_jekyll_paginate.rb +11 -0
- metadata +80 -0
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
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
|
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: []
|