perron 0.18.0 → 1.1.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 +11 -15
- data/app/controllers/perron/concierge_controller.rb +16 -0
- data/app/helpers/perron/markdown_helper.rb +3 -2
- data/app/helpers/perron/meta_tags_helper.rb +3 -9
- data/app/helpers/perron/paginate_helper.rb +40 -0
- data/app/views/perron/concierge/show.html.erb +271 -0
- data/lib/generators/rails/content/USAGE +21 -4
- data/lib/generators/rails/content/content_generator.rb +16 -12
- data/lib/generators/rails/content/templates/controller.rb.tt +6 -0
- data/lib/generators/rails/content/templates/model.rb.tt +1 -1
- data/lib/perron/assets/icon.png +0 -0
- data/lib/perron/assets/icon.svg +1 -0
- data/lib/perron/collection.rb +2 -1
- data/lib/perron/configuration.rb +27 -2
- data/lib/perron/data_source/class_methods.rb +8 -0
- data/lib/perron/data_source.rb +20 -33
- data/lib/perron/development_feed_server.rb +69 -0
- data/lib/perron/engine.rb +32 -1
- data/lib/perron/errors.rb +2 -0
- data/lib/perron/feeds.rb +4 -3
- data/lib/perron/html_processor/absolute_urls.rb +27 -0
- data/lib/perron/html_processor/base.rb +2 -2
- data/lib/perron/html_processor.rb +7 -11
- data/lib/{generators/perron/templates → perron/install}/README.md.tt +7 -9
- data/lib/perron/install/deploy.yml +15 -0
- data/lib/perron/install.rb +26 -0
- data/lib/perron/markdown.rb +2 -2
- data/lib/perron/output_server.rb +9 -0
- data/lib/perron/paginate.rb +58 -0
- data/lib/perron/relation.rb +24 -6
- data/lib/perron/resource/adjacency.rb +70 -0
- data/lib/perron/resource/associations.rb +1 -1
- data/lib/perron/resource/class_methods.rb +6 -0
- data/lib/perron/resource/configuration.rb +12 -4
- data/lib/perron/resource/metadata.rb +19 -4
- data/lib/perron/resource/publishable.rb +2 -0
- data/lib/perron/resource/related.rb +32 -31
- data/lib/perron/resource/sourceable.rb +98 -16
- data/lib/perron/resource.rb +8 -0
- data/lib/perron/site/builder/assets.rb +1 -1
- data/lib/perron/site/builder/feeds/atom.erb +44 -0
- data/lib/perron/site/builder/feeds/atom.rb +41 -0
- data/lib/perron/site/builder/feeds/json.erb +19 -0
- data/lib/perron/site/builder/feeds/json.rb +7 -33
- data/lib/perron/site/builder/feeds/rss.erb +28 -0
- data/lib/perron/site/builder/feeds/rss.rb +6 -28
- data/lib/perron/site/builder/feeds/template.rb +63 -0
- data/lib/perron/site/builder/feeds.rb +8 -3
- data/lib/perron/site/builder/page.rb +19 -4
- data/lib/perron/site/builder/paths.rb +75 -13
- data/lib/perron/site/builder/route_resources.rb +79 -0
- data/lib/perron/site/builder/sitemap.rb +71 -20
- data/lib/perron/site/builder.rb +25 -1
- data/lib/perron/site/validate.rb +19 -7
- data/lib/perron/site.rb +7 -0
- data/lib/perron/tasks/build.rake +6 -7
- data/lib/perron/tasks/deploy.rake +58 -0
- data/lib/perron/tasks/install.rake +12 -0
- data/lib/perron/version.rb +1 -1
- data/lib/perron.rb +1 -0
- data/perron.gemspec +1 -1
- metadata +25 -8
- data/lib/generators/perron/install_generator.rb +0 -32
- data/lib/perron/html_processor/syntax_highlight.rb +0 -32
- /data/lib/{generators/perron/templates → perron/install}/initializer.rb.tt +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bdd54bac7e739c0199d3381c1f20aaf15dd5c4dfddec9f57635c0dcf41b66505
|
|
4
|
+
data.tar.gz: c04dc9b074c2a53803086ee6f9a5a7780a26f930661a0884513185323197ba03
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0fdac048810289509dc253a93839fd2df30358a90e463dac6acc3e0cad1171640698e973522d5321b53ca807c56f9a806aeb4bdc972f56773ee0b2693871009b
|
|
7
|
+
data.tar.gz: 8864e0a699f0cf04cc67c696dee53df33e70cd7abe9d57a34b75312613d332c09d0e675fc46a02a52870f30c0a556bb00c98fd7150c8769af5bd1a1ddb79429c
|
data/Gemfile.lock
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
perron (
|
|
4
|
+
perron (1.1.0)
|
|
5
5
|
csv
|
|
6
6
|
json
|
|
7
|
-
mata (~> 0.
|
|
7
|
+
mata (~> 0.10.0)
|
|
8
8
|
psych
|
|
9
9
|
rails (>= 7.2.0)
|
|
10
10
|
|
|
@@ -136,11 +136,10 @@ GEM
|
|
|
136
136
|
net-pop
|
|
137
137
|
net-smtp
|
|
138
138
|
marcel (1.0.4)
|
|
139
|
-
mata (0.
|
|
139
|
+
mata (0.10.0)
|
|
140
140
|
listen (~> 3.0)
|
|
141
141
|
rack (>= 3.0)
|
|
142
142
|
mini_mime (1.1.5)
|
|
143
|
-
mini_portile2 (2.8.9)
|
|
144
143
|
minitest (5.27.0)
|
|
145
144
|
net-imap (0.5.9)
|
|
146
145
|
date
|
|
@@ -152,24 +151,21 @@ GEM
|
|
|
152
151
|
net-smtp (0.5.1)
|
|
153
152
|
net-protocol
|
|
154
153
|
nio4r (2.7.4)
|
|
155
|
-
nokogiri (1.
|
|
156
|
-
mini_portile2 (~> 2.8.2)
|
|
154
|
+
nokogiri (1.19.3-aarch64-linux-gnu)
|
|
157
155
|
racc (~> 1.4)
|
|
158
|
-
nokogiri (1.
|
|
156
|
+
nokogiri (1.19.3-aarch64-linux-musl)
|
|
159
157
|
racc (~> 1.4)
|
|
160
|
-
nokogiri (1.
|
|
158
|
+
nokogiri (1.19.3-arm-linux-gnu)
|
|
161
159
|
racc (~> 1.4)
|
|
162
|
-
nokogiri (1.
|
|
160
|
+
nokogiri (1.19.3-arm-linux-musl)
|
|
163
161
|
racc (~> 1.4)
|
|
164
|
-
nokogiri (1.
|
|
162
|
+
nokogiri (1.19.3-arm64-darwin)
|
|
165
163
|
racc (~> 1.4)
|
|
166
|
-
nokogiri (1.
|
|
164
|
+
nokogiri (1.19.3-x86_64-darwin)
|
|
167
165
|
racc (~> 1.4)
|
|
168
|
-
nokogiri (1.
|
|
166
|
+
nokogiri (1.19.3-x86_64-linux-gnu)
|
|
169
167
|
racc (~> 1.4)
|
|
170
|
-
nokogiri (1.
|
|
171
|
-
racc (~> 1.4)
|
|
172
|
-
nokogiri (1.18.8-x86_64-linux-musl)
|
|
168
|
+
nokogiri (1.19.3-x86_64-linux-musl)
|
|
173
169
|
racc (~> 1.4)
|
|
174
170
|
parallel (1.27.0)
|
|
175
171
|
parser (3.3.8.0)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Perron
|
|
2
|
+
class ConciergeController < ActionController::Base
|
|
3
|
+
def show
|
|
4
|
+
render :show
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def run_command
|
|
8
|
+
command = params[:command]
|
|
9
|
+
|
|
10
|
+
return redirect_back fallback_location: root_path unless command.start_with?("bin/rails generate content")
|
|
11
|
+
|
|
12
|
+
system(command)
|
|
13
|
+
redirect_back fallback_location: root_path
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -4,10 +4,11 @@ require "perron/markdown"
|
|
|
4
4
|
|
|
5
5
|
module Perron
|
|
6
6
|
module MarkdownHelper
|
|
7
|
-
def markdownify(content = nil, process:
|
|
7
|
+
def markdownify(content = nil, process: nil, resource: nil, &block)
|
|
8
8
|
text = block_given? ? capture(&block).strip_heredoc : content
|
|
9
|
+
processors = (process.nil? || process.empty?) ? Perron.configuration.default_processors : process
|
|
9
10
|
|
|
10
|
-
Perron::Markdown.render(text, processors:
|
|
11
|
+
Perron::Markdown.render(text, processors: processors, resource: resource || @resource)
|
|
11
12
|
end
|
|
12
13
|
end
|
|
13
14
|
end
|
|
@@ -2,16 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module Perron
|
|
4
4
|
module MetaTagsHelper
|
|
5
|
-
def meta_tags(options = {})
|
|
5
|
+
def meta_tags(options = {})
|
|
6
|
+
metadata = (@metadata || {}).merge(@resource&.metadata || {})
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Resource = Data.define(:path, :collection, :metadata, :published_at)
|
|
10
|
-
|
|
11
|
-
def resource
|
|
12
|
-
return Resource.new(request.path, nil, @metadata, nil) if @metadata.present?
|
|
13
|
-
|
|
14
|
-
@resource || Resource.new(request.path, nil, {}, nil)
|
|
8
|
+
Perron::Metatags.new(metadata).render(options)
|
|
15
9
|
end
|
|
16
10
|
end
|
|
17
11
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Perron
|
|
4
|
+
module PaginateHelper
|
|
5
|
+
def paginate(scope, page: nil, **options)
|
|
6
|
+
page ||= (params[:page] || 1).to_i
|
|
7
|
+
resource_class = scope.model_class
|
|
8
|
+
|
|
9
|
+
config = resource_class.configuration.pagination
|
|
10
|
+
per_page = options[:per_page] || config.per_page
|
|
11
|
+
page_path_template = options[:path_template] || config.path_template
|
|
12
|
+
|
|
13
|
+
route = find_index_route(resource_class)
|
|
14
|
+
base_path = route_path(route)
|
|
15
|
+
|
|
16
|
+
use_query_params = Rails.env.development? || Rails.env.local?
|
|
17
|
+
paginate = Paginate.new(scope, page: page, per_page: per_page, base_path: base_path, page_path_template: page_path_template, use_query_params: use_query_params)
|
|
18
|
+
|
|
19
|
+
[paginate, paginate.items]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def find_index_route(resource_class)
|
|
25
|
+
controller_name = resource_class.name.demodulize.sub("Controller", "").underscore.pluralize
|
|
26
|
+
|
|
27
|
+
Rails.application.routes.routes.find do |r|
|
|
28
|
+
r.defaults[:controller] == "content/#{controller_name}" &&
|
|
29
|
+
r.defaults[:action] == "index"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def route_path(route)
|
|
34
|
+
return "/#{route.name}/" unless route
|
|
35
|
+
|
|
36
|
+
path_spec = route.path.spec.to_s
|
|
37
|
+
path_spec.sub(/\(.*?\)/, "").gsub(/:[^\/]+/, "").sub(/\/$/, "") + "/"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Welcome to Perron</title>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
|
|
7
|
+
<link rel="icon" href="/icon.png" type="image/png">
|
|
8
|
+
<link rel="apple-touch-icon" href="/icon.png" sizes="512x512">
|
|
9
|
+
|
|
10
|
+
<style>
|
|
11
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
12
|
+
ul { list-style: none; }
|
|
13
|
+
|
|
14
|
+
a {
|
|
15
|
+
color: #1f2937;
|
|
16
|
+
|
|
17
|
+
&:hover: { text-decoration: none; }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
body {
|
|
21
|
+
padding: 2rem 1rem;
|
|
22
|
+
min-height: 100vh;
|
|
23
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
24
|
+
line-height: 1.6;
|
|
25
|
+
color: #1f2937;
|
|
26
|
+
background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.hero {
|
|
30
|
+
max-width: 72rem;
|
|
31
|
+
margin: 0 auto 4rem;
|
|
32
|
+
padding: 3rem 0;
|
|
33
|
+
|
|
34
|
+
h1 {
|
|
35
|
+
font-size: clamp(2rem, 5vw, 3.5rem);
|
|
36
|
+
font-weight: 900;
|
|
37
|
+
text-align: center;
|
|
38
|
+
color: #111827;
|
|
39
|
+
letter-spacing: -.025em;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
ul {
|
|
43
|
+
display: grid;
|
|
44
|
+
row-gap: 1rem;
|
|
45
|
+
margin: 1rem auto 0;
|
|
46
|
+
max-width: 48rem;
|
|
47
|
+
padding: 1rem;
|
|
48
|
+
background: white;
|
|
49
|
+
border-radius: .5rem;
|
|
50
|
+
border: 1px solid #e5e7eb;
|
|
51
|
+
|
|
52
|
+
li {
|
|
53
|
+
border-radius: .5rem;
|
|
54
|
+
|
|
55
|
+
p { font-weight: 600; }
|
|
56
|
+
|
|
57
|
+
div {
|
|
58
|
+
display: grid;
|
|
59
|
+
grid-template-columns: 1fr min-content;
|
|
60
|
+
margin-top: .25rem;
|
|
61
|
+
padding: .75rem 1rem;
|
|
62
|
+
background-color: rgb(248 250 252);
|
|
63
|
+
border-radius: .5rem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
code {
|
|
67
|
+
font-size: .875rem;
|
|
68
|
+
font-family: "SF Mono", Monaco, "Cascadia Code", monospace;
|
|
69
|
+
font-weight: 500;
|
|
70
|
+
color: rgb(51 65 85);
|
|
71
|
+
overflow: auto;
|
|
72
|
+
white-space: nowrap;
|
|
73
|
+
user-select: all;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
svg {
|
|
77
|
+
width: 1.125rem;
|
|
78
|
+
aspect-ratio: 1/1;
|
|
79
|
+
color: rgb(100 116 139);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
input[type=submit] {
|
|
83
|
+
border: 0;
|
|
84
|
+
padding: .25rem 1rem;
|
|
85
|
+
font-size: .875rem;
|
|
86
|
+
font-weight: 300;
|
|
87
|
+
color: white;
|
|
88
|
+
background-color: rgb(249 115 22);
|
|
89
|
+
border-radius: .25rem;
|
|
90
|
+
transition: background-color 300ms ease;
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
|
|
93
|
+
&:hover {
|
|
94
|
+
background-color: rgb(251 146 60);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.info {
|
|
102
|
+
display: grid;
|
|
103
|
+
gap: 2rem;
|
|
104
|
+
max-width: 6xl;
|
|
105
|
+
margin: 0 auto;
|
|
106
|
+
overflow-x: clip;
|
|
107
|
+
|
|
108
|
+
@media (min-width: 768px) {
|
|
109
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
li {
|
|
113
|
+
&:nth-child(odd) {
|
|
114
|
+
@media (min-width: 640px) {
|
|
115
|
+
transform: rotate(-1deg);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
&:nth-child(even) {
|
|
120
|
+
@media (min-width: 640px) {
|
|
121
|
+
transform: rotate(1deg);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
a {
|
|
127
|
+
display: block;
|
|
128
|
+
padding: 2rem 1.5rem;
|
|
129
|
+
text-decoration: none;
|
|
130
|
+
background: white;
|
|
131
|
+
border: 1px solid #e5e7eb;
|
|
132
|
+
border-radius: 1rem;
|
|
133
|
+
transition: all 300ms ease;
|
|
134
|
+
|
|
135
|
+
&:hover {
|
|
136
|
+
transform: translateY(-4px);
|
|
137
|
+
box-shadow: 0 20px 25px -5px rgb(0 0 0 / .1);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
h2 {
|
|
141
|
+
font-size: 1.25rem;
|
|
142
|
+
font-weight: 700;
|
|
143
|
+
color: #111827;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
p {
|
|
147
|
+
margin-top: .25rem;
|
|
148
|
+
color: #4b5563;
|
|
149
|
+
line-height: 1.7;
|
|
150
|
+
|
|
151
|
+
a {
|
|
152
|
+
color: #f97316;
|
|
153
|
+
text-decoration: none;
|
|
154
|
+
font-weight: 500;
|
|
155
|
+
transition: color 200ms ease;
|
|
156
|
+
|
|
157
|
+
&:hover {
|
|
158
|
+
color: #ea580c;
|
|
159
|
+
text-decoration: underline;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.versions {
|
|
167
|
+
display: flex;
|
|
168
|
+
align-items: center;
|
|
169
|
+
justify-content: center;
|
|
170
|
+
column-gap: 1.5rem;
|
|
171
|
+
margin-top: 2rem;
|
|
172
|
+
font-family: "SF Mono", Monaco, "Cascadia Code", monospace;
|
|
173
|
+
text-align: center;
|
|
174
|
+
color: #6b7280;
|
|
175
|
+
font-size: .75rem;
|
|
176
|
+
}
|
|
177
|
+
</style>
|
|
178
|
+
</head>
|
|
179
|
+
|
|
180
|
+
<body>
|
|
181
|
+
<section class="hero">
|
|
182
|
+
<h1>Welcome to Perron</h1>
|
|
183
|
+
|
|
184
|
+
<ul>
|
|
185
|
+
<li>
|
|
186
|
+
<p>
|
|
187
|
+
Create a collection
|
|
188
|
+
</p>
|
|
189
|
+
|
|
190
|
+
<div>
|
|
191
|
+
<code>bin/rails generate content Post</code>
|
|
192
|
+
|
|
193
|
+
<%= form_with url: perron_run_command_path do |form| %>
|
|
194
|
+
<%= form.hidden_field :command, value: "bin/rails generate content Post" %>
|
|
195
|
+
|
|
196
|
+
<% if defined?(Content::Post) %>
|
|
197
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm45.66,85.66-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35a8,8,0,0,1,11.32,11.32Z"/></svg>
|
|
198
|
+
<% else %>
|
|
199
|
+
<%= form.submit "Run" %>
|
|
200
|
+
<% end %>
|
|
201
|
+
<% end %>
|
|
202
|
+
</div>
|
|
203
|
+
</li>
|
|
204
|
+
|
|
205
|
+
<li>
|
|
206
|
+
<p>
|
|
207
|
+
Create your first content
|
|
208
|
+
</p>
|
|
209
|
+
|
|
210
|
+
<div>
|
|
211
|
+
<code>bin/rails generate content Post --new "My first post"</code>
|
|
212
|
+
|
|
213
|
+
<%= form_with url: perron_run_command_path do |form| %>
|
|
214
|
+
<%= form.hidden_field :command, value: 'bin/rails generate content Post --new' %>
|
|
215
|
+
|
|
216
|
+
<% if File.file?("app/content/posts/untitled.md") %>
|
|
217
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm45.66,85.66-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35a8,8,0,0,1,11.32,11.32Z"/></svg>
|
|
218
|
+
<% else %>
|
|
219
|
+
<%= form.submit "Run" %>
|
|
220
|
+
<% end %>
|
|
221
|
+
<% end %>
|
|
222
|
+
</div>
|
|
223
|
+
</li>
|
|
224
|
+
|
|
225
|
+
<li>
|
|
226
|
+
<p>
|
|
227
|
+
Run the server, and visit <code><a href="/posts/">http://localhost:3000/posts/</a></code>
|
|
228
|
+
</p>
|
|
229
|
+
|
|
230
|
+
<div>
|
|
231
|
+
<code>bin/dev</code>
|
|
232
|
+
</div>
|
|
233
|
+
</li>
|
|
234
|
+
</ul>
|
|
235
|
+
</section>
|
|
236
|
+
|
|
237
|
+
<ul class="info">
|
|
238
|
+
<li>
|
|
239
|
+
<a href="https://perron.railsdesigner.com/docs/">
|
|
240
|
+
<h2>Documentation</h2>
|
|
241
|
+
|
|
242
|
+
<p>Visit the docs for more</p>
|
|
243
|
+
</a>
|
|
244
|
+
</li>
|
|
245
|
+
|
|
246
|
+
<li>
|
|
247
|
+
<a href="https://perron.railsdesigner.com/library/">
|
|
248
|
+
<h2>Library</h2>
|
|
249
|
+
|
|
250
|
+
<p>Check out the library for snippets, components and templates</p>
|
|
251
|
+
</a>
|
|
252
|
+
</li>
|
|
253
|
+
|
|
254
|
+
<li>
|
|
255
|
+
<a href="https://github.com/Rails-Designer/perron">
|
|
256
|
+
<h2>GitHub</h2>
|
|
257
|
+
|
|
258
|
+
<p>Check out Perron's source</p>
|
|
259
|
+
</a>
|
|
260
|
+
</li>
|
|
261
|
+
</ul>
|
|
262
|
+
|
|
263
|
+
<div class="versions">
|
|
264
|
+
<span>Ruby <%= RUBY_VERSION %></span>
|
|
265
|
+
|
|
266
|
+
<span>Rails <%= Rails.version %></span>
|
|
267
|
+
|
|
268
|
+
<span>Perron <%= Perron::VERSION %></span>
|
|
269
|
+
</div>
|
|
270
|
+
</body>
|
|
271
|
+
</html>
|
|
@@ -12,6 +12,7 @@ Options:
|
|
|
12
12
|
--force-plural Use plural form for model name and class
|
|
13
13
|
--[no-]include-root Include root action and route
|
|
14
14
|
(default: true for pages, false otherwise)
|
|
15
|
+
--inline Render show action inline instead of using view template
|
|
15
16
|
|
|
16
17
|
Examples:
|
|
17
18
|
Generate basic content scaffold:
|
|
@@ -29,6 +30,17 @@ Examples:
|
|
|
29
30
|
Adds route:
|
|
30
31
|
resources :posts, module: :content, only: %w[index show]
|
|
31
32
|
|
|
33
|
+
Generate inline show action:
|
|
34
|
+
rails generate content Post --inline
|
|
35
|
+
|
|
36
|
+
Creates controller with inline rendering:
|
|
37
|
+
def show
|
|
38
|
+
@resource = Content::Post.find!(params[:id])
|
|
39
|
+
render @resource.inline
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Skips creating app/views/content/posts/show.html.erb
|
|
43
|
+
|
|
32
44
|
Generate pages scaffold with root:
|
|
33
45
|
rails generate content Page
|
|
34
46
|
|
|
@@ -48,17 +60,22 @@ Examples:
|
|
|
48
60
|
Creates data source files in app/content/data/ and adds
|
|
49
61
|
.sources and .template_source class methods to the model.
|
|
50
62
|
|
|
51
|
-
Adds .sources and .template_source class methods to model.
|
|
52
|
-
|
|
53
63
|
Create new content file from template:
|
|
54
64
|
rails generate content Post --new
|
|
55
65
|
rails generate content Post --new "My First Post"
|
|
56
66
|
|
|
57
67
|
Creates a new content file in app/content/posts/ using:
|
|
58
|
-
1.
|
|
59
|
-
2.
|
|
68
|
+
1. Template file with strftime patterns (e.g., %Y-%m-%d-title.md.tt)
|
|
69
|
+
2. Template file without strftime patterns (e.g., template.md.tt)
|
|
60
70
|
3. Empty file with frontmatter dashes (if no template)
|
|
61
71
|
|
|
72
|
+
Template filename examples:
|
|
73
|
+
%Y-%m-%d-title.md.tt → 2026-03-10-my-post.md
|
|
74
|
+
%s-title.md.tt → 1741737600-my-post.md
|
|
75
|
+
%d-title.md.tt → 10-my-post.md
|
|
76
|
+
title.md.tt → my-post.md
|
|
77
|
+
%Y-%m.md.tt → 2026-03.md
|
|
78
|
+
|
|
62
79
|
Template files support ERB:
|
|
63
80
|
---
|
|
64
81
|
title: <%= @title %>
|
|
@@ -13,6 +13,7 @@ module Rails
|
|
|
13
13
|
desc: "Create a new content file from template instead of generating scaffold"
|
|
14
14
|
class_option :data, type: :array, default: [], banner: "source1(.ext) source2(.ext)",
|
|
15
15
|
desc: "Specify data sources with optional extensions (defaults to .yml)"
|
|
16
|
+
class_option :inline, type: :boolean, default: false, desc: "Render show action inline instead of using a view template"
|
|
16
17
|
|
|
17
18
|
argument :actions, type: :array, default: %w[index show], banner: "actions", desc: "Specify which actions to generate (index/show)"
|
|
18
19
|
|
|
@@ -53,6 +54,8 @@ module Rails
|
|
|
53
54
|
empty_directory view_directory
|
|
54
55
|
|
|
55
56
|
actions.each do |action|
|
|
57
|
+
next if action == "show" && options[:inline]
|
|
58
|
+
|
|
56
59
|
template "#{action}.html.erb.tt", File.join(view_directory, "#{action}.html.erb")
|
|
57
60
|
end
|
|
58
61
|
end
|
|
@@ -84,15 +87,12 @@ module Rails
|
|
|
84
87
|
return if @content_mode
|
|
85
88
|
return unless should_include_root?
|
|
86
89
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def root
|
|
90
|
-
@resource = Content::#{class_name}.root
|
|
90
|
+
controller_file = "app/controllers/content/#{plural_file_name}_controller.rb"
|
|
91
|
+
return unless File.exist?(File.join(destination_root, controller_file))
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
end
|
|
93
|
+
root_action = " def root\n @resource = Content::#{class_name}.root\n\n render :show\n end\n\n"
|
|
94
|
+
|
|
95
|
+
inject_into_file controller_file, root_action, after: "class Content::#{plural_class_name}Controller < ApplicationController\n"
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def create_root_content_file
|
|
@@ -129,16 +129,20 @@ module Rails
|
|
|
129
129
|
def pages_controller? = plural_file_name == "pages"
|
|
130
130
|
|
|
131
131
|
def template_file
|
|
132
|
-
@template_file ||= Dir.glob(File.join(content_directory, "
|
|
132
|
+
@template_file ||= Dir.glob(File.join(content_directory, "*.tt")).first
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
def filename_from_template
|
|
136
136
|
@filename_from_template ||= begin
|
|
137
137
|
return "untitled.md" unless template_file
|
|
138
138
|
|
|
139
|
-
File.basename(template_file, ".tt")
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
name = File.basename(template_file, ".tt")
|
|
140
|
+
name = Time.current.strftime(name)
|
|
141
|
+
|
|
142
|
+
if name.include?("title")
|
|
143
|
+
name.sub("title", @content_title ? @content_title.parameterize : "untitled")
|
|
144
|
+
else
|
|
145
|
+
name
|
|
142
146
|
end
|
|
143
147
|
end
|
|
144
148
|
end
|
|
@@ -3,11 +3,17 @@ class Content::<%= plural_class_name %>Controller < ApplicationController
|
|
|
3
3
|
def index
|
|
4
4
|
@resources = Content::<%= class_name %>.all
|
|
5
5
|
end
|
|
6
|
+
<%- if actions.include?("show") -%>
|
|
6
7
|
|
|
8
|
+
<%- end -%>
|
|
7
9
|
<%- end -%>
|
|
8
10
|
<%- if actions.include?("show") -%>
|
|
9
11
|
def show
|
|
10
12
|
@resource = Content::<%= class_name %>.find!(params[:id])
|
|
13
|
+
<%- if options[:inline] -%>
|
|
14
|
+
|
|
15
|
+
render @resource.inline
|
|
16
|
+
<%- end -%>
|
|
11
17
|
end
|
|
12
18
|
<%- end -%>
|
|
13
19
|
end
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" fill="none" viewBox="0 0 400 400"><path fill="#f97316" d="M177.101 221.582a8.59 8.59 0 0 1 8.587-8.587h68.524q23.93 0 40.424-16.458 16.728-16.697 16.728-46.99 0-22.66-17.425-40.549-17.424-18.129-39.727-18.128H126.636c-8.836 0-16 7.163-16 16v244.766a8.364 8.364 0 1 1-16.727 0V89.696c0-8.837 7.164-16 16-16h144.303q29.272 0 51.576 23.137 22.303 23.136 22.303 52.714 0 36.495-21.606 58.678-21.606 21.944-52.273 21.944h-68.524a8.587 8.587 0 0 1-8.587-8.587m20 34.348a8.59 8.59 0 0 1 8.587-8.587h48.524q24.162 0 44.606-11.926 20.678-12.165 33.222-34.825 12.778-22.66 12.778-51.045 0-36.733-27.414-64.88-27.182-28.145-63.192-28.145H93.182c-8.837 0-16 7.163-16 16v299.114a8.364 8.364 0 1 1-16.727 0V55.348c0-8.837 7.163-16 16-16h177.757q28.111 0 52.97 15.266t39.495 40.788q14.868 25.522 14.868 54.145 0 32.44-14.868 59.155-14.87 26.477-39.495 41.265-24.394 14.55-52.97 14.55h-48.524a8.59 8.59 0 0 1-8.587-8.587m20 34.348a8.587 8.587 0 0 1 8.587-8.587h28.524q24.86 0 47.626-10.018 22.768-10.257 39.495-27.431 16.96-17.412 26.95-41.981 9.99-24.806 9.99-52.714 0-33.393-16.96-62.732-16.959-29.578-45.768-46.99-28.575-17.65-61.333-17.651H59.727c-8.836 0-16 7.163-16 16v353.462a8.364 8.364 0 0 1-16.727 0V21c0-8.837 7.163-16 16-16h211.212q37.172 0 69.697 20.036 32.526 19.799 51.808 53.192Q395 111.62 395 149.547q0 33.871-11.384 62.494-11.383 28.385-30.667 47.228-19.283 18.844-44.838 29.339-25.555 10.257-53.899 10.257h-28.524a8.59 8.59 0 0 1-8.587-8.587m-73.01 41.358a8.364 8.364 0 0 1-16.727 0V124.043c0-8.836 7.163-16 16-16h110.848q16.495 0 28.344 12.404 12.08 12.404 12.08 29.1 0 46.274-40.424 46.274h-88.524a8.587 8.587 0 1 1 0-17.174h88.524q11.152 0 17.424-6.44t6.273-22.66q0-10.495-6.97-17.412-6.737-6.918-16.727-6.918h-94.121c-8.837 0-16 7.164-16 16z" style="mix-blend-mode:multiply"/></svg>
|
data/lib/perron/collection.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Perron
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def all(resource_class = "Content::#{name.classify}".safe_constantize)
|
|
19
|
-
Perron::Relation.new(load_resources(resource_class).select(&:published?))
|
|
19
|
+
Perron::Relation.new(load_resources(resource_class).select(&:published?), resource_class)
|
|
20
20
|
end
|
|
21
21
|
alias_method :resources, :all
|
|
22
22
|
|
|
@@ -52,6 +52,7 @@ module Perron
|
|
|
52
52
|
|
|
53
53
|
Dir.glob("#{@collection_path}/**/*.*")
|
|
54
54
|
.select { allowed_extensions.include?(File.extname(it)) }
|
|
55
|
+
.reject { File.basename(it, ".*").downcase == "readme" }
|
|
55
56
|
.map { resource_class.new(it) }
|
|
56
57
|
end
|
|
57
58
|
end
|
data/lib/perron/configuration.rb
CHANGED
|
@@ -15,6 +15,8 @@ module Perron
|
|
|
15
15
|
|
|
16
16
|
@config.output = "output"
|
|
17
17
|
|
|
18
|
+
@config.output_server_strict = true
|
|
19
|
+
|
|
18
20
|
@config.mode = :standalone
|
|
19
21
|
|
|
20
22
|
@config.live_reload = false
|
|
@@ -35,8 +37,12 @@ module Perron
|
|
|
35
37
|
|
|
36
38
|
@config.markdown_options = {}
|
|
37
39
|
|
|
40
|
+
@config.default_processors = []
|
|
41
|
+
|
|
38
42
|
@config.search_scope = []
|
|
39
43
|
|
|
44
|
+
@config.cache_data_sources = false
|
|
45
|
+
|
|
40
46
|
@config.sitemap = ActiveSupport::OrderedOptions.new
|
|
41
47
|
@config.sitemap.enabled = false
|
|
42
48
|
@config.sitemap.priority = 0.5
|
|
@@ -47,6 +53,9 @@ module Perron
|
|
|
47
53
|
|
|
48
54
|
@config.metadata = ActiveSupport::OrderedOptions.new
|
|
49
55
|
@config.metadata.title_separator = " — "
|
|
56
|
+
|
|
57
|
+
@config.before_build = nil
|
|
58
|
+
@config.after_build = nil
|
|
50
59
|
end
|
|
51
60
|
|
|
52
61
|
def input = Rails.root.join("app", "content")
|
|
@@ -61,6 +70,22 @@ module Perron
|
|
|
61
70
|
@additional_routes || (mode.integrated? ? [] : %w[root_path])
|
|
62
71
|
end
|
|
63
72
|
|
|
73
|
+
def deploy
|
|
74
|
+
@deploy ||= ActiveSupport::OrderedOptions.new.tap do |config|
|
|
75
|
+
def config.method_missing(method_name, *args, &block)
|
|
76
|
+
if method_name.to_s.end_with?("=")
|
|
77
|
+
super
|
|
78
|
+
else
|
|
79
|
+
self[method_name] ||= ActiveSupport::OrderedOptions.new
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def config.respond_to_missing?(method_name, include_private = false)
|
|
84
|
+
!method_name.to_s.end_with?("=") || super
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
64
89
|
attr_writer :additional_routes
|
|
65
90
|
|
|
66
91
|
def url
|
|
@@ -78,8 +103,8 @@ module Perron
|
|
|
78
103
|
end
|
|
79
104
|
end
|
|
80
105
|
|
|
81
|
-
def respond_to_missing?(method_name)
|
|
82
|
-
@config.respond_to?(method_name) || super
|
|
106
|
+
def respond_to_missing?(method_name, ...)
|
|
107
|
+
@config.respond_to?(method_name, ...) || super
|
|
83
108
|
end
|
|
84
109
|
end
|
|
85
110
|
end
|
|
@@ -17,6 +17,14 @@ module Perron
|
|
|
17
17
|
all.find { it[:id] == id || it["id"] == id }
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
def find!(id)
|
|
21
|
+
data_source = all.find { it[:id] == id || it["id"] == id }
|
|
22
|
+
|
|
23
|
+
return data_source if data_source
|
|
24
|
+
|
|
25
|
+
raise Errors::DataSourceNotFoundError, "Row not found with id: #{id}"
|
|
26
|
+
end
|
|
27
|
+
|
|
20
28
|
def count = all.size
|
|
21
29
|
|
|
22
30
|
def first = all.first
|