jekyll-uj-powertools 1.6.24 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +84 -0
- data/jekyll-uj-powertools.gemspec +1 -1
- data/lib/generators/dynamic-pages.rb +152 -0
- data/lib/generators/inject-properties.rb +1 -1
- data/lib/generators/limit-collections.rb +56 -0
- data/lib/jekyll-uj-powertools.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 60803db8fb2a1e2c50aea11a0f49e964fc16d3323943ce0330f4312fd6468e37
|
|
4
|
+
data.tar.gz: 05f5ac27b7651b0d5592395cc510ba5b31e77f9b5ead9f35f0de6420d5919fd9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 17bb830ad04f8be16741f99f9dcaab5f723b7c9bb58f1ffbdfa0bf9a9637c0357e829c82dd4dc13feee2e70773bde48cd71e1cface15873f7232a60ea6880f4c
|
|
7
|
+
data.tar.gz: 21c374abbaa0e43c40bf1ecda75ac52eda70dd33140c69c972bf352eca2d571c35c5d70485947275a639c46b7df8e235f496a2b678a29008722ebeadb9e4e62b
|
data/README.md
CHANGED
|
@@ -294,6 +294,90 @@ Creates language-specific URLs for multilingual sites.
|
|
|
294
294
|
{% uj_translation_url target_lang, "/pricing" %}
|
|
295
295
|
```
|
|
296
296
|
|
|
297
|
+
## Generators
|
|
298
|
+
|
|
299
|
+
### Dynamic Pages Generator
|
|
300
|
+
Automatically generate pages from collection data based on frontmatter fields. This is useful for creating category, tag, or any taxonomy pages dynamically without manually creating each page.
|
|
301
|
+
|
|
302
|
+
#### Configuration
|
|
303
|
+
Add to your `_config.yml`:
|
|
304
|
+
|
|
305
|
+
```yaml
|
|
306
|
+
generators:
|
|
307
|
+
collection_categories:
|
|
308
|
+
# Example 1: Extract category from nested frontmatter field
|
|
309
|
+
- collection: recipes
|
|
310
|
+
field: recipe.cuisine # Dot notation for nested fields
|
|
311
|
+
layout: recipe-category
|
|
312
|
+
permalink: /recipes/:slug
|
|
313
|
+
title: ":name Recipes"
|
|
314
|
+
description: "Browse our collection of :name recipes."
|
|
315
|
+
|
|
316
|
+
# Example 2: Simple frontmatter field
|
|
317
|
+
- collection: products
|
|
318
|
+
field: category
|
|
319
|
+
layout: product-category
|
|
320
|
+
permalink: /products/:slug
|
|
321
|
+
title: ":name Products"
|
|
322
|
+
description: "Shop our :name products."
|
|
323
|
+
|
|
324
|
+
# Example 3: Minimal config (uses defaults)
|
|
325
|
+
- collection: articles
|
|
326
|
+
field: category
|
|
327
|
+
layout: article-category
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### Template Variables
|
|
331
|
+
Use these placeholders in `title`, `description`, and `permalink`:
|
|
332
|
+
- `:name` - The formatted category name (e.g., "Asian Cuisine")
|
|
333
|
+
- `:slug` - The URL-safe slug (e.g., "asian-cuisine")
|
|
334
|
+
|
|
335
|
+
#### Defaults
|
|
336
|
+
- `permalink`: `/:collection/:slug` (e.g., `/recipes/asian`)
|
|
337
|
+
- `title`: `:name`
|
|
338
|
+
- `description`: `Browse our collection of :name.`
|
|
339
|
+
|
|
340
|
+
#### Generated Page Data
|
|
341
|
+
Each generated page includes the following data accessible in the layout:
|
|
342
|
+
- `page.title` - The formatted title
|
|
343
|
+
- `page.description` - The formatted description
|
|
344
|
+
- `page.category_slug` - URL-safe slug (e.g., "asian-cuisine")
|
|
345
|
+
- `page.category_name` - Human-readable name (e.g., "Asian Cuisine")
|
|
346
|
+
- `page.collection_name` - Source collection name (e.g., "recipes")
|
|
347
|
+
- `page.meta.title` - SEO title with site name appended
|
|
348
|
+
- `page.meta.description` - SEO description
|
|
349
|
+
|
|
350
|
+
#### Example Layout
|
|
351
|
+
Create a layout file (e.g., `_layouts/recipe-category.html`) to display the category page:
|
|
352
|
+
|
|
353
|
+
```liquid
|
|
354
|
+
---
|
|
355
|
+
layout: default
|
|
356
|
+
---
|
|
357
|
+
<h1>{{ page.category_name }}</h1>
|
|
358
|
+
<p>{{ page.description }}</p>
|
|
359
|
+
|
|
360
|
+
{% assign items = site.recipes | where_exp: "item", "item.recipe.cuisine == page.category_name" %}
|
|
361
|
+
{% for item in items %}
|
|
362
|
+
<article>
|
|
363
|
+
<h2><a href="{{ item.url }}">{{ item.title }}</a></h2>
|
|
364
|
+
</article>
|
|
365
|
+
{% endfor %}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Development Config (`_config.dev.yml`)
|
|
369
|
+
Speed up dev builds by limiting collections. Create `_config.dev.yml` in your Jekyll source:
|
|
370
|
+
|
|
371
|
+
```yaml
|
|
372
|
+
limit_collections:
|
|
373
|
+
recipes: 50
|
|
374
|
+
products: 20
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Run with: `bundle exec jekyll serve --config _config.yml,_config.dev.yml`
|
|
378
|
+
|
|
379
|
+
UJ auto-loads this file in dev mode.
|
|
380
|
+
|
|
297
381
|
## Final notes
|
|
298
382
|
These examples show how you can use the features of `jekyll-uj-powertools` in your Jekyll site.
|
|
299
383
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Dynamic Pages Generator
|
|
2
|
+
# Automatically generates pages from collection data based on config
|
|
3
|
+
#
|
|
4
|
+
# Usage in _config.yml:
|
|
5
|
+
#
|
|
6
|
+
# generators:
|
|
7
|
+
# collection_categories:
|
|
8
|
+
# # Example 1: Extract category from frontmatter field
|
|
9
|
+
# - collection: recipes
|
|
10
|
+
# field: recipe.cuisine # Dot notation for nested fields
|
|
11
|
+
# layout: recipe-category
|
|
12
|
+
# permalink: /recipes/:slug
|
|
13
|
+
# title: ":name Recipes"
|
|
14
|
+
# description: "Browse our collection of :name recipes."
|
|
15
|
+
#
|
|
16
|
+
# # Example 2: Simple frontmatter field
|
|
17
|
+
# - collection: products
|
|
18
|
+
# field: category
|
|
19
|
+
# layout: product-category
|
|
20
|
+
# permalink: /products/:slug
|
|
21
|
+
# title: ":name Products"
|
|
22
|
+
# description: "Shop our :name products."
|
|
23
|
+
#
|
|
24
|
+
# # Example 3: Minimal config (uses defaults)
|
|
25
|
+
# - collection: articles
|
|
26
|
+
# field: category
|
|
27
|
+
# layout: article-category
|
|
28
|
+
#
|
|
29
|
+
# Template variables for title/description/permalink:
|
|
30
|
+
# :name - The formatted category name (e.g., "Asian Cuisine")
|
|
31
|
+
# :slug - The URL-safe slug (e.g., "asian-cuisine")
|
|
32
|
+
#
|
|
33
|
+
# Defaults:
|
|
34
|
+
# permalink: /:collection/:slug (e.g., /recipes/asian)
|
|
35
|
+
# title: :name
|
|
36
|
+
# description: "Browse our collection of :name."
|
|
37
|
+
|
|
38
|
+
module Jekyll
|
|
39
|
+
class DynamicPagesGenerator < Generator
|
|
40
|
+
safe true
|
|
41
|
+
priority :normal # Run before InjectProperties (:low) so dynamic pages get resolved data
|
|
42
|
+
|
|
43
|
+
def generate(site)
|
|
44
|
+
config = site.config.dig('generators', 'collection_categories')
|
|
45
|
+
return unless config.is_a?(Array)
|
|
46
|
+
|
|
47
|
+
config.each do |category_config|
|
|
48
|
+
generate_category_pages(site, category_config)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def generate_category_pages(site, config)
|
|
55
|
+
collection_name = config['collection']
|
|
56
|
+
field = config['field']
|
|
57
|
+
return unless collection_name && field
|
|
58
|
+
|
|
59
|
+
collection = site.collections[collection_name]
|
|
60
|
+
return unless collection
|
|
61
|
+
|
|
62
|
+
# Configuration with defaults
|
|
63
|
+
layout = config['layout']
|
|
64
|
+
permalink_template = config['permalink'] || "/#{collection_name}/:slug"
|
|
65
|
+
title_template = config['title'] || ':name'
|
|
66
|
+
description_template = config['description'] || 'Browse our collection of :name.'
|
|
67
|
+
|
|
68
|
+
# Extract unique categories from documents
|
|
69
|
+
categories = {}
|
|
70
|
+
|
|
71
|
+
collection.docs.each do |doc|
|
|
72
|
+
category_value = dig_value(doc.data, field)
|
|
73
|
+
next unless category_value && !category_value.empty?
|
|
74
|
+
|
|
75
|
+
category_slug = slugify(category_value)
|
|
76
|
+
next if categories.key?(category_slug)
|
|
77
|
+
|
|
78
|
+
# Format category name: titleize
|
|
79
|
+
category_name = titleize(category_value)
|
|
80
|
+
|
|
81
|
+
# Apply templates
|
|
82
|
+
permalink = apply_template(permalink_template, category_name, category_slug)
|
|
83
|
+
title = apply_template(title_template, category_name, category_slug)
|
|
84
|
+
description = apply_template(description_template, category_name, category_slug)
|
|
85
|
+
|
|
86
|
+
categories[category_slug] = {
|
|
87
|
+
'slug' => category_slug,
|
|
88
|
+
'name' => category_name,
|
|
89
|
+
'title' => title,
|
|
90
|
+
'description' => description,
|
|
91
|
+
'permalink' => permalink
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Generate a page for each category
|
|
96
|
+
categories.each do |slug, data|
|
|
97
|
+
page = DynamicCategoryPage.new(site, layout, data, collection_name)
|
|
98
|
+
site.pages << page
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
Jekyll.logger.info "DynamicPages:", "Generated #{categories.size} category pages for '#{collection_name}'"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Dig into nested hash using dot notation (e.g., "recipe.cuisine")
|
|
105
|
+
def dig_value(hash, field)
|
|
106
|
+
keys = field.split('.')
|
|
107
|
+
value = hash
|
|
108
|
+
keys.each do |key|
|
|
109
|
+
return nil unless value.is_a?(Hash)
|
|
110
|
+
value = value[key]
|
|
111
|
+
end
|
|
112
|
+
value.is_a?(String) ? value : nil
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def slugify(str)
|
|
116
|
+
str.to_s.downcase.strip.gsub(/[^\w\s-]/, '').gsub(/[\s_]+/, '-')
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def titleize(str)
|
|
120
|
+
str.to_s.split(/[\s_-]+/).map(&:capitalize).join(' ')
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def apply_template(template, name, slug)
|
|
124
|
+
template.gsub(':name', name).gsub(':slug', slug)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Custom page class for dynamically generated category pages
|
|
129
|
+
class DynamicCategoryPage < Page
|
|
130
|
+
def initialize(site, layout, data, collection_name)
|
|
131
|
+
@site = site
|
|
132
|
+
@base = site.source
|
|
133
|
+
@dir = ''
|
|
134
|
+
@name = "#{data['slug']}.html"
|
|
135
|
+
|
|
136
|
+
self.process(@name)
|
|
137
|
+
self.data = {
|
|
138
|
+
'layout' => layout,
|
|
139
|
+
'title' => data['title'],
|
|
140
|
+
'description' => data['description'],
|
|
141
|
+
'category_slug' => data['slug'],
|
|
142
|
+
'category_name' => data['name'],
|
|
143
|
+
'collection_name' => collection_name,
|
|
144
|
+
'permalink' => data['permalink'],
|
|
145
|
+
'meta' => {
|
|
146
|
+
'title' => "#{data['title']} - #{site.config.dig('brand', 'name') || site.config['title']}",
|
|
147
|
+
'description' => data['description']
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Limit Collections Generator
|
|
2
|
+
# Limits the number of documents in collections during development for faster builds
|
|
3
|
+
#
|
|
4
|
+
# Usage in _config.dev.yml:
|
|
5
|
+
# limit_collections:
|
|
6
|
+
# recipes: 50
|
|
7
|
+
# products: 20
|
|
8
|
+
#
|
|
9
|
+
# By default, uses a seeded random sample for diverse selection across categories.
|
|
10
|
+
# To disable randomization and take first N documents:
|
|
11
|
+
# limit_collections:
|
|
12
|
+
# recipes: 50
|
|
13
|
+
# products: 20
|
|
14
|
+
# randomize: false
|
|
15
|
+
#
|
|
16
|
+
# Run Jekyll with: bundle exec jekyll serve --config _config.yml,_config.dev.yml
|
|
17
|
+
|
|
18
|
+
module Jekyll
|
|
19
|
+
class LimitCollectionsGenerator < Generator
|
|
20
|
+
safe true
|
|
21
|
+
priority :highest # Run before other generators
|
|
22
|
+
|
|
23
|
+
def generate(site)
|
|
24
|
+
limits = site.config['limit_collections']
|
|
25
|
+
return unless limits.is_a?(Hash)
|
|
26
|
+
|
|
27
|
+
# Check if randomization is disabled (default: true)
|
|
28
|
+
randomize = limits.fetch('randomize', true)
|
|
29
|
+
|
|
30
|
+
limits.each do |collection_name, limit|
|
|
31
|
+
# Skip the 'randomize' option itself
|
|
32
|
+
next if collection_name == 'randomize'
|
|
33
|
+
next unless limit.is_a?(Integer) && limit > 0
|
|
34
|
+
|
|
35
|
+
collection = site.collections[collection_name]
|
|
36
|
+
next unless collection
|
|
37
|
+
|
|
38
|
+
original_count = collection.docs.size
|
|
39
|
+
next if original_count <= limit
|
|
40
|
+
|
|
41
|
+
if randomize
|
|
42
|
+
# Use a seeded random for repeatable but diverse sampling
|
|
43
|
+
# Seed based on collection name so it's consistent across rebuilds
|
|
44
|
+
rng = Random.new(collection_name.hash.abs)
|
|
45
|
+
sampled_docs = collection.docs.shuffle(random: rng).first(limit)
|
|
46
|
+
collection.docs.replace(sampled_docs)
|
|
47
|
+
Jekyll.logger.info "LimitCollections:", "Limited '#{collection_name}' from #{original_count} to #{limit} documents (random sample)"
|
|
48
|
+
else
|
|
49
|
+
# Take first N documents in order
|
|
50
|
+
collection.docs.replace(collection.docs.first(limit))
|
|
51
|
+
Jekyll.logger.info "LimitCollections:", "Limited '#{collection_name}' from #{original_count} to #{limit} documents"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/jekyll-uj-powertools.rb
CHANGED
|
@@ -6,8 +6,10 @@ module Jekyll
|
|
|
6
6
|
require_relative "filters/main"
|
|
7
7
|
|
|
8
8
|
# Load Generators
|
|
9
|
+
require_relative "generators/limit-collections"
|
|
9
10
|
require_relative "generators/inject-properties"
|
|
10
11
|
require_relative "generators/blog-taxonomy"
|
|
12
|
+
require_relative "generators/dynamic-pages"
|
|
11
13
|
|
|
12
14
|
# Load Hooks
|
|
13
15
|
require_relative "hooks/inject-properties"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-uj-powertools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ITW Creative Works
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-12-
|
|
11
|
+
date: 2025-12-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jekyll
|
|
@@ -115,7 +115,9 @@ files:
|
|
|
115
115
|
- jekyll-uj-powertools.gemspec
|
|
116
116
|
- lib/filters/main.rb
|
|
117
117
|
- lib/generators/blog-taxonomy.rb
|
|
118
|
+
- lib/generators/dynamic-pages.rb
|
|
118
119
|
- lib/generators/inject-properties.rb
|
|
120
|
+
- lib/generators/limit-collections.rb
|
|
119
121
|
- lib/helpers/variable_resolver.rb
|
|
120
122
|
- lib/hooks/inject-properties.rb
|
|
121
123
|
- lib/hooks/markdown-images.rb
|