perron 0.12.0 → 0.13.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/Gemfile.lock +1 -1
- data/README.md +2 -482
- data/lib/generators/content/USAGE +16 -0
- data/lib/generators/content/content_generator.rb +1 -1
- data/lib/generators/perron/install_generator.rb +2 -0
- data/lib/perron/resource/table_of_content.rb +58 -0
- data/lib/perron/resource.rb +10 -2
- data/lib/perron/site/builder/feeds/json.rb +1 -0
- data/lib/perron/site/builder/feeds/rss.rb +1 -1
- data/lib/perron/site/builder/paths.rb +4 -0
- data/lib/perron/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a0f1b173ae94ac69df1bf9fe680062fa7d07ef6af132677313e80a1d909cb68
|
4
|
+
data.tar.gz: 6e6c7865e13a312b6bdac601d6d8878f13136c8d70734ceb5c03fa5ea8d83159
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '066983a2578dabbc1d1033f8e9d36065ff03cd9d46fde15cc77348086c114e23c9052aff9e2087bc7e429584bcdb99821bdb1195ba908053f7b5d80966b6133c'
|
7
|
+
data.tar.gz: 98eafaeed24b4395c55f4847cf672941f99f551833f15c29cf6a8e3974c8ff0d4da7facc6af87e400e95449b6b76687f3f3c89e14ee3ef60cd1b89583271f0f1
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -13,489 +13,9 @@ A Rails-based static site generator.
|
|
13
13
|
</a>
|
14
14
|
|
15
15
|
|
16
|
-
##
|
16
|
+
## Documentation
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
Start by adding Perron:
|
21
|
-
```bash
|
22
|
-
bundle add perron
|
23
|
-
```
|
24
|
-
|
25
|
-
Then generate the initializer:
|
26
|
-
```bash
|
27
|
-
rails generate perron:install
|
28
|
-
```
|
29
|
-
|
30
|
-
|
31
|
-
This creates an initializer:
|
32
|
-
```ruby
|
33
|
-
Perron.configure do |config|
|
34
|
-
config.site_name = "Helptail"
|
35
|
-
end
|
36
|
-
```
|
37
|
-
|
38
|
-
|
39
|
-
## Mode
|
40
|
-
|
41
|
-
Perron can operate in two modes, configured via `config.mode`. This allows a build to be either a full static site or be integrated pages in a dynamic Rails application.
|
42
|
-
|
43
|
-
| **Mode** | `:standalone` (default) | `:integrated` |
|
44
|
-
| :--- | :--- | :--- |
|
45
|
-
| **Use Case** | Full static site for hosts like Netlify/Vercel | Add static pages to a live Rails app |
|
46
|
-
| **Output** | `output/` directory | `public/` directory |
|
47
|
-
| **Asset Handling** | Via Perron | Via Asset Pipeline |
|
48
|
-
|
49
|
-
|
50
|
-
## Collections
|
51
|
-
|
52
|
-
Perron is, just like Rails, designed with convention over configuration in mind. Content is stored in `app/content/*/*.{erb,md,*}` and backed by a class, located in `app/models/content/` that inherits from `Perron::Resource`.
|
53
|
-
|
54
|
-
The controllers are located in `app/controllers/content/`. To make them available, create a route: `resources :posts, module: :content, only: %w[index show]`.
|
55
|
-
|
56
|
-
|
57
|
-
### Create a new collection
|
58
|
-
|
59
|
-
```bash
|
60
|
-
bin/rails generate content Post
|
61
|
-
```
|
62
|
-
|
63
|
-
This will create the following files:
|
64
|
-
|
65
|
-
* `app/models/content/post.rb`
|
66
|
-
* `app/controllers/content/posts_controller.rb`
|
67
|
-
* `app/views/content/posts/index.html.erb`
|
68
|
-
* `app/views/content/posts/show.html.erb`
|
69
|
-
|
70
|
-
And adds a route: `resources :posts, module: :content, only: %w[index show]`
|
71
|
-
|
72
|
-
|
73
|
-
### Routes
|
74
|
-
|
75
|
-
Perron uses standard Rails routing, allowing the use of familiar route helpers. For a typical “clean slug”, the filename without extensions serves as the `id` parameter (slug).
|
76
|
-
```ruby
|
77
|
-
<%# For app/content/pages/about.md %>
|
78
|
-
<%= link_to "About Us", page_path("about") # => <a href="/about/">About Us</a> %>
|
79
|
-
```
|
80
|
-
|
81
|
-
To create files with specific extensions directly (e.g., `pricing.html`), the route must first be configured to treat the entire filename as the ID. In `config/routes.rb`, modify the generated `resources` line by adding a `constraints` option:
|
82
|
-
|
83
|
-
```ruby
|
84
|
-
# Change from…
|
85
|
-
resources :pages, path: "/", module: :content, only: %w[show]
|
86
|
-
|
87
|
-
# …to…
|
88
|
-
resources :pages, path: "/", module: :content, only: %w[show], constraints: { id: /[^\/]+/ }
|
89
|
-
```
|
90
|
-
|
91
|
-
With this change, a content file named `app/content/pages/pricing.html.erb` can be linked using the full filename. The builder will then create `pricing.html` in the output directory.
|
92
|
-
```ruby
|
93
|
-
<%= link_to "View Pricing", page_path("pricing", format: :html) # => <a href="/pricing.html">View Pricing</a> %>
|
94
|
-
```
|
95
|
-
|
96
|
-
|
97
|
-
### Setting a root page
|
98
|
-
|
99
|
-
To set a root page, include `Perron::Root` in your `Content::PagesController` and add a `app/content/pages/root.{md,erb,*}` file. Then add `root to: "content/pages#root"` add the bottom of your `config/routes.erb`.
|
100
|
-
|
101
|
-
This is automatically added for you when you create a `Page` collection.
|
102
|
-
|
103
|
-
|
104
|
-
## Markdown support
|
105
|
-
|
106
|
-
Perron supports markdown with the `markdownify` helper.
|
107
|
-
|
108
|
-
There are no markdown gems bundled by default, so you'll need to add one of these to your `Gemfile`:
|
109
|
-
|
110
|
-
- `commonmarker`
|
111
|
-
- `kramdown`
|
112
|
-
- `redcarpet`
|
113
|
-
|
114
|
-
```bash
|
115
|
-
bundle add {commonmarker,kramdown,redcarpet}
|
116
|
-
```
|
117
|
-
|
118
|
-
### Configuration
|
119
|
-
|
120
|
-
To pass options to the parser, set `markdown_options` in `config/initializers/perron.rb`. The options hash is passed directly to the chosen library.
|
121
|
-
|
122
|
-
**Commonmarker**
|
123
|
-
```ruby
|
124
|
-
# Options are passed as keyword arguments.
|
125
|
-
Perron.configuration.markdown_options = { options: [:HARDBREAKS], extensions: [:table] }
|
126
|
-
```
|
127
|
-
|
128
|
-
**Kramdown**
|
129
|
-
```ruby
|
130
|
-
# Options are passed as a standard hash.
|
131
|
-
Perron.configuration.markdown_options = { input: "GFM", smart_quotes: "apos,quot" }
|
132
|
-
```
|
133
|
-
|
134
|
-
**Redcarpet**
|
135
|
-
```ruby
|
136
|
-
# Options are nested under :renderer_options and :markdown_options.
|
137
|
-
Perron.configuration.markdown_options = {
|
138
|
-
renderer_options: { hard_wrap: true },
|
139
|
-
markdown_options: { tables: true, autolink: true }
|
140
|
-
}
|
141
|
-
```
|
142
|
-
|
143
|
-
|
144
|
-
## HTML transformations
|
145
|
-
|
146
|
-
Perron can post-process the HTML generated from your Markdown content.
|
147
|
-
|
148
|
-
|
149
|
-
### Usage
|
150
|
-
|
151
|
-
Apply transformations by passing an array of processor names or classes to the `markdownify` helper via the `process` option.
|
152
|
-
```erb
|
153
|
-
<%= markdownify @resource.content, process: %w[lazy_load_images syntax_highlight target_blank] %>
|
154
|
-
```
|
155
|
-
|
156
|
-
|
157
|
-
### Available processors
|
158
|
-
|
159
|
-
The following processors are built-in and can be activated by passing their string name:
|
160
|
-
|
161
|
-
- `target_blank`: Adds `target="_blank"` to all external links;
|
162
|
-
- `lazy_load_images`: Adds `loading="lazy"` to all `<img>` tags.
|
163
|
-
- `syntax_highlight`: Applies syntax highlighting to fenced code blocks (e.g., \`\`\`ruby). This requires adding the `rouge` gem to your Gemfile (`bundle add rouge`). You will also need to include a Rouge CSS theme for colors to appear.
|
164
|
-
|
165
|
-
|
166
|
-
### Creating your own processors
|
167
|
-
|
168
|
-
You can create your own processor by defining a class that inherits from `Perron::HtmlProcessor::Base` and implements a `process` method.
|
169
|
-
Then, pass the class constant directly in the `process` array.
|
170
|
-
|
171
|
-
```ruby
|
172
|
-
# app/processors/add_nofollow_processor.rb
|
173
|
-
class AddNofollowProcessor < Perron::HtmlProcessor::Base
|
174
|
-
def process
|
175
|
-
@html.css("a[target=_blank]").each { it["rel"] = "nofollow" }
|
176
|
-
end
|
177
|
-
end
|
178
|
-
```
|
179
|
-
|
180
|
-
```erb
|
181
|
-
<%= markdownify @resource.content, process: ["target_blank", AddNofollowProcessor] %>
|
182
|
-
```
|
183
|
-
|
184
|
-
|
185
|
-
### Embed Ruby
|
186
|
-
|
187
|
-
Perron provides flexible options for embedding dynamic Ruby code in your content using ERB.
|
188
|
-
|
189
|
-
|
190
|
-
#### 1. File extension
|
191
|
-
|
192
|
-
Any content file with a `.erb` extension (e.g., `about.erb`) will automatically have its content processed as ERB.
|
193
|
-
|
194
|
-
|
195
|
-
#### 2. Frontmatter
|
196
|
-
|
197
|
-
You can enable ERB processing on a per-file basis, even for standard `.md` files, by adding `erb: true` to the file's frontmatter.
|
198
|
-
```markdown
|
199
|
-
---
|
200
|
-
title: Dynamic Page
|
201
|
-
erb: true
|
202
|
-
---
|
203
|
-
|
204
|
-
This entire page will be processed by ERB.
|
205
|
-
The current time is: <%= Time.current.to_fs(:long_ordinal) %>.
|
206
|
-
```
|
207
|
-
|
208
|
-
|
209
|
-
#### 3. `erbify` helper
|
210
|
-
|
211
|
-
For the most granular control, the `erbify` helper allows to process specific sections of a file as ERB.
|
212
|
-
This is ideal for generating dynamic content like lists or tables from your resource's metadata, without needing to enable ERB for the entire file. The `erbify` helper can be used with a string or, more commonly, a block.
|
213
|
-
|
214
|
-
**Example:** Generating a list from frontmatter data in a standard `.md` file.
|
215
|
-
```markdown
|
216
|
-
---
|
217
|
-
title: Features
|
218
|
-
features:
|
219
|
-
- Rails based
|
220
|
-
- SEO friendly
|
221
|
-
- Markdown first
|
222
|
-
- ERB support
|
223
|
-
---
|
224
|
-
|
225
|
-
Check out our amazing features:
|
226
|
-
|
227
|
-
<%= erbify do %>
|
228
|
-
<ul>
|
229
|
-
<% @resource.metadata.features.each do |feature| %>
|
230
|
-
<li>
|
231
|
-
<%= feature %>
|
232
|
-
</li>
|
233
|
-
<% end %>
|
234
|
-
</ul>
|
235
|
-
<% end %>
|
236
|
-
```
|
237
|
-
|
238
|
-
|
239
|
-
## Data files
|
240
|
-
|
241
|
-
Perron can consume structured data from YML, JSON, or CSV files, making them available within your templates.
|
242
|
-
This is useful for populating features, team members, or any other repeated data structure.
|
243
|
-
|
244
|
-
### Usage
|
245
|
-
|
246
|
-
To use a data file, instantiate `Perron::Site.data` with the basename of the file and iterate over the result.
|
247
|
-
```erb
|
248
|
-
<% Perron::Site.data.features.each do |feature| %>
|
249
|
-
<h4><%= feature.name %></h4>
|
250
|
-
<p><%= feature.description %></p>
|
251
|
-
<% end %>
|
252
|
-
```
|
253
|
-
|
254
|
-
### File location and formats
|
255
|
-
|
256
|
-
By default, Perron looks up `app/content/data/` for files with a `.yml`, `.json`, or `.csv` extension.
|
257
|
-
For a `features` call, it would find `features.yml`, `features.json`, or `features.csv`. You can also provide a path to any data file, via `Perron::Data.new("path/to/data.json")`.
|
258
|
-
|
259
|
-
### Accessing data
|
260
|
-
|
261
|
-
The wrapper object provides flexible, read-only access to each record's attributes. Both dot notation and hash-like key access are supported.
|
262
|
-
```ruby
|
263
|
-
feature.name
|
264
|
-
feature[:name]
|
265
|
-
```
|
266
|
-
|
267
|
-
### Rendering
|
268
|
-
|
269
|
-
You can render data collections directly using Rails-like partial rendering. When you call `render` on a data collection, Perron will automatically render a partial for each item.
|
270
|
-
```erb
|
271
|
-
<%= render Perron::Site.data.features %>
|
272
|
-
```
|
273
|
-
|
274
|
-
This expects a partial at `app/views/content/features/_feature.html.erb` that will be rendered once for each feature in your data file. The individual record is made available as a local variable matching the singular form of the collection name.
|
275
|
-
```erb
|
276
|
-
<!-- app/views/content/features/_feature.html.erb -->
|
277
|
-
<div class="feature">
|
278
|
-
<h4><%= feature.name %></h4>
|
279
|
-
<p><%= feature.description %></p>
|
280
|
-
</div>
|
281
|
-
```
|
282
|
-
|
283
|
-
|
284
|
-
## Feeds
|
285
|
-
|
286
|
-
The `feeds` helper automatically generates HTML `<link>` tags for your site's RSS and JSON feeds.
|
287
|
-
|
288
|
-
|
289
|
-
### Usage
|
290
|
-
|
291
|
-
In your layout (e.g., `app/views/layouts/application.html.erb`), add the helper to the `<head>` section:
|
292
|
-
```erb
|
293
|
-
<head>
|
294
|
-
…
|
295
|
-
<%= feeds %>
|
296
|
-
…
|
297
|
-
</head>
|
298
|
-
```
|
299
|
-
|
300
|
-
To render feeds for specific collections, such as `posts`:
|
301
|
-
```erb
|
302
|
-
<%= feeds only: %w[posts] %>
|
303
|
-
```
|
304
|
-
|
305
|
-
Similarly, you can exclude collections:
|
306
|
-
```erb
|
307
|
-
<%= feeds except: %w[pages] %>
|
308
|
-
```
|
309
|
-
|
310
|
-
|
311
|
-
### Configuration
|
312
|
-
|
313
|
-
Feeds are configured within the `Resource` class corresponding to a collection:
|
314
|
-
```ruby
|
315
|
-
# app/models/content/post.rb
|
316
|
-
class Content::Post < Perron::Resource
|
317
|
-
configure do |config|
|
318
|
-
config.feeds.rss.enabled = true
|
319
|
-
# config.feeds.rss.title = "My RSS feed" # defaults to configured site_name
|
320
|
-
# config.feeds.rss.description = "My RSS feed description" # defaults to configured site_description
|
321
|
-
# config.feeds.rss.path = "path-to-feed.xml"
|
322
|
-
# config.feeds.rss.max_items = 25
|
323
|
-
#
|
324
|
-
config.feeds.json.enabled = true
|
325
|
-
# config.feeds.json.title = "My JSON feed" # defaults to configured site_name
|
326
|
-
# config.feeds.json.description = "My JSON feed description" # defaults to configured site_description
|
327
|
-
# config.feeds.json.max_items = 15
|
328
|
-
# config.feeds.json.path = "path-to-feed.json"
|
329
|
-
end
|
330
|
-
end
|
331
|
-
```
|
332
|
-
|
333
|
-
|
334
|
-
## Metatags
|
335
|
-
|
336
|
-
The `meta_tags` helper automatically generates SEO and social sharing meta tags for your pages.
|
337
|
-
|
338
|
-
### Usage
|
339
|
-
|
340
|
-
In your layout (e.g., `app/views/layouts/application.html.erb`), add the helper to the `<head>` section:
|
341
|
-
```erb
|
342
|
-
<head>
|
343
|
-
…
|
344
|
-
<%= meta_tags %>
|
345
|
-
…
|
346
|
-
</head>
|
347
|
-
```
|
348
|
-
|
349
|
-
You can render specific subsets of tags:
|
350
|
-
```erb
|
351
|
-
<%= meta_tags only: %w[title description] %>
|
352
|
-
```
|
353
|
-
|
354
|
-
Or exclude certain tags:
|
355
|
-
```erb
|
356
|
-
<%= meta_tags except: %w[twitter_card twitter_image] %>
|
357
|
-
```
|
358
|
-
|
359
|
-
### Priority
|
360
|
-
|
361
|
-
Values are determined with the following precedence, from highest to lowest:
|
362
|
-
|
363
|
-
#### 1. Controller action
|
364
|
-
|
365
|
-
Define a `@metadata` instance variable in your controller:
|
366
|
-
```ruby
|
367
|
-
class Content::PostsController < ApplicationController
|
368
|
-
def index
|
369
|
-
@metadata = {
|
370
|
-
title: "All Blog Posts",
|
371
|
-
description: "A collection of our articles."
|
372
|
-
}
|
373
|
-
@resources = Content::Post.all
|
374
|
-
end
|
375
|
-
end
|
376
|
-
```
|
377
|
-
|
378
|
-
#### 2. Page frontmatter
|
379
|
-
|
380
|
-
Add values to the YAML frontmatter in content files:
|
381
|
-
```yaml
|
382
|
-
---
|
383
|
-
title: My Awesome Post
|
384
|
-
description: A deep dive into how meta tags work.
|
385
|
-
image: /assets/images/my-awesome-post.png
|
386
|
-
author: Kendall
|
387
|
-
---
|
388
|
-
|
389
|
-
Your content here…
|
390
|
-
```
|
391
|
-
|
392
|
-
#### 3. Collection configuration
|
393
|
-
|
394
|
-
Set collection defaults in the resource model:
|
395
|
-
```ruby
|
396
|
-
class Content::Post < Perron::Resource
|
397
|
-
Perron.configure do |config|
|
398
|
-
# …
|
399
|
-
|
400
|
-
config.metadata.description = "Put your routine tasks on autopilot"
|
401
|
-
config.metadata.author = "Helptail team"
|
402
|
-
end
|
403
|
-
end
|
404
|
-
```
|
405
|
-
|
406
|
-
#### 4. Default values
|
407
|
-
|
408
|
-
Set site-wide defaults in the initializer:
|
409
|
-
```ruby
|
410
|
-
Perron.configure do |config|
|
411
|
-
# …
|
412
|
-
|
413
|
-
config.metadata.description = "Put your routine tasks on autopilot"
|
414
|
-
config.metadata.author = "Helptail team"
|
415
|
-
end
|
416
|
-
```
|
417
|
-
|
418
|
-
|
419
|
-
## Related resources
|
420
|
-
|
421
|
-
The `related_resources` method allows to find and display a list of similar resources
|
422
|
-
from the sme collection. Similarity is calculated using the **[TF-IDF](https://en.wikipedia.org/wiki/Tf%E2%80%93idf)** algorithm on the content of each resource.
|
423
|
-
|
424
|
-
|
425
|
-
### Basic usage
|
426
|
-
|
427
|
-
To get a list of the 5 most similar resources, call the method on any resource instance.
|
428
|
-
```ruby
|
429
|
-
# app/views/content/posts/show.html.erb
|
430
|
-
@resource.related_resources
|
431
|
-
|
432
|
-
# Just the 3 most similar resources
|
433
|
-
@resource.related_resources(limit: 3)
|
434
|
-
```
|
435
|
-
|
436
|
-
|
437
|
-
## XML sitemap
|
438
|
-
|
439
|
-
A sitemap is a XML file that lists all the pages of a website to help search engines discover and index content more efficiently, typically containing URLs, last modification dates, change frequency, and priority values.
|
440
|
-
|
441
|
-
Enable it with the following line in the Perron configuration:
|
442
|
-
```ruby
|
443
|
-
Perron.configure do |config|
|
444
|
-
# …
|
445
|
-
config.sitemap.enabled = true
|
446
|
-
# config.sitemap.priority = 0.8
|
447
|
-
# config.sitemap.change_frequency = :monthly
|
448
|
-
# …
|
449
|
-
end
|
450
|
-
```
|
451
|
-
|
452
|
-
Values can be overridden per collection…
|
453
|
-
```ruby
|
454
|
-
# app/models/content/post.rb
|
455
|
-
class Content::Post < Perron::Resource
|
456
|
-
configure do |config|
|
457
|
-
config.sitemap.enabled = false
|
458
|
-
config.sitemap.priority = 0.5
|
459
|
-
config.sitemap.change_frequency = :weekly
|
460
|
-
end
|
461
|
-
end
|
462
|
-
```
|
463
|
-
|
464
|
-
…or on a resource basis:
|
465
|
-
```ruby
|
466
|
-
# app/content/posts/my-first-post.md
|
467
|
-
---
|
468
|
-
sitemap_priority: 0.25
|
469
|
-
sitemap_change_frequency: :daily
|
470
|
-
---
|
471
|
-
```
|
472
|
-
|
473
|
-
|
474
|
-
## Building your static site
|
475
|
-
|
476
|
-
When in `standalone` mode and you're ready to generate your static site, run:
|
477
|
-
```bash
|
478
|
-
RAILS_ENV=production rails perron:build
|
479
|
-
```
|
480
|
-
|
481
|
-
This will create your static site in the configured output directory (`output` by default).
|
482
|
-
|
483
|
-
|
484
|
-
## Sites using Perron
|
485
|
-
|
486
|
-
Sites that use Perron.
|
487
|
-
|
488
|
-
### Standalone (as a SSG)
|
489
|
-
- [AppRefresher](https://apprefresher.com)
|
490
|
-
- [Helptail](https://helptail.com)
|
491
|
-
|
492
|
-
### Integrated (part of a Rails app)
|
493
|
-
- [Rails Designers (private community for Rails UI engineers)](https://railsdesigners.com)
|
494
|
-
|
495
|
-
|
496
|
-
## Contributing
|
497
|
-
|
498
|
-
This project uses [Standard](https://github.com/testdouble/standard) for formatting Ruby code. Please run `be standardrb` before submitting pull requests. Run tests with `rails test`.
|
18
|
+
📑 [See the docs site (built with Perron!)](https://perron.railsdesigner.com/docs/)
|
499
19
|
|
500
20
|
|
501
21
|
## License
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Description:
|
2
|
+
Creates a new content model with specified actions
|
3
|
+
|
4
|
+
Example:
|
5
|
+
rails generate content post
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
- app/models/content/post.rb
|
9
|
+
- app/controllers/content/posts_controller.rb
|
10
|
+
- app/views/content/posts/index.html.erb
|
11
|
+
- app/views/content/posts/show.html.erb
|
12
|
+
- …and add `resource :posts, module: :content, only: %w[index show]` to `config/routes.rb`
|
13
|
+
|
14
|
+
Arguments:
|
15
|
+
NAME: Name of the content model
|
16
|
+
actions: List of actions to generate (index or show)
|
@@ -7,7 +7,7 @@ class ContentGenerator < Rails::Generators::NamedBase
|
|
7
7
|
|
8
8
|
class_option :force_plural, type: :boolean, default: false, desc: "Forces the use of a plural model name and class"
|
9
9
|
|
10
|
-
argument :actions, type: :array, default: %w[index show], desc: "Specify which actions to generate (index/show)"
|
10
|
+
argument :actions, type: :array, default: %w[index show], banner: "actions", desc: "Specify which actions to generate (index/show)"
|
11
11
|
|
12
12
|
def create_model
|
13
13
|
template "model.rb.tt", File.join("app/models/content", "#{file_name}.rb")
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Perron
|
4
|
+
class Resource
|
5
|
+
module TableOfContent
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def table_of_content(levels: %w[h1 h2 h3 h4 h5 h6])
|
9
|
+
return [] if content.blank? || metadata.toc == false
|
10
|
+
|
11
|
+
document = Nokogiri::HTML::DocumentFragment.parse(Markdown.render(content))
|
12
|
+
headings = extract_headings from: document, levels: levels.join(", ")
|
13
|
+
|
14
|
+
Builder.new.build(headings)
|
15
|
+
end
|
16
|
+
alias_method :table_of_contents, :table_of_content
|
17
|
+
alias_method :toc, :table_of_content
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
Item = ::Data.define(:id, :text, :level, :children)
|
22
|
+
|
23
|
+
def extract_headings(from:, levels:)
|
24
|
+
from.css(levels).each_with_object([]) do |heading, headings|
|
25
|
+
heading.tap do |node|
|
26
|
+
heading_text = node.text.strip
|
27
|
+
id = node["id"] || node.at("a")&.[]("id")
|
28
|
+
|
29
|
+
next if heading_text.empty? || id.blank?
|
30
|
+
|
31
|
+
headings << Item.new(
|
32
|
+
id: id,
|
33
|
+
text: heading_text,
|
34
|
+
level: node.name[1..].to_i,
|
35
|
+
children: []
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Builder
|
42
|
+
def build(headings)
|
43
|
+
parents = {0 => {children: []}}
|
44
|
+
|
45
|
+
headings.each_with_object(parents[0][:children]) do |heading, _|
|
46
|
+
parents.delete_if { |level, _| level >= heading.level }
|
47
|
+
|
48
|
+
parent = parents[parents.keys.select { it < heading.level }.max || 0]
|
49
|
+
|
50
|
+
(parent.is_a?(Hash) ? parent[:children] : parent.children) << heading
|
51
|
+
|
52
|
+
parents[heading.level] = heading
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/perron/resource.rb
CHANGED
@@ -9,6 +9,7 @@ require "perron/resource/related"
|
|
9
9
|
require "perron/resource/renderer"
|
10
10
|
require "perron/resource/slug"
|
11
11
|
require "perron/resource/separator"
|
12
|
+
require "perron/resource/table_of_content"
|
12
13
|
|
13
14
|
module Perron
|
14
15
|
class Resource
|
@@ -18,6 +19,7 @@ module Perron
|
|
18
19
|
include Perron::Resource::Core
|
19
20
|
include Perron::Resource::ClassMethods
|
20
21
|
include Perron::Resource::Publishable
|
22
|
+
include Perron::Resource::TableOfContent
|
21
23
|
|
22
24
|
attr_reader :file_path, :id
|
23
25
|
|
@@ -48,9 +50,9 @@ module Perron
|
|
48
50
|
def content
|
49
51
|
page_content = Perron::Resource::Separator.new(raw_content).content
|
50
52
|
|
51
|
-
return page_content
|
53
|
+
return Perron::Resource::Renderer.erb(page_content, resource: self) if erb_processing?
|
52
54
|
|
53
|
-
|
55
|
+
render_inline_erb using: page_content
|
54
56
|
end
|
55
57
|
|
56
58
|
def to_partial_path
|
@@ -79,6 +81,12 @@ module Perron
|
|
79
81
|
).first(ID_LENGTH)
|
80
82
|
end
|
81
83
|
|
84
|
+
def render_inline_erb(using:)
|
85
|
+
using.gsub(/<%=\s*erbify\s+do\s*%>(.*?)<%\s*end\s*%>/m) do
|
86
|
+
Perron::Resource::Renderer.erb(Regexp.last_match(1).strip_heredoc, resource: self)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
82
90
|
def erb_processing?
|
83
91
|
@file_path.ends_with?(".erb") || metadata.erb == true
|
84
92
|
end
|
@@ -17,6 +17,7 @@ module Perron
|
|
17
17
|
|
18
18
|
hash = Rails.application.routes.url_helpers.with_options(@configuration.default_url_options) do |url|
|
19
19
|
{
|
20
|
+
generator: "Perron (#{Perron::VERSION})",
|
20
21
|
version: "https://jsonfeed.org/version/1.1",
|
21
22
|
home_page_url: @configuration.url,
|
22
23
|
title: feed_configuration.title.presence || @configuration.site_name,
|
@@ -18,10 +18,10 @@ module Perron
|
|
18
18
|
Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
|
19
19
|
xml.rss(:version => "2.0", "xmlns:atom" => "http://www.w3.org/2005/Atom") do
|
20
20
|
xml.channel do
|
21
|
+
xml.generator "Perron (#{Perron::VERSION})"
|
21
22
|
xml.title feed_configuration.title.presence || @configuration.site_name
|
22
23
|
xml.description feed_configuration.description.presence || @configuration.site_description
|
23
24
|
xml.link @configuration.url
|
24
|
-
xml.generator "Perron (#{Perron::VERSION})"
|
25
25
|
|
26
26
|
Rails.application.routes.url_helpers.with_options(@configuration.default_url_options) do |url|
|
27
27
|
resources.each do |resource|
|
@@ -18,6 +18,10 @@ module Perron
|
|
18
18
|
next if skip? root
|
19
19
|
|
20
20
|
@paths << (root ? routes.root_path : routes.public_send(show_path, resource))
|
21
|
+
|
22
|
+
(resource.class.try(:nested_routes) || []).each do |nested|
|
23
|
+
@paths << routes.polymorphic_path([resource, nested])
|
24
|
+
end
|
21
25
|
end
|
22
26
|
end
|
23
27
|
end
|
data/lib/perron/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rails Designer Developers
|
@@ -86,6 +86,7 @@ files:
|
|
86
86
|
- bin/rails
|
87
87
|
- bin/release
|
88
88
|
- bin/setup
|
89
|
+
- lib/generators/content/USAGE
|
89
90
|
- lib/generators/content/content_generator.rb
|
90
91
|
- lib/generators/content/templates/controller.rb.tt
|
91
92
|
- lib/generators/content/templates/index.html.erb.tt
|
@@ -122,6 +123,7 @@ files:
|
|
122
123
|
- lib/perron/resource/renderer.rb
|
123
124
|
- lib/perron/resource/separator.rb
|
124
125
|
- lib/perron/resource/slug.rb
|
126
|
+
- lib/perron/resource/table_of_content.rb
|
125
127
|
- lib/perron/root.rb
|
126
128
|
- lib/perron/site.rb
|
127
129
|
- lib/perron/site/builder.rb
|