perron 0.16.0 → 0.18.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 +1 -1
- data/Gemfile.lock +25 -2
- data/app/controllers/perron/searches_controller.rb +48 -0
- data/app/helpers/perron/feeds_helper.rb +7 -0
- data/app/helpers/perron/markdown_helper.rb +3 -3
- data/app/helpers/perron/meta_tags_helper.rb +17 -0
- data/bin/release +19 -4
- data/lib/generators/perron/templates/README.md.tt +31 -7
- data/lib/generators/perron/templates/initializer.rb.tt +11 -11
- data/lib/generators/rails/content/USAGE +40 -15
- data/lib/generators/rails/content/content_generator.rb +53 -15
- data/lib/generators/rails/content/templates/controller.rb.tt +1 -5
- data/lib/generators/rails/content/templates/model.rb.tt +11 -0
- data/lib/generators/rails/content/templates/root.erb.tt +1 -1
- data/lib/generators/rails/content/templates/show.html.erb.tt +1 -1
- data/lib/perron/collection.rb +10 -1
- data/lib/perron/configuration.rb +14 -6
- data/lib/perron/content/data.rb +6 -2
- data/lib/perron/data_source/class_methods.rb +58 -0
- data/lib/perron/data_source/helper_context.rb +20 -0
- data/lib/perron/data_source/item.rb +37 -0
- data/lib/perron/{data → data_source}/proxy.rb +1 -1
- data/lib/perron/data_source.rb +155 -0
- data/lib/perron/engine.rb +12 -0
- data/lib/perron/html_processor/syntax_highlight.rb +2 -0
- data/lib/perron/output_server.rb +7 -2
- data/lib/perron/relation.rb +51 -0
- data/lib/perron/resource/associations.rb +2 -2
- data/lib/perron/resource/class_methods.rb +14 -4
- data/lib/perron/resource/configuration.rb +8 -11
- data/lib/perron/resource/core.rb +11 -0
- data/lib/perron/resource/metadata.rb +1 -1
- data/lib/perron/resource/related/stop_words.rb +20 -20
- data/lib/perron/resource/related.rb +73 -52
- data/lib/perron/resource/scopes.rb +29 -0
- data/lib/perron/resource/searchable.rb +19 -0
- data/lib/perron/resource/sourceable.rb +2 -2
- data/lib/perron/resource/sweeper.rb +45 -0
- data/lib/perron/resource/table_of_content.rb +0 -18
- data/lib/perron/resource.rb +34 -24
- data/lib/perron/site/builder/additional_routes.rb +23 -0
- data/lib/perron/site/builder/feeds/author.rb +2 -1
- data/lib/perron/site/builder/paths.rb +2 -9
- data/lib/perron/site/builder/sitemap.rb +20 -2
- data/lib/perron/site/builder.rb +2 -0
- data/lib/perron/site.rb +3 -3
- data/lib/perron/tasks/build.rake +8 -1
- data/lib/perron/version.rb +1 -1
- data/lib/perron.rb +1 -1
- data/perron.gemspec +1 -0
- metadata +29 -7
- data/app/helpers/feeds_helper.rb +0 -5
- data/app/helpers/meta_tags_helper.rb +0 -15
- data/lib/perron/data.rb +0 -145
- data/lib/perron/root.rb +0 -13
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Perron
|
|
4
|
+
class DataSource < SimpleDelegator
|
|
5
|
+
module ClassMethods
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
class_methods do
|
|
9
|
+
def all
|
|
10
|
+
parts = name.to_s.split("::").drop(2)
|
|
11
|
+
identifier = parts.empty? ? name.demodulize.underscore : parts.map(&:underscore).join("/")
|
|
12
|
+
|
|
13
|
+
new(identifier)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def find(id)
|
|
17
|
+
all.find { it[:id] == id || it["id"] == id }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def count = all.size
|
|
21
|
+
|
|
22
|
+
def first = all.first
|
|
23
|
+
|
|
24
|
+
def second = all[1]
|
|
25
|
+
|
|
26
|
+
def third = all[2]
|
|
27
|
+
|
|
28
|
+
def fourth = all[3]
|
|
29
|
+
|
|
30
|
+
def fifth = all[4]
|
|
31
|
+
|
|
32
|
+
def forty_two = all[41]
|
|
33
|
+
|
|
34
|
+
def last = all.last
|
|
35
|
+
|
|
36
|
+
def take(n) = all.first(n)
|
|
37
|
+
|
|
38
|
+
def path_for(identifier)
|
|
39
|
+
path = Pathname.new(identifier)
|
|
40
|
+
|
|
41
|
+
return path.to_s if path.file? && path.absolute?
|
|
42
|
+
|
|
43
|
+
base_path = Rails.root.join("app", "content", "data")
|
|
44
|
+
|
|
45
|
+
SUPPORTED_EXTENSIONS.lazy.map { base_path.join("#{identifier}#{it}") }.find(&:exist?)&.to_s
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def path_for!(identifier)
|
|
49
|
+
path_for(identifier).tap do |path|
|
|
50
|
+
raise Errors::FileNotFoundError, "No data file found for `#{identifier}`" unless path
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def directory?(identifier) = Dir.exist?(Rails.root.join("app", "content", "data", identifier))
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Perron
|
|
4
|
+
class DataSource < SimpleDelegator
|
|
5
|
+
class HelperContext
|
|
6
|
+
include Singleton
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
self.class.include ActionView::Helpers::AssetUrlHelper
|
|
10
|
+
self.class.include ActionView::Helpers::DateHelper
|
|
11
|
+
self.class.include Rails.application.routes.url_helpers
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get_binding = binding
|
|
15
|
+
|
|
16
|
+
def default_url_options = Perron.configuration.default_url_options || {}
|
|
17
|
+
end
|
|
18
|
+
private_constant :HelperContext
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Perron
|
|
4
|
+
class DataSource < SimpleDelegator
|
|
5
|
+
class Item
|
|
6
|
+
def initialize(attributes, identifier:)
|
|
7
|
+
@attributes = attributes.transform_keys(&:to_sym)
|
|
8
|
+
@identifier = identifier
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def [](key) = @attributes[key.to_sym]
|
|
12
|
+
|
|
13
|
+
def association_value(key) = self[key]
|
|
14
|
+
|
|
15
|
+
def to_partial_path
|
|
16
|
+
@to_partial_path ||= begin
|
|
17
|
+
identifier = @identifier.to_s
|
|
18
|
+
collection = File.extname(identifier).present? ? File.basename(identifier, ".*") : identifier
|
|
19
|
+
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.singularize(File.basename(collection)))
|
|
20
|
+
|
|
21
|
+
File.join("content", collection, element)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def method_missing(method_name, *arguments, &block)
|
|
26
|
+
return super if !@attributes.key?(method_name) || arguments.any? || block
|
|
27
|
+
|
|
28
|
+
@attributes[method_name]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
32
|
+
@attributes.key?(method_name) || super
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
private_constant :Item
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "csv"
|
|
4
|
+
|
|
5
|
+
require "perron/data_source/class_methods"
|
|
6
|
+
require "perron/data_source/item"
|
|
7
|
+
require "perron/data_source/helper_context"
|
|
8
|
+
|
|
9
|
+
module Perron
|
|
10
|
+
class DataSource < SimpleDelegator
|
|
11
|
+
include Enumerable
|
|
12
|
+
|
|
13
|
+
include Perron::DataSource::ClassMethods
|
|
14
|
+
|
|
15
|
+
def initialize(identifier)
|
|
16
|
+
@identifier = identifier
|
|
17
|
+
@file_path = self.class.path_for!(identifier)
|
|
18
|
+
@records = records
|
|
19
|
+
|
|
20
|
+
super(records)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def each(&block) = @records.each(&block)
|
|
24
|
+
|
|
25
|
+
def count = @records.count
|
|
26
|
+
|
|
27
|
+
def first(n = nil)
|
|
28
|
+
n ? @records.first(n) : @records.first
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def last = @records.last
|
|
32
|
+
|
|
33
|
+
def [](index) = @records[index]
|
|
34
|
+
|
|
35
|
+
def size = @records.size
|
|
36
|
+
alias_method :length, :size
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
PARSER_METHODS = {
|
|
41
|
+
".yml" => :parse_yaml, ".yaml" => :parse_yaml,
|
|
42
|
+
".json" => :parse_json, ".csv" => :parse_csv
|
|
43
|
+
}.freeze
|
|
44
|
+
SUPPORTED_EXTENSIONS = PARSER_METHODS.keys
|
|
45
|
+
|
|
46
|
+
def records
|
|
47
|
+
content = rendered_from(@file_path)
|
|
48
|
+
data = parsed_from(content, @file_path)
|
|
49
|
+
|
|
50
|
+
unless data.is_a?(Array)
|
|
51
|
+
raise Errors::DataParseError, "Data in `#{@file_path}` must be an array of objects."
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
data.map.with_index do |item, index|
|
|
55
|
+
unless item.is_a?(Hash)
|
|
56
|
+
raise Errors::DataParseError, "Item at index #{index} in `#{@file_path}` must be a hash/object, got #{item.class}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
Item.new(item, identifier: @identifier)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
# def records
|
|
63
|
+
# content = rendered_from(@file_path)
|
|
64
|
+
# data = parsed_from(content, @file_path)
|
|
65
|
+
|
|
66
|
+
# unless data.is_a?(Array)
|
|
67
|
+
# raise Errors::DataParseError, "Data in `#{@file_path}` must be an array of objects."
|
|
68
|
+
# end
|
|
69
|
+
|
|
70
|
+
# data.map { Item.new(it, identifier: @identifier) }
|
|
71
|
+
# end
|
|
72
|
+
|
|
73
|
+
def rendered_from(path)
|
|
74
|
+
raw_content = File.read(path)
|
|
75
|
+
|
|
76
|
+
render_erb(raw_content)
|
|
77
|
+
rescue NameError, ArgumentError, SyntaxError => error
|
|
78
|
+
raise Errors::DataParseError, "Failed to render ERB in `#{path}`: (#{error.class}) #{error.message}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def parsed_from(content, path)
|
|
82
|
+
extension = File.extname(path)
|
|
83
|
+
parser_method = PARSER_METHODS.fetch(extension) do
|
|
84
|
+
raise Errors::UnsupportedDataFormatError, "Unsupported data format: #{extension}. Supported formats: #{SUPPORTED_EXTENSIONS.join(", ")}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
send(parser_method, content, path)
|
|
88
|
+
end
|
|
89
|
+
# def parsed_from(content, path)
|
|
90
|
+
# extension = File.extname(path)
|
|
91
|
+
# parser_method = PARSER_METHODS.fetch(extension) do
|
|
92
|
+
# raise Errors::UnsupportedDataFormatError, "Unsupported data format: #{extension}"
|
|
93
|
+
# end
|
|
94
|
+
|
|
95
|
+
# send(parser_method, content)
|
|
96
|
+
# rescue Psych::SyntaxError, JSON::ParserError, CSV::MalformedCSVError => error
|
|
97
|
+
# raise Errors::DataParseError, "Failed to parse data format in `#{path}`: (#{error.class}) #{error.message}"
|
|
98
|
+
# end
|
|
99
|
+
|
|
100
|
+
def render_erb(content) = ERB.new(content).result(HelperContext.instance.get_binding)
|
|
101
|
+
|
|
102
|
+
def parse_yaml(content, path)
|
|
103
|
+
YAML.safe_load(content, permitted_classes: [Symbol, Time], aliases: true)
|
|
104
|
+
rescue Psych::SyntaxError => error
|
|
105
|
+
line_info = error.line ? " at line #{error.line}" : ""
|
|
106
|
+
column_info = error.column ? ", column #{error.column}" : ""
|
|
107
|
+
|
|
108
|
+
raise Errors::DataParseError, "Invalid YAML syntax in `#{path}`#{line_info}#{column_info}: #{error.problem}"
|
|
109
|
+
end
|
|
110
|
+
# def parse_yaml(content)
|
|
111
|
+
# YAML.safe_load(content, permitted_classes: [Symbol, Time], aliases: true)
|
|
112
|
+
# end
|
|
113
|
+
|
|
114
|
+
def parse_json(content, path)
|
|
115
|
+
JSON.parse(content, symbolize_names: true)
|
|
116
|
+
rescue JSON::ParserError => error
|
|
117
|
+
line_match = error.message.match(/at line (\d+)/)
|
|
118
|
+
line_info = line_match ? " at line #{line_match[1]}" : ""
|
|
119
|
+
|
|
120
|
+
raise Errors::DataParseError, "Invalid JSON syntax in `#{path}`#{line_info}: #{error.message}"
|
|
121
|
+
end
|
|
122
|
+
# def parse_json(content)
|
|
123
|
+
# JSON.parse(content, symbolize_names: true)
|
|
124
|
+
# end
|
|
125
|
+
|
|
126
|
+
def parse_csv(content, path)
|
|
127
|
+
expected_headers = nil
|
|
128
|
+
|
|
129
|
+
CSV.new(content, headers: true, header_converters: :symbol).map.with_index do |row, index|
|
|
130
|
+
expected_headers ||= row.headers
|
|
131
|
+
|
|
132
|
+
if row.headers != expected_headers
|
|
133
|
+
missing = expected_headers - row.headers
|
|
134
|
+
extra = row.headers - expected_headers
|
|
135
|
+
|
|
136
|
+
error_parts = []
|
|
137
|
+
error_parts << "missing columns: #{missing.join(", ")}" if missing.any?
|
|
138
|
+
error_parts << "extra columns: #{extra.join(", ")}" if extra.any?
|
|
139
|
+
|
|
140
|
+
raise Errors::DataParseError, "Column mismatch in `#{path}` at row #{index + 2} (#{error_parts.join("; ")}). Expected: #{expected_headers.join(", ")}"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
row.to_h
|
|
144
|
+
end
|
|
145
|
+
rescue CSV::MalformedCSVError => error
|
|
146
|
+
line_match = error.message.match(/line (\d+)/)
|
|
147
|
+
line_info = line_match ? " at line #{line_match[1]}" : ""
|
|
148
|
+
|
|
149
|
+
raise Errors::DataParseError, "Malformed CSV in `#{path}`#{line_info}: #{error.message}"
|
|
150
|
+
end
|
|
151
|
+
# def parse_csv(content)
|
|
152
|
+
# CSV.new(content, headers: true, header_converters: :symbol).to_a.map(&:to_h)
|
|
153
|
+
# end
|
|
154
|
+
end
|
|
155
|
+
end
|
data/lib/perron/engine.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "perron/output_server"
|
|
4
|
+
require "mata"
|
|
4
5
|
|
|
5
6
|
module Perron
|
|
6
7
|
class Engine < Rails::Engine
|
|
@@ -12,6 +13,17 @@ module Perron
|
|
|
12
13
|
app.middleware.use Perron::OutputServer
|
|
13
14
|
end
|
|
14
15
|
|
|
16
|
+
initializer "perron.configure_hmr", after: :load_config_initializers do |app|
|
|
17
|
+
if Rails.env.development? && Perron.configuration.live_reload
|
|
18
|
+
app.config.middleware.insert_before(
|
|
19
|
+
ActionDispatch::Static,
|
|
20
|
+
Mata,
|
|
21
|
+
watch: Perron.configuration.live_reload_watch_paths,
|
|
22
|
+
skip: Perron.configuration.live_reload_skip_paths
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
15
27
|
rake_tasks do
|
|
16
28
|
load File.expand_path("../tasks/build.rake", __FILE__)
|
|
17
29
|
load File.expand_path("../tasks/clobber.rake", __FILE__)
|
|
@@ -7,6 +7,8 @@ module Perron
|
|
|
7
7
|
class HtmlProcessor
|
|
8
8
|
class SyntaxHighlight < HtmlProcessor::Base
|
|
9
9
|
def process
|
|
10
|
+
Perron.deprecator.deprecation_warning(:syntax_highlight)
|
|
11
|
+
|
|
10
12
|
@html.css('pre > code[class*="language-"]').each do |code_block|
|
|
11
13
|
language = code_block[:class][/(?<=language-)\S+/]
|
|
12
14
|
|
data/lib/perron/output_server.rb
CHANGED
|
@@ -27,21 +27,26 @@ module Perron
|
|
|
27
27
|
|
|
28
28
|
def serve(file_path)
|
|
29
29
|
content = File.read(file_path)
|
|
30
|
+
injected_content = inject_preview_indicator(content)
|
|
30
31
|
|
|
31
32
|
[
|
|
32
33
|
200,
|
|
33
34
|
|
|
34
35
|
{
|
|
35
36
|
"Content-Type" => "text/html; charset=utf-8",
|
|
36
|
-
"Content-Length" =>
|
|
37
|
+
"Content-Length" => injected_content.bytesize.to_s
|
|
37
38
|
},
|
|
38
39
|
|
|
39
|
-
[
|
|
40
|
+
[injected_content]
|
|
40
41
|
]
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
def enabled? = Dir.exist?(output_path)
|
|
44
45
|
|
|
46
|
+
def inject_preview_indicator(content)
|
|
47
|
+
content.gsub(/<title>(.*?)<\/title>/i, "<title>[PREVIEW] \\1</title>")
|
|
48
|
+
end
|
|
49
|
+
|
|
45
50
|
def output_path
|
|
46
51
|
@output_path ||= Rails.root.join(Perron.configuration.output)
|
|
47
52
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Perron
|
|
4
|
+
class Relation < Array
|
|
5
|
+
def initialize(resources = [])
|
|
6
|
+
super
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def where(**conditions)
|
|
10
|
+
filtered = select do |resource|
|
|
11
|
+
conditions.all? do |key, value|
|
|
12
|
+
key_value = resource.public_send(key)
|
|
13
|
+
|
|
14
|
+
if value.is_a?(Array)
|
|
15
|
+
value.map(&:to_s).include?(key_value.to_s)
|
|
16
|
+
else
|
|
17
|
+
key_value.to_s == value.to_s
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Relation.new(filtered)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def limit(count) = Relation.new(first(count))
|
|
26
|
+
|
|
27
|
+
def offset(count) = Relation.new(drop(count))
|
|
28
|
+
|
|
29
|
+
def order(attribute, direction = :asc)
|
|
30
|
+
if attribute.is_a?(Hash)
|
|
31
|
+
attribute, direction = attribute.first
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
sorted = sort_by { it.public_send(attribute) }
|
|
35
|
+
|
|
36
|
+
Relation.new((direction == :desc) ? sorted.reverse : sorted)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def pluck(*attributes)
|
|
40
|
+
raise ArgumentError, "wrong number of arguments (given 0, expected 1+)" if attributes.empty?
|
|
41
|
+
|
|
42
|
+
map do |resource|
|
|
43
|
+
if attributes.size == 1
|
|
44
|
+
resource.public_send(attributes.first)
|
|
45
|
+
else
|
|
46
|
+
attributes.map { resource.public_send(it) }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -66,7 +66,7 @@ module Perron
|
|
|
66
66
|
def records_for_ids(associated_class, ids)
|
|
67
67
|
ids = Array(ids)
|
|
68
68
|
|
|
69
|
-
associated_class.all.select { ids.include?(it[:id]) || ids.include?(it["id"]) }
|
|
69
|
+
Perron::Relation.new(associated_class.all.select { ids.include?(it[:id]) || ids.include?(it["id"]) })
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def records_for_foreign_key(associated_class, association_name, **options)
|
|
@@ -74,7 +74,7 @@ module Perron
|
|
|
74
74
|
primary_key_method = options.fetch(:primary_key, :slug)
|
|
75
75
|
lookup_value = public_send(primary_key_method)
|
|
76
76
|
|
|
77
|
-
associated_class.all.select { it.association_value(foreign_key) == lookup_value }
|
|
77
|
+
Perron::Relation.new(associated_class.all.select { it.association_value(foreign_key) == lookup_value })
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def inverse_association_name = self.class.name.demodulize.underscore
|
|
@@ -8,11 +8,23 @@ module Perron
|
|
|
8
8
|
class_methods do
|
|
9
9
|
def find(slug) = collection.find(slug, name.constantize)
|
|
10
10
|
|
|
11
|
+
def find!(slug) = collection.find!(slug, name.constantize)
|
|
12
|
+
|
|
11
13
|
def all = collection.all(self)
|
|
12
14
|
|
|
15
|
+
def where(**conditions) = all.where(**conditions)
|
|
16
|
+
|
|
17
|
+
def limit(count) = all.limit(count)
|
|
18
|
+
|
|
19
|
+
def offset(count) = all.offset(count)
|
|
20
|
+
|
|
13
21
|
def count = all.size
|
|
14
22
|
|
|
15
|
-
def
|
|
23
|
+
def order(attribute, direction = :asc) = all.order(attribute, direction)
|
|
24
|
+
|
|
25
|
+
def first(n = nil)
|
|
26
|
+
n ? all.first(n) : all[0]
|
|
27
|
+
end
|
|
16
28
|
|
|
17
29
|
def second = all[1]
|
|
18
30
|
|
|
@@ -30,9 +42,7 @@ module Perron
|
|
|
30
42
|
|
|
31
43
|
def collection = Collection.new(collection_name)
|
|
32
44
|
|
|
33
|
-
def root
|
|
34
|
-
collection_name.pages? && collection.find_by_file_name("root", name.constantize)
|
|
35
|
-
end
|
|
45
|
+
def root = all.find(&:root?)
|
|
36
46
|
|
|
37
47
|
def model_name
|
|
38
48
|
@model_name ||= ActiveModel::Name.new(self, nil, name.demodulize.to_s)
|
|
@@ -22,8 +22,6 @@ module Perron
|
|
|
22
22
|
config.feeds.json.path = "feeds/#{collection.name.demodulize.parameterize}.json"
|
|
23
23
|
config.feeds.json.max_items = 20
|
|
24
24
|
|
|
25
|
-
config.linked_data = ActiveSupport::OrderedOptions.new
|
|
26
|
-
|
|
27
25
|
config.related_posts = ActiveSupport::OrderedOptions.new
|
|
28
26
|
config.related_posts.enabled = false
|
|
29
27
|
config.related_posts.max = 5
|
|
@@ -39,18 +37,17 @@ module Perron
|
|
|
39
37
|
end
|
|
40
38
|
|
|
41
39
|
class Options < ActiveSupport::OrderedOptions
|
|
42
|
-
def
|
|
43
|
-
if
|
|
44
|
-
key
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return self[key].merge!(value) if self[key].is_a?(ActiveSupport::OrderedOptions) && value.is_a?(Hash)
|
|
40
|
+
def []=(key, value)
|
|
41
|
+
if self[key].is_a?(ActiveSupport::OrderedOptions) && value.is_a?(Hash)
|
|
42
|
+
self[key].merge!(value)
|
|
43
|
+
else
|
|
44
|
+
super
|
|
48
45
|
end
|
|
49
|
-
|
|
50
|
-
super
|
|
51
46
|
end
|
|
52
47
|
|
|
53
|
-
def respond_to_missing?(name, include_private = false)
|
|
48
|
+
def respond_to_missing?(name, include_private = false)
|
|
49
|
+
name.to_s.end_with?("=") || super
|
|
50
|
+
end
|
|
54
51
|
end
|
|
55
52
|
private_constant :Options
|
|
56
53
|
end
|
data/lib/perron/resource/core.rb
CHANGED
|
@@ -10,6 +10,17 @@ module Perron
|
|
|
10
10
|
def to_model = self
|
|
11
11
|
|
|
12
12
|
def model_name = self.class.model_name
|
|
13
|
+
|
|
14
|
+
def association_value(key) = metadata[key]
|
|
15
|
+
|
|
16
|
+
def to_partial_path
|
|
17
|
+
@to_partial_path ||= begin
|
|
18
|
+
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self.class.model_name))
|
|
19
|
+
collection = ActiveSupport::Inflector.tableize(self.class.model_name)
|
|
20
|
+
|
|
21
|
+
File.join("content", collection, element)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
13
24
|
end
|
|
14
25
|
end
|
|
15
26
|
end
|
|
@@ -47,7 +47,7 @@ module Perron
|
|
|
47
47
|
|
|
48
48
|
def canonical_url
|
|
49
49
|
return @frontmatter[:canonical_url] if @frontmatter[:canonical_url]
|
|
50
|
-
return Rails.application.routes.url_helpers.root_url(**Perron.configuration.default_url_options) if @resource.
|
|
50
|
+
return Rails.application.routes.url_helpers.root_url(**Perron.configuration.default_url_options) if @resource.root?
|
|
51
51
|
|
|
52
52
|
begin
|
|
53
53
|
Rails.application.routes.url_helpers.polymorphic_url(
|
|
@@ -5,28 +5,28 @@ module Perron
|
|
|
5
5
|
class Resource
|
|
6
6
|
class Related
|
|
7
7
|
module StopWords
|
|
8
|
+
ALL = Set[
|
|
9
|
+
"a", "about", "above", "after", "again", "against", "all", "am",
|
|
10
|
+
"an", "and", "any", "are", "as", "at", "be", "because", "been",
|
|
11
|
+
"before", "being", "below", "between", "both", "but", "by", "can",
|
|
12
|
+
"did", "do", "does", "doing", "down", "during", "each", "few",
|
|
13
|
+
"for", "from", "further", "had", "has", "have", "having", "he",
|
|
14
|
+
"her", "here", "hers", "herself", "him", "himself", "his", "how",
|
|
15
|
+
"i", "if", "in", "into", "is", "it", "its", "itself", "just",
|
|
16
|
+
"me", "more", "most", "my", "myself", "no", "nor", "not", "now",
|
|
17
|
+
"of", "off", "on", "once", "only", "or", "other", "our", "ours",
|
|
18
|
+
"ourselves", "out", "over", "own", "s", "same", "she", "should",
|
|
19
|
+
"so", "some", "such", "t", "than", "that", "the", "their",
|
|
20
|
+
"theirs", "them", "themselves", "then", "there", "these", "they",
|
|
21
|
+
"this", "those", "through", "to", "too", "under", "until", "up",
|
|
22
|
+
"very", "was", "we", "were", "what", "when", "where", "which",
|
|
23
|
+
"while", "who", "whom", "why", "will", "with", "you", "your",
|
|
24
|
+
"yours", "yourself", "yourselves"
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
8
27
|
module_function
|
|
9
28
|
|
|
10
|
-
def all
|
|
11
|
-
Set[
|
|
12
|
-
"a", "about", "above", "after", "again", "against", "all", "am",
|
|
13
|
-
"an", "and", "any", "are", "as", "at", "be", "because", "been",
|
|
14
|
-
"before", "being", "below", "between", "both", "but", "by", "can",
|
|
15
|
-
"did", "do", "does", "doing", "down", "during", "each", "few",
|
|
16
|
-
"for", "from", "further", "had", "has", "have", "having", "he",
|
|
17
|
-
"her", "here", "hers", "herself", "him", "himself", "his", "how",
|
|
18
|
-
"i", "if", "in", "into", "is", "it", "its", "itself", "just",
|
|
19
|
-
"me", "more", "most", "my", "myself", "no", "nor", "not", "now",
|
|
20
|
-
"of", "off", "on", "once", "only", "or", "other", "our", "ours",
|
|
21
|
-
"ourselves", "out", "over", "own", "s", "same", "she", "should",
|
|
22
|
-
"so", "some", "such", "t", "than", "that", "the", "their",
|
|
23
|
-
"theirs", "them", "themselves", "then", "there", "these", "they",
|
|
24
|
-
"this", "those", "through", "to", "too", "under", "until", "up",
|
|
25
|
-
"very", "was", "we", "were", "what", "when", "where", "which",
|
|
26
|
-
"while", "who", "whom", "why", "will", "with", "you", "your",
|
|
27
|
-
"yours", "yourself", "yourselves"
|
|
28
|
-
]
|
|
29
|
-
end
|
|
29
|
+
def all = ALL
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
end
|