perron 1.0.0 → 1.2.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 +4 -1
- 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 +1 -1
- data/lib/perron/collection.rb +2 -1
- data/lib/perron/configuration.rb +29 -4
- data/lib/perron/data_source.rb +6 -4
- data/lib/perron/deprecator.rb +1 -1
- data/lib/perron/engine.rb +11 -8
- data/lib/perron/feeds/split.rb +46 -0
- data/lib/perron/feeds.rb +17 -0
- data/lib/perron/html_processor/absolute_urls.rb +1 -1
- data/lib/perron/install/deploy.yml.erb +14 -0
- data/lib/perron/paginate.rb +58 -0
- data/lib/perron/relation.rb +27 -7
- data/lib/perron/resource/class_methods.rb +6 -0
- data/lib/perron/resource/configuration.rb +16 -3
- data/lib/perron/resource/metadata.rb +11 -4
- data/lib/perron/resource/publishable.rb +9 -1
- data/lib/perron/resource/separator.rb +11 -5
- data/lib/perron/resource/sourceable.rb +63 -9
- data/lib/perron/resource/updatable.rb +46 -0
- data/lib/perron/resource.rb +7 -0
- data/lib/perron/site/builder/feeds/atom.erb +1 -1
- data/lib/perron/site/builder/feeds/atom.rb +8 -2
- data/lib/perron/site/builder/feeds/json.rb +8 -2
- data/lib/perron/site/builder/feeds/rss.rb +8 -2
- data/lib/perron/site/builder/feeds.rb +32 -10
- data/lib/perron/site/builder/page.rb +19 -4
- data/lib/perron/site/builder/paths.rb +23 -1
- data/lib/perron/site/builder/sitemap.rb +23 -10
- data/lib/perron/site/builder.rb +24 -0
- data/lib/perron/site/validate.rb +18 -5
- data/lib/perron/tasks/deploy.rake +69 -0
- data/lib/perron/version.rb +1 -1
- data/lib/perron.rb +1 -0
- data/perron.gemspec +1 -1
- metadata +10 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 84ad5823f9409ec7734c3fef06a9d57602e5da59c14e939d2e4acb8f3f2b4e22
|
|
4
|
+
data.tar.gz: 9e8e47f8706704ec8d8d6efb66ccbb6eb87f815570166f0bd188ce0e6f085b9a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 766753cd5c6e74d3353ad603015e316f7158a1a79c28ed8b0fbcbd8d9a7c8c0a1a4cb88e372fbe741548a83e0d0a042da44df8c8545cc6973eb768283177dae8
|
|
7
|
+
data.tar.gz: 605182a711103ad0e0ca07c330290ee2ab1574fcfda9596e042ea7365a4f329485a407692ce4c5e94d5c720e1100bb1c79ef4300ccb04eb4119f989af895e2a5
|
data/Gemfile.lock
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
perron (1.
|
|
4
|
+
perron (1.2.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)
|
|
@@ -5,8 +5,11 @@ module Perron
|
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
def run_command
|
|
8
|
-
|
|
8
|
+
command = params[:command]
|
|
9
9
|
|
|
10
|
+
return redirect_back fallback_location: root_path unless command.start_with?("bin/rails generate content")
|
|
11
|
+
|
|
12
|
+
system(command)
|
|
10
13
|
redirect_back fallback_location: root_path
|
|
11
14
|
end
|
|
12
15
|
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
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
<code>bin/rails generate content Post --new "My first post"</code>
|
|
212
212
|
|
|
213
213
|
<%= form_with url: perron_run_command_path do |form| %>
|
|
214
|
-
<%= form.hidden_field :command, value: 'bin/rails generate content Post --new
|
|
214
|
+
<%= form.hidden_field :command, value: 'bin/rails generate content Post --new' %>
|
|
215
215
|
|
|
216
216
|
<% if File.file?("app/content/posts/untitled.md") %>
|
|
217
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>
|
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,8 +15,6 @@ module Perron
|
|
|
15
15
|
|
|
16
16
|
@config.output = "output"
|
|
17
17
|
|
|
18
|
-
@config.output_server_strict = true
|
|
19
|
-
|
|
20
18
|
@config.mode = :standalone
|
|
21
19
|
|
|
22
20
|
@config.live_reload = false
|
|
@@ -37,6 +35,8 @@ module Perron
|
|
|
37
35
|
|
|
38
36
|
@config.markdown_options = {}
|
|
39
37
|
|
|
38
|
+
@config.default_processors = []
|
|
39
|
+
|
|
40
40
|
@config.search_scope = []
|
|
41
41
|
|
|
42
42
|
@config.cache_data_sources = false
|
|
@@ -45,12 +45,17 @@ module Perron
|
|
|
45
45
|
@config.sitemap.enabled = false
|
|
46
46
|
@config.sitemap.priority = 0.5
|
|
47
47
|
@config.sitemap.change_frequency = :monthly
|
|
48
|
+
@config.sitemap.emit_priority = false
|
|
49
|
+
@config.sitemap.emit_changefreq = false
|
|
48
50
|
|
|
49
51
|
@config.site_name = nil
|
|
50
52
|
@config.site_description = nil
|
|
51
53
|
|
|
52
54
|
@config.metadata = ActiveSupport::OrderedOptions.new
|
|
53
55
|
@config.metadata.title_separator = " — "
|
|
56
|
+
|
|
57
|
+
@config.before_build = nil
|
|
58
|
+
@config.after_build = nil
|
|
54
59
|
end
|
|
55
60
|
|
|
56
61
|
def input = Rails.root.join("app", "content")
|
|
@@ -61,10 +66,30 @@ module Perron
|
|
|
61
66
|
|
|
62
67
|
def mode = @config.mode.to_s.inquiry
|
|
63
68
|
|
|
69
|
+
def output_server_strict = @config.fetch(:output_server_strict) { mode.standalone? }
|
|
70
|
+
|
|
64
71
|
def additional_routes
|
|
65
72
|
@additional_routes || (mode.integrated? ? [] : %w[root_path])
|
|
66
73
|
end
|
|
67
74
|
|
|
75
|
+
def deploy
|
|
76
|
+
Perron.deprecator.deprecation_warning(:deploy, "Perron.configuration.deploy is deprecated and its behavior will change in a future 1.x release (target: 1.3.0).")
|
|
77
|
+
|
|
78
|
+
@deploy ||= ActiveSupport::OrderedOptions.new.tap do |config|
|
|
79
|
+
def config.method_missing(method_name, *args, &block)
|
|
80
|
+
if method_name.to_s.end_with?("=")
|
|
81
|
+
super
|
|
82
|
+
else
|
|
83
|
+
self[method_name] ||= ActiveSupport::OrderedOptions.new
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def config.respond_to_missing?(method_name, include_private = false)
|
|
88
|
+
!method_name.to_s.end_with?("=") || super
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
68
93
|
attr_writer :additional_routes
|
|
69
94
|
|
|
70
95
|
def url
|
|
@@ -82,8 +107,8 @@ module Perron
|
|
|
82
107
|
end
|
|
83
108
|
end
|
|
84
109
|
|
|
85
|
-
def respond_to_missing?(method_name)
|
|
86
|
-
@config.respond_to?(method_name) || super
|
|
110
|
+
def respond_to_missing?(method_name, ...)
|
|
111
|
+
@config.respond_to?(method_name, ...) || super
|
|
87
112
|
end
|
|
88
113
|
end
|
|
89
114
|
end
|
data/lib/perron/data_source.rb
CHANGED
|
@@ -66,11 +66,13 @@ module Perron
|
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
data.map.with_index do |item, index|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
if item.is_a?(Hash)
|
|
70
|
+
Item.new(item, identifier: @identifier)
|
|
71
|
+
elsif item.is_a?(String)
|
|
72
|
+
item
|
|
73
|
+
else
|
|
74
|
+
raise Errors::DataParseError, "Item at index #{index} in `#{@file_path}` must be a hash/object or string, got #{item.class}"
|
|
71
75
|
end
|
|
72
|
-
|
|
73
|
-
Item.new(item, identifier: @identifier)
|
|
74
76
|
end
|
|
75
77
|
end
|
|
76
78
|
|
data/lib/perron/deprecator.rb
CHANGED
data/lib/perron/engine.rb
CHANGED
|
@@ -30,17 +30,19 @@ module Perron
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
initializer "perron.concierge", before: :add_builtin_route do |app|
|
|
33
|
-
|
|
34
|
-
app.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
if Rails.env.development?
|
|
34
|
+
app.config.after_initialize do
|
|
35
|
+
app.routes.append do
|
|
36
|
+
namespace :perron do
|
|
37
|
+
post :run_command, to: "concierge#run_command"
|
|
38
|
+
end
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
root to: "perron/concierge#show" unless app.routes.named_routes.key?(:root)
|
|
41
|
+
end
|
|
40
42
|
end
|
|
41
|
-
end
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
app.routes.finalize!
|
|
45
|
+
end
|
|
44
46
|
end
|
|
45
47
|
|
|
46
48
|
initializer "perron.inflections" do
|
|
@@ -57,6 +59,7 @@ module Perron
|
|
|
57
59
|
load File.expand_path("../tasks/install.rake", __FILE__)
|
|
58
60
|
load File.expand_path("../tasks/sync_sources.rake", __FILE__)
|
|
59
61
|
load File.expand_path("../tasks/validate.rake", __FILE__)
|
|
62
|
+
load File.expand_path("../tasks/deploy.rake", __FILE__)
|
|
60
63
|
end
|
|
61
64
|
end
|
|
62
65
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Perron
|
|
4
|
+
class Feeds
|
|
5
|
+
module Split
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def grouped_resources(resources, extractor)
|
|
9
|
+
resources
|
|
10
|
+
.flat_map { |resource| extract(resource, extractor).map { [it, resource] } }
|
|
11
|
+
.group_by(&:first)
|
|
12
|
+
.transform_values { it.map(&:last) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def split_values(resources, extractor)
|
|
16
|
+
resources
|
|
17
|
+
.flat_map { extract(it, extractor) }
|
|
18
|
+
.uniq
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def extract(resource, extractor)
|
|
22
|
+
value = case extractor
|
|
23
|
+
when Symbol then resource.public_send(extractor)
|
|
24
|
+
when Proc then extractor.call(resource)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
Array(value)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def split_path_for(type_config, value)
|
|
31
|
+
split_by = type_config[:split_by]
|
|
32
|
+
|
|
33
|
+
if split_by[:path]
|
|
34
|
+
split_by[:path].gsub(":value", value.to_s.parameterize)
|
|
35
|
+
else
|
|
36
|
+
extension = File.extname(type_config.path)
|
|
37
|
+
base_path = type_config.path.delete_suffix(extension)
|
|
38
|
+
field = split_by[:extractor]
|
|
39
|
+
field_name = field.is_a?(Symbol) ? field.to_s : "custom"
|
|
40
|
+
|
|
41
|
+
"#{base_path}/#{field_name}/#{value.to_s.parameterize}#{extension}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/perron/feeds.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "perron/feeds/split"
|
|
4
|
+
|
|
3
5
|
module Perron
|
|
4
6
|
class Feeds
|
|
5
7
|
include ActionView::Helpers::TagHelper
|
|
8
|
+
include Perron::Feeds::Split
|
|
6
9
|
|
|
7
10
|
def render(options = {})
|
|
8
11
|
html_tags = []
|
|
@@ -22,6 +25,20 @@ module Perron
|
|
|
22
25
|
title = "#{collection.name.humanize} #{type.to_s.humanize} Feed"
|
|
23
26
|
|
|
24
27
|
html_tags << tag(:link, rel: "alternate", type: MIME_TYPES[type], title: title, href: absolute_url)
|
|
28
|
+
|
|
29
|
+
next unless feed[:split_by]
|
|
30
|
+
|
|
31
|
+
split_values(collection.resources, feed[:split_by][:extractor]).each do |value|
|
|
32
|
+
split_path = split_path_for(feed, value)
|
|
33
|
+
split_url = URI.join(url.root_url, split_path).to_s
|
|
34
|
+
split_title = if (title_template = feed[:split_by][:title])
|
|
35
|
+
title_template.gsub(":value", value.to_s.humanize)
|
|
36
|
+
else
|
|
37
|
+
"#{collection.name.humanize}: #{value.to_s.humanize} #{type.to_s.humanize} Feed"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
html_tags << tag(:link, rel: "alternate", type: MIME_TYPES[type], title: split_title, href: split_url)
|
|
41
|
+
end
|
|
25
42
|
end
|
|
26
43
|
end
|
|
27
44
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Deploy configuration for Perron
|
|
2
|
+
# Requires Beam Up (`bundle add beam_up`)
|
|
3
|
+
# See also: https://perron.railsdesigner.com/docs/deploy/#deploy-task
|
|
4
|
+
|
|
5
|
+
# Uncomment and configure your provider:
|
|
6
|
+
# provider: seal_static
|
|
7
|
+
|
|
8
|
+
# seal_static:
|
|
9
|
+
# api_key: your_key_here
|
|
10
|
+
|
|
11
|
+
before_actions:
|
|
12
|
+
- RAILS_ENV=production bundle exec rails perron:build
|
|
13
|
+
after_actions:
|
|
14
|
+
- bundle exec rails perron:clobber
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Perron
|
|
4
|
+
class Paginate
|
|
5
|
+
def initialize(collection, page:, per_page:, base_path: nil, page_path_template: nil, use_query_params: false)
|
|
6
|
+
@collection = collection
|
|
7
|
+
@per_page = per_page
|
|
8
|
+
@base_path = base_path
|
|
9
|
+
@page_path_template = page_path_template || "/page/:page/"
|
|
10
|
+
@use_query_params = use_query_params
|
|
11
|
+
|
|
12
|
+
@total_items = collection.size
|
|
13
|
+
@total_pages = total_items.zero? ? 0 : (total_items.to_f / per_page).ceil
|
|
14
|
+
@current_page = page.clamp(1, total_pages.zero? ? 1 : total_pages)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_reader :current_page, :total_pages, :total_items, :per_page
|
|
18
|
+
|
|
19
|
+
def items
|
|
20
|
+
offset = (@current_page - 1) * @per_page
|
|
21
|
+
|
|
22
|
+
@collection[offset, @per_page] || []
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def next? = @current_page < @total_pages
|
|
26
|
+
|
|
27
|
+
def previous? = @current_page > 1
|
|
28
|
+
|
|
29
|
+
def next
|
|
30
|
+
return unless next?
|
|
31
|
+
|
|
32
|
+
page_path(@current_page + 1)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def previous
|
|
36
|
+
return unless previous?
|
|
37
|
+
|
|
38
|
+
target = (@current_page == 2) ? 1 : @current_page - 1
|
|
39
|
+
|
|
40
|
+
page_path(target)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
attr_reader :use_query_params
|
|
46
|
+
|
|
47
|
+
def page_path(number)
|
|
48
|
+
return if number < 1
|
|
49
|
+
return if number > @total_pages && @total_pages > 0
|
|
50
|
+
|
|
51
|
+
return @base_path if number <= 1
|
|
52
|
+
|
|
53
|
+
return "#{@base_path}?page=#{number}" if @use_query_params
|
|
54
|
+
|
|
55
|
+
@base_path.sub(/\/$/, "") + @page_path_template.sub(":page", number.to_s)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/perron/relation.rb
CHANGED
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Perron
|
|
4
4
|
class Relation < Array
|
|
5
|
-
def initialize(resources = [])
|
|
6
|
-
super
|
|
5
|
+
def initialize(resources = [], model_class = nil)
|
|
6
|
+
super(resources)
|
|
7
|
+
|
|
8
|
+
@model_class = model_class
|
|
7
9
|
end
|
|
10
|
+
attr_reader :model_class
|
|
8
11
|
|
|
9
12
|
def where(**conditions)
|
|
10
13
|
filtered = select do |resource|
|
|
@@ -19,21 +22,23 @@ module Perron
|
|
|
19
22
|
end
|
|
20
23
|
end
|
|
21
24
|
|
|
22
|
-
Relation.new(filtered)
|
|
25
|
+
Relation.new(filtered, @model_class)
|
|
23
26
|
end
|
|
24
27
|
|
|
25
|
-
def limit(count) = Relation.new(first(count))
|
|
28
|
+
def limit(count) = Relation.new(first(count), @model_class)
|
|
26
29
|
|
|
27
|
-
def offset(count) = Relation.new(drop(count))
|
|
30
|
+
def offset(count) = Relation.new(drop(count), @model_class)
|
|
28
31
|
|
|
29
32
|
def order(attribute, direction = :asc)
|
|
30
33
|
if attribute.is_a?(Hash)
|
|
31
34
|
attribute, direction = attribute.first
|
|
32
35
|
end
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
present, missing = partition { !it.public_send(attribute).nil? }
|
|
38
|
+
present = present.sort_by { it.public_send(attribute) }
|
|
39
|
+
present = present.reverse if direction == :desc
|
|
35
40
|
|
|
36
|
-
Relation.new(
|
|
41
|
+
Relation.new(present + missing, @model_class)
|
|
37
42
|
end
|
|
38
43
|
|
|
39
44
|
def pluck(*attributes)
|
|
@@ -47,5 +52,20 @@ module Perron
|
|
|
47
52
|
end
|
|
48
53
|
end
|
|
49
54
|
end
|
|
55
|
+
|
|
56
|
+
def in_order_of(attribute, values, filter: true)
|
|
57
|
+
return Relation.new([]) if values.empty?
|
|
58
|
+
|
|
59
|
+
indexed = values.each_with_index.to_h
|
|
60
|
+
|
|
61
|
+
resources = if filter
|
|
62
|
+
select { indexed.key?(it.public_send(attribute)) }
|
|
63
|
+
.sort_by { indexed[it.public_send(attribute)] }
|
|
64
|
+
else
|
|
65
|
+
sort_by { indexed[it.public_send(attribute)] || Float::INFINITY }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
Relation.new(resources)
|
|
69
|
+
end
|
|
50
70
|
end
|
|
51
71
|
end
|
|
@@ -22,6 +22,8 @@ module Perron
|
|
|
22
22
|
|
|
23
23
|
def order(attribute, direction = :asc) = all.order(attribute, direction)
|
|
24
24
|
|
|
25
|
+
def in_order_of(attribute, values, filter: true) = all.in_order_of(attribute, values, filter:)
|
|
26
|
+
|
|
25
27
|
def first(n = nil)
|
|
26
28
|
n ? all.first(n) : all[0]
|
|
27
29
|
end
|
|
@@ -44,6 +46,10 @@ module Perron
|
|
|
44
46
|
|
|
45
47
|
def root = all.find(&:root?)
|
|
46
48
|
|
|
49
|
+
def destroy_all
|
|
50
|
+
all.each(&:destroy)
|
|
51
|
+
end
|
|
52
|
+
|
|
47
53
|
def model_name
|
|
48
54
|
@model_name ||= ActiveModel::Name.new(self, nil, name.demodulize.to_s)
|
|
49
55
|
end
|
|
@@ -12,17 +12,17 @@ module Perron
|
|
|
12
12
|
|
|
13
13
|
config.feeds = Options.new
|
|
14
14
|
|
|
15
|
-
config.feeds.atom =
|
|
15
|
+
config.feeds.atom = FeedTypeConfig.new
|
|
16
16
|
config.feeds.atom.enabled = false
|
|
17
17
|
config.feeds.atom.path = "feeds/#{collection.name.demodulize.parameterize}.atom"
|
|
18
18
|
config.feeds.atom.max_items = 20
|
|
19
19
|
|
|
20
|
-
config.feeds.json =
|
|
20
|
+
config.feeds.json = FeedTypeConfig.new
|
|
21
21
|
config.feeds.json.enabled = false
|
|
22
22
|
config.feeds.json.path = "feeds/#{collection.name.demodulize.parameterize}.json"
|
|
23
23
|
config.feeds.json.max_items = 20
|
|
24
24
|
|
|
25
|
-
config.feeds.rss =
|
|
25
|
+
config.feeds.rss = FeedTypeConfig.new
|
|
26
26
|
config.feeds.rss.enabled = false
|
|
27
27
|
config.feeds.rss.path = "feeds/#{collection.name.demodulize.parameterize}.xml"
|
|
28
28
|
config.feeds.rss.max_items = 20
|
|
@@ -31,6 +31,9 @@ module Perron
|
|
|
31
31
|
config.related_posts.enabled = false
|
|
32
32
|
config.related_posts.max = 5
|
|
33
33
|
|
|
34
|
+
config.pagination = ActiveSupport::OrderedOptions.new
|
|
35
|
+
config.pagination.path_template = "/page/:page/"
|
|
36
|
+
|
|
34
37
|
config.sitemap = ActiveSupport::OrderedOptions.new
|
|
35
38
|
config.sitemap.exclude = false
|
|
36
39
|
end
|
|
@@ -55,6 +58,16 @@ module Perron
|
|
|
55
58
|
end
|
|
56
59
|
end
|
|
57
60
|
private_constant :Options
|
|
61
|
+
|
|
62
|
+
class FeedTypeConfig < ActiveSupport::OrderedOptions
|
|
63
|
+
def split_by(method_or_lambda = nil, path: nil, title: nil, &block)
|
|
64
|
+
extractor = method_or_lambda || block
|
|
65
|
+
|
|
66
|
+
self[:split_by] = {extractor: extractor}
|
|
67
|
+
self[:split_by][:path] = path if path
|
|
68
|
+
self[:split_by][:title] = title if title
|
|
69
|
+
end
|
|
70
|
+
end
|
|
58
71
|
end
|
|
59
72
|
end
|
|
60
73
|
end
|
|
@@ -3,27 +3,34 @@
|
|
|
3
3
|
module Perron
|
|
4
4
|
class Resource
|
|
5
5
|
class Metadata
|
|
6
|
-
def initialize(resource:, frontmatter:, collection:)
|
|
6
|
+
def initialize(resource:, frontmatter:, collection:, controller_metadata: {})
|
|
7
7
|
@resource = resource
|
|
8
8
|
@frontmatter = frontmatter&.deep_symbolize_keys || {}
|
|
9
9
|
@collection = collection
|
|
10
|
+
@controller_metadata = controller_metadata
|
|
10
11
|
@config = Perron.configuration
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def data
|
|
14
15
|
@data ||= ActiveSupport::OrderedOptions
|
|
15
16
|
.new
|
|
16
|
-
.merge(apply_fallbacks_and_defaults(to:
|
|
17
|
+
.merge(apply_fallbacks_and_defaults(to: merged_metadata))
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
private
|
|
20
21
|
|
|
21
|
-
def
|
|
22
|
+
def merged_metadata
|
|
23
|
+
site_data
|
|
24
|
+
.merge(collection_data)
|
|
25
|
+
.merge(@controller_metadata)
|
|
26
|
+
.merge(@frontmatter)
|
|
27
|
+
end
|
|
22
28
|
|
|
23
29
|
def apply_fallbacks_and_defaults(to:)
|
|
24
30
|
to[:title] ||= @config.site_name || Rails.application.name.underscore.camelize
|
|
25
31
|
|
|
26
32
|
to[:canonical_url] ||= canonical_url
|
|
33
|
+
to[:explicit_canonical_url] = @frontmatter[:canonical_url]
|
|
27
34
|
|
|
28
35
|
to[:image] = absolute_url(to[:image]) if to[:image]
|
|
29
36
|
|
|
@@ -65,7 +72,7 @@ module Perron
|
|
|
65
72
|
return path if path.blank?
|
|
66
73
|
return path if path.start_with?("http://", "https://", "//")
|
|
67
74
|
|
|
68
|
-
Perron.configuration.url.delete_suffix("/")
|
|
75
|
+
"#{Perron.configuration.url.delete_suffix("/")}/#{path.delete_prefix("/")}"
|
|
69
76
|
end
|
|
70
77
|
|
|
71
78
|
def site_data
|