perron 0.18.0 → 1.0.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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/controllers/perron/concierge_controller.rb +13 -0
  4. data/app/helpers/perron/markdown_helper.rb +2 -2
  5. data/app/views/perron/concierge/show.html.erb +271 -0
  6. data/lib/generators/rails/content/USAGE +21 -4
  7. data/lib/generators/rails/content/content_generator.rb +16 -12
  8. data/lib/generators/rails/content/templates/controller.rb.tt +6 -0
  9. data/lib/generators/rails/content/templates/model.rb.tt +1 -1
  10. data/lib/perron/assets/icon.png +0 -0
  11. data/lib/perron/assets/icon.svg +1 -0
  12. data/lib/perron/configuration.rb +4 -0
  13. data/lib/perron/data_source/class_methods.rb +8 -0
  14. data/lib/perron/data_source.rb +14 -29
  15. data/lib/perron/development_feed_server.rb +69 -0
  16. data/lib/perron/engine.rb +29 -1
  17. data/lib/perron/errors.rb +2 -0
  18. data/lib/perron/feeds.rb +4 -3
  19. data/lib/perron/html_processor/absolute_urls.rb +27 -0
  20. data/lib/perron/html_processor/base.rb +2 -2
  21. data/lib/perron/html_processor.rb +7 -11
  22. data/lib/{generators/perron/templates → perron/install}/README.md.tt +7 -9
  23. data/lib/perron/install.rb +23 -0
  24. data/lib/perron/markdown.rb +2 -2
  25. data/lib/perron/output_server.rb +9 -0
  26. data/lib/perron/resource/adjacency.rb +70 -0
  27. data/lib/perron/resource/associations.rb +1 -1
  28. data/lib/perron/resource/configuration.rb +9 -4
  29. data/lib/perron/resource/metadata.rb +10 -1
  30. data/lib/perron/resource/publishable.rb +2 -0
  31. data/lib/perron/resource/related.rb +32 -31
  32. data/lib/perron/resource/sourceable.rb +39 -9
  33. data/lib/perron/resource.rb +2 -0
  34. data/lib/perron/site/builder/assets.rb +1 -1
  35. data/lib/perron/site/builder/feeds/atom.erb +44 -0
  36. data/lib/perron/site/builder/feeds/atom.rb +41 -0
  37. data/lib/perron/site/builder/feeds/json.erb +19 -0
  38. data/lib/perron/site/builder/feeds/json.rb +7 -33
  39. data/lib/perron/site/builder/feeds/rss.erb +28 -0
  40. data/lib/perron/site/builder/feeds/rss.rb +6 -28
  41. data/lib/perron/site/builder/feeds/template.rb +63 -0
  42. data/lib/perron/site/builder/feeds.rb +8 -3
  43. data/lib/perron/site/builder/paths.rb +58 -14
  44. data/lib/perron/site/builder/route_resources.rb +79 -0
  45. data/lib/perron/site/builder/sitemap.rb +71 -20
  46. data/lib/perron/site/builder.rb +1 -1
  47. data/lib/perron/site/validate.rb +1 -2
  48. data/lib/perron/site.rb +7 -0
  49. data/lib/perron/tasks/build.rake +6 -7
  50. data/lib/perron/tasks/install.rake +12 -0
  51. data/lib/perron/version.rb +1 -1
  52. metadata +18 -5
  53. data/lib/generators/perron/install_generator.rb +0 -32
  54. data/lib/perron/html_processor/syntax_highlight.rb +0 -32
  55. /data/lib/{generators/perron/templates → perron/install}/initializer.rb.tt +0 -0
@@ -0,0 +1,44 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <feed xmlns="http://www.w3.org/2005/Atom">
3
+ <generator uri="https://perron.railsdesigner.com/" version="<%= Perron::VERSION %>">Perron</generator>
4
+ <id><%= current_feed_url.call %></id>
5
+ <title><%= config.title.presence || @configuration.site_name %></title>
6
+ <subtitle><%= config.description.presence || @configuration.site_description %></subtitle>
7
+ <link href="<%= current_feed_url.call %>" rel="self" type="application/atom+xml"/>
8
+ <link href="<%= @configuration.url %>" rel="alternate" type="text/html"/>
9
+ <updated><%= resources.first&.published_at&.iso8601 || Time.current.iso8601 %></updated>
10
+
11
+ <% feed_author = config.author || { name: @configuration.site_name, email: "noreply@#{URI.parse(@configuration.url).host}" } %>
12
+ <author>
13
+ <% if feed_author[:name] %><name><%= feed_author[:name] %></name><% end %>
14
+ <% if feed_author[:email] %><email><%= feed_author[:email] %></email><% end %>
15
+ </author>
16
+
17
+ <% resources.each do |resource| %>
18
+ <entry>
19
+ <id><%= url_for_resource.call(resource) || "#{@configuration.url}/posts/#{resource.id}" %></id>
20
+ <title><%= resource.metadata.title %></title>
21
+ <link href="<%= url_for_resource.call(resource) %>" rel="alternate" type="text/html"/>
22
+ <published><%= resource.published_at&.iso8601 %></published>
23
+ <updated><%= (resource.metadata.updated_at || resource.published_at)&.iso8601 %></updated>
24
+
25
+ <% entry_author = author.call(resource); if entry_author %>
26
+ <author>
27
+ <% if entry_author.name %><name><%= entry_author.name %></name><% end %>
28
+ <% if entry_author.email %><email><%= entry_author.email %></email><% end %>
29
+ </author>
30
+ <% end %>
31
+
32
+ <% base_url = url_for_resource.call(resource) %>
33
+ <% if base_url %>
34
+ <content type="html" xml:base="<%= base_url %>"><![CDATA[<%= Perron::Markdown.render(resource.content, processors: ["absolute_urls"]) %>]]></content>
35
+ <% else %>
36
+ <content type="html"><![CDATA[<%= Perron::Markdown.render(resource.content, processors: ["absolute_urls"]) %>]]></content>
37
+ <% end %>
38
+
39
+ <% resource.metadata.tags&.each do |tag| %>
40
+ <category term="<%= tag %>"/>
41
+ <% end %>
42
+ </entry>
43
+ <% end %>
44
+ </feed>
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "perron/site/builder/feeds/author"
4
+ require "perron/site/builder/feeds/template"
5
+
6
+ module Perron
7
+ module Site
8
+ class Builder
9
+ class Feeds
10
+ class Atom
11
+ include Feeds::Author
12
+ include Feeds::Template
13
+
14
+ def initialize(collection:)
15
+ @collection = collection
16
+ @configuration = Perron.configuration
17
+ end
18
+
19
+ def generate
20
+ return if resources.empty?
21
+
22
+ template = find_template("atom")
23
+ return unless template
24
+
25
+ render(template, feed_configuration)
26
+ end
27
+
28
+ private
29
+
30
+ def resources
31
+ @resource ||= @collection.resources
32
+ .reject { it.metadata.feed == false }
33
+ .sort_by { it.metadata.published_at || it.metadata.updated_at || Time.current }
34
+ .reverse
35
+ .take(feed_configuration.max_items)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ <%= {
2
+ generator: "Perron (#{Perron::VERSION})",
3
+ version: "https://jsonfeed.org/version/1.1",
4
+ home_page_url: @configuration.url,
5
+ title: config.title.presence || @configuration.site_name,
6
+ description: config.description.presence || @configuration.site_description,
7
+
8
+ items: resources.map { |resource|
9
+ item_author = author.call(resource)
10
+ {
11
+ id: resource.id,
12
+ url: url_for_resource.call(resource),
13
+ date_published: resource.published_at&.iso8601,
14
+ title: resource.metadata.title,
15
+ authors: (item_author && item_author.name ? [{ name: item_author.name, email: item_author.email, url: item_author.url, avatar: item_author.avatar }.compact] : nil),
16
+ content_html: Perron::Markdown.render(resource.content, processors: ["absolute_urls"])
17
+ }.compact
18
+ }
19
+ }.to_json %>
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
3
  require "perron/site/builder/feeds/author"
4
+ require "perron/site/builder/feeds/template"
5
5
 
6
6
  module Perron
7
7
  module Site
@@ -9,6 +9,7 @@ module Perron
9
9
  class Feeds
10
10
  class Json
11
11
  include Feeds::Author
12
+ include Feeds::Template
12
13
 
13
14
  def initialize(collection:)
14
15
  @collection = collection
@@ -16,50 +17,23 @@ module Perron
16
17
  end
17
18
 
18
19
  def generate
19
- return nil if resources.empty?
20
+ return if resources.empty?
20
21
 
21
- hash = Rails.application.routes.url_helpers.with_options(@configuration.default_url_options) do |url|
22
- {
23
- generator: "Perron (#{Perron::VERSION})",
24
- version: "https://jsonfeed.org/version/1.1",
25
- home_page_url: @configuration.url,
26
- title: feed_configuration.title.presence || @configuration.site_name,
27
- description: feed_configuration.description.presence || @configuration.site_description,
28
- items: resources.map do |resource|
29
- {
30
- id: resource.id,
31
- url: url.polymorphic_url(resource, ref: feed_configuration.ref).delete_suffix("?ref="),
32
- date_published: resource.published_at&.iso8601,
33
- authors: authors(resource),
34
- title: resource.metadata.title,
35
- content_html: Perron::Markdown.render(resource.content)
36
- }
37
- end
38
- }
39
- end
22
+ template = find_template("json")
23
+ return unless template
40
24
 
41
- JSON.pretty_generate hash
25
+ render(template, feed_configuration)
42
26
  end
43
27
 
44
28
  private
45
29
 
46
30
  def resources
47
- @resources ||= @collection.resources
31
+ @resource ||= @collection.resources
48
32
  .reject { it.metadata.feed == false }
49
33
  .sort_by { it.metadata.published_at || it.metadata.updated_at || Time.current }
50
34
  .reverse
51
35
  .take(feed_configuration.max_items)
52
36
  end
53
-
54
- def feed_configuration = @collection.configuration.feeds.json
55
-
56
- def authors(resource)
57
- author = author(resource)
58
-
59
- return nil unless author&.name
60
-
61
- [{name: author.name, email: author.email, url: author.url, avatar: author.avatar}.compact].presence
62
- end
63
37
  end
64
38
  end
65
39
  end
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
3
+ <channel>
4
+ <generator>Perron (<%= Perron::VERSION %>)</generator>
5
+ <link><%= @configuration.url %></link>
6
+ <title><%= config.title.presence || @configuration.site_name %></title>
7
+ <description><%= config.description.presence || @configuration.site_description %></description>
8
+
9
+ <% resources.each do |resource| %>
10
+ <item>
11
+ <guid isPermaLink="false"><%= resource.id %></guid>
12
+
13
+ <% resource_url = url_for_resource.call(resource) %>
14
+ <% if resource_url %>
15
+ <link><%= resource_url %></link>
16
+ <% end %>
17
+
18
+ <pubDate><%= resource.published_at&.rfc822 %></pubDate>
19
+ <% entry_author = author.call(resource); if entry_author && entry_author.email %>
20
+ <author><%= entry_author.name ? "#{entry_author.email} (#{entry_author.name})" : entry_author.email %></author>
21
+ <% end %>
22
+ <title><%= resource.metadata.title %></title>
23
+
24
+ <description><![CDATA[<%= Perron::Markdown.render(resource.content, processors: ["absolute_urls"]) %>]]></description>
25
+ </item>
26
+ <% end %>
27
+ </channel>
28
+ </rss>
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "nokogiri"
4
3
  require "perron/site/builder/feeds/author"
4
+ require "perron/site/builder/feeds/template"
5
5
 
6
6
  module Perron
7
7
  module Site
@@ -9,6 +9,7 @@ module Perron
9
9
  class Feeds
10
10
  class Rss
11
11
  include Feeds::Author
12
+ include Feeds::Template
12
13
 
13
14
  def initialize(collection:)
14
15
  @collection = collection
@@ -18,31 +19,10 @@ module Perron
18
19
  def generate
19
20
  return if resources.empty?
20
21
 
21
- Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
22
- xml.rss(:version => "2.0", "xmlns:atom" => "http://www.w3.org/2005/Atom") do
23
- xml.channel do
24
- xml.generator "Perron (#{Perron::VERSION})"
25
- xml.title feed_configuration.title.presence || @configuration.site_name
26
- xml.description feed_configuration.description.presence || @configuration.site_description
27
- xml.link @configuration.url
28
-
29
- Rails.application.routes.url_helpers.with_options(@configuration.default_url_options) do |url|
30
- resources.each do |resource|
31
- xml.item do
32
- xml.guid resource.id
33
- xml.link url.polymorphic_url(resource, ref: feed_configuration.ref).delete_suffix("?ref="), isPermaLink: true
34
- xml.pubDate(resource.published_at&.rfc822)
35
- if (author = author(resource)) && author.email
36
- xml.author author.name ? "#{author.email} (#{author.name})" : author.email
37
- end
38
- xml.title resource.metadata.title
39
- xml.description { xml.cdata(Perron::Markdown.render(resource.content)) }
40
- end
41
- end
42
- end
43
- end
44
- end
45
- end.to_xml
22
+ template = find_template("rss")
23
+ return unless template
24
+
25
+ render(template, feed_configuration)
46
26
  end
47
27
 
48
28
  private
@@ -54,8 +34,6 @@ module Perron
54
34
  .reverse
55
35
  .take(feed_configuration.max_items)
56
36
  end
57
-
58
- def feed_configuration = @collection.configuration.feeds.rss
59
37
  end
60
38
  end
61
39
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Perron
4
+ module Site
5
+ class Builder
6
+ class Feeds
7
+ module Template
8
+ def find_template(type)
9
+ collection_name = @collection.name.to_s.pluralize
10
+
11
+ user_path = Rails.root.join("app/views/content/#{collection_name}/#{type}.erb")
12
+ return user_path if File.exist?(user_path)
13
+
14
+ default_path = Pathname.new(__dir__).join("#{type}.erb")
15
+ return default_path if File.exist?(default_path)
16
+
17
+ nil
18
+ end
19
+
20
+ def render(template_path, feed_config)
21
+ template = File.read(template_path)
22
+ b = binding
23
+
24
+ b.local_variable_set(:collection, @collection)
25
+ b.local_variable_set(:resources, resources)
26
+ b.local_variable_set(:config, feed_config)
27
+ b.local_variable_set(:routes, routes)
28
+ b.local_variable_set(:author, method(:author))
29
+ b.local_variable_set(:url_for_resource, method(:url_for_resource))
30
+ b.local_variable_set(:current_feed_url, method(:current_feed_url))
31
+
32
+ ERB.new(template).result(b)
33
+ end
34
+
35
+ def url_for_resource(resource)
36
+ routes
37
+ .polymorphic_url(resource, **@configuration.default_url_options.merge(ref: feed_configuration.ref))
38
+ .delete_suffix("?ref=")
39
+ rescue
40
+ nil
41
+ end
42
+
43
+ def current_feed_url
44
+ path = feed_configuration.path || "feed.atom"
45
+ URI.join(@configuration.url, path).to_s
46
+ end
47
+
48
+ def routes
49
+ Rails.application.routes.url_helpers
50
+ end
51
+
52
+ def feed_configuration
53
+ case self.class.name.demodulize
54
+ when "Rss" then @collection.configuration.feeds.rss
55
+ when "Atom" then @collection.configuration.feeds.atom
56
+ when "Json" then @collection.configuration.feeds.json
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "perron/site/builder/feeds/rss"
3
+ require "perron/site/builder/feeds/atom"
4
4
  require "perron/site/builder/feeds/json"
5
+ require "perron/site/builder/feeds/rss"
5
6
 
6
7
  module Perron
7
8
  module Site
@@ -17,13 +18,17 @@ module Perron
17
18
 
18
19
  config = collection.configuration.feeds
19
20
 
20
- if config.rss.enabled
21
- create_file at: config.rss.path, with: Rss.new(collection: collection).generate
21
+ if config.atom.enabled
22
+ create_file at: config.atom.path, with: Atom.new(collection: collection).generate
22
23
  end
23
24
 
24
25
  if config.json.enabled
25
26
  create_file at: config.json.path, with: Json.new(collection: collection).generate
26
27
  end
28
+
29
+ if config.rss.enabled
30
+ create_file at: config.rss.path, with: Rss.new(collection: collection).generate
31
+ end
27
32
  end
28
33
  end
29
34
 
@@ -1,36 +1,80 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "perron/site/builder/route_resources"
4
+
3
5
  module Perron
4
6
  module Site
5
7
  class Builder
6
8
  class Paths
7
- def initialize(collection, paths)
8
- @collection, @paths = collection, paths
9
+ include RouteResources
10
+
11
+ def initialize(paths)
12
+ @paths = paths
9
13
  end
10
14
 
11
15
  def get
12
- @paths << routes.public_send(index_path) if routes.respond_to?(index_path)
13
-
14
- if routes.respond_to?(show_path)
15
- @collection.send(:load_resources).select(&:buildable?).each do |resource|
16
- next if resource.root?
16
+ buildable_routes.each do |route|
17
+ paths_for(route).each { @paths << it }
18
+ end
19
+ end
17
20
 
18
- @paths << routes.public_send(show_path, resource)
21
+ private
19
22
 
20
- (resource.class.try(:nested_routes) || []).each do |nested|
21
- @paths << routes.polymorphic_path([resource, nested])
22
- end
23
+ def paths_for(route)
24
+ case route.defaults[:action]
25
+ when "index"
26
+ required_params = route.required_keys - [:controller, :action]
27
+ if required_params.any?
28
+ raise "Route `#{route.name}` (#{route.path.spec}) is an index route but requires parameters #{required_params}. Perron doesn't know how to generate these parameters."
23
29
  end
30
+
31
+ [routes.public_send("#{route.name}_path")]
32
+ when "show" then show_paths_for(route)
33
+ else []
24
34
  end
25
35
  end
26
36
 
27
- private
37
+ def show_paths_for(route)
38
+ resources_for(route).reject(&:root?).map do |resource|
39
+ routes.public_send("#{route.name}_path", resource)
40
+ end
41
+ end
28
42
 
29
43
  def routes = Rails.application.routes.url_helpers
30
44
 
31
- def index_path = "#{@collection.name}_path"
45
+ class ConstraintCollection
46
+ def initialize(values)
47
+ @values = values
48
+ end
32
49
 
33
- def show_path = "#{@collection.name.singularize}_path"
50
+ def load_resources
51
+ @values.map { ConstraintResource.new(it) }
52
+ end
53
+ end
54
+
55
+ class ConstraintResource
56
+ def initialize(value)
57
+ @value = value
58
+ end
59
+
60
+ def to_param = @value
61
+
62
+ def buildable? = true
63
+
64
+ def root? = false
65
+
66
+ def metadata = Metadata.new
67
+
68
+ class Metadata
69
+ def sitemap = nil
70
+
71
+ def sitemap_priority = nil
72
+
73
+ def sitemap_change_frequency = nil
74
+
75
+ def updated_at = nil
76
+ end
77
+ end
34
78
  end
35
79
  end
36
80
  end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Perron
4
+ module Site
5
+ class Builder
6
+ module RouteResources
7
+ def buildable_routes
8
+ Rails.application.routes.routes.select do |route|
9
+ route.defaults[:controller]&.start_with?("content/") &&
10
+ %w[index show].include?(route.defaults[:action])
11
+ end
12
+ end
13
+
14
+ def resources_for(route)
15
+ constraint_resources = constraint_resources_from(route)
16
+ return constraint_resources if constraint_resources.any?
17
+
18
+ collection = collection_for(route)
19
+ return [] unless collection
20
+
21
+ resources = collection.send(:load_resources).select(&:buildable?)
22
+ constraint = route.path.requirements[:id]
23
+
24
+ constraint.is_a?(Regexp) ? resources.select { constraint.match?(it.to_param) } : resources
25
+ end
26
+
27
+ def collection_for(route)
28
+ collection = standard_collection(route)
29
+ return collection if collection
30
+
31
+ collection = parent_collection(route)
32
+ return collection if collection
33
+
34
+ constraint_collection(route)
35
+ end
36
+
37
+ private
38
+
39
+ def standard_collection(route)
40
+ controller_class = "#{route.defaults[:controller]}_controller".classify.constantize
41
+ collection_name = controller_class.respond_to?(:collection_name) ? controller_class.collection_name : route.defaults[:controller].split("/").last.chomp("_controller")
42
+
43
+ Perron::Site.find_collection(collection_name)
44
+ rescue NameError
45
+ Perron::Site.find_collection(route.defaults[:controller].split("/").last.chomp("_controller"))
46
+ end
47
+
48
+ def parent_collection(route)
49
+ parent_controller = route.defaults[:controller].split("/")[-2]
50
+ return nil unless parent_controller
51
+
52
+ Perron::Site.find_collection(parent_controller.chomp("_controller"))
53
+ end
54
+
55
+ def constraint_collection(route)
56
+ constraint = route.path.requirements[:id]
57
+ return nil unless constraint.is_a?(Regexp)
58
+ return nil unless constraint.source.include?("|")
59
+
60
+ values = constraint.source.split("|")
61
+ return nil unless values.all? { it.match?(/\A\w+\z/) }
62
+
63
+ Paths::ConstraintCollection.new(values)
64
+ end
65
+
66
+ def constraint_resources_from(route)
67
+ constraint = route.path.requirements[:id]
68
+ return [] unless constraint.is_a?(Regexp)
69
+ return [] unless constraint.source.include?("|")
70
+
71
+ values = constraint.source.split("|")
72
+ return [] unless values.all? { it.match?(/\A\w+\z/) }
73
+
74
+ values.map { Paths::ConstraintResource.new(it) }
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "perron/site/builder/route_resources"
4
+
3
5
  module Perron
4
6
  module Site
5
7
  class Builder
6
8
  class Sitemap
9
+ include RouteResources
10
+
7
11
  def initialize(output_path)
8
12
  @output_path = output_path
9
13
  end
@@ -11,12 +15,27 @@ module Perron
11
15
  def generate
12
16
  return if !Perron.configuration.sitemap.enabled
13
17
 
18
+ added_urls = Set.new
19
+
14
20
  xml = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |builder|
15
21
  builder.urlset(xmlns: "http://www.sitemaps.org/schemas/sitemap/0.9") do
16
22
  add_additional_routes(with: builder)
17
23
 
18
- Perron::Site.collections.each do |collection|
19
- add_urls_for(collection, with: builder)
24
+ buildable_routes.each do |route|
25
+ case route.defaults[:action]
26
+ when "index"
27
+ add_index_url(route, with: builder, added_urls: added_urls)
28
+ when "show"
29
+ if (route.required_keys - %i[controller action]).empty?
30
+ add_index_url(route, with: builder, added_urls: added_urls)
31
+ else
32
+ resources_for(route).reject(&:root?).each do |resource|
33
+ next if resource.metadata.sitemap == false
34
+
35
+ add_show_url(route, resource, with: builder, added_urls: added_urls)
36
+ end
37
+ end
38
+ end
20
39
  end
21
40
  end
22
41
  end.to_xml
@@ -41,31 +60,63 @@ module Perron
41
60
  end
42
61
  end
43
62
 
44
- def add_urls_for(collection, with:)
45
- return if collection.configuration.blank?
46
- return if collection.configuration.sitemap.exclude == true
63
+ def add_index_url(route, with:, added_urls:)
64
+ collection = collection_for(route)
65
+ return if collection&.configuration&.sitemap&.enabled == false
47
66
 
48
- collection.resources.each do |resource|
49
- next if resource.metadata.sitemap == false
67
+ url_options = Perron.configuration.default_url_options
68
+ url_options = url_options.merge(trailing_slash: false) if route.path.spec.to_s.match?(/\.\w+/)
50
69
 
51
- priority = resource.metadata.sitemap_priority || collection.configuration.sitemap.priority || Perron.configuration.sitemap.priority
52
- change_frequency = resource.metadata.sitemap_change_frequency || collection.configuration.sitemap.change_frequency || Perron.configuration.sitemap.change_frequency
70
+ routes.with_options(url_options) do |url|
71
+ loc = url.public_send("#{route.name}_url")
72
+ next if added_urls.include?(loc)
73
+ added_urls << loc
53
74
 
54
- Rails.application.routes.url_helpers.with_options(Perron.configuration.default_url_options) do |url|
55
- with.url do
56
- with.loc resource.root? ? url.root_url : url.polymorphic_url(resource)
57
- with.priority priority
58
- with.changefreq change_frequency
59
- begin
60
- with.lastmod resource.metadata.updated_at.iso8601
61
- rescue
62
- Time.current.iso8601
63
- end
64
- end
75
+ lastmod = last_modified_for(collection)
76
+
77
+ with.url do
78
+ with.loc loc
79
+ with.priority Perron.configuration.sitemap.priority
80
+ with.changefreq Perron.configuration.sitemap.change_frequency
81
+ with.lastmod lastmod if lastmod
82
+ end
83
+ end
84
+ end
85
+
86
+ def add_show_url(route, resource, with:, added_urls:)
87
+ collection = collection_for(route)
88
+ return if collection&.configuration&.sitemap&.enabled == false
89
+
90
+ priority = resource.metadata.sitemap_priority || collection&.configuration&.sitemap&.priority || Perron.configuration.sitemap.priority
91
+ change_frequency = resource.metadata.sitemap_change_frequency || collection&.configuration&.sitemap&.change_frequency || Perron.configuration.sitemap.change_frequency
92
+
93
+ url_options = Perron.configuration.default_url_options
94
+ url_options = url_options.merge(trailing_slash: false) if route.path.spec.to_s.match?(/\.\w+/)
95
+
96
+ loc = resource.root? ? routes.root_url(url_options) : routes.public_send("#{route.name}_url", resource, **url_options)
97
+
98
+ return if added_urls.include?(loc)
99
+ added_urls << loc
100
+
101
+ routes.with_options(Perron.configuration.default_url_options) do |url|
102
+ with.url do
103
+ with.loc loc
104
+ with.priority priority
105
+ with.changefreq change_frequency
106
+ with.lastmod resource.metadata.updated_at&.iso8601 if resource.metadata.updated_at.present?
65
107
  end
66
108
  end
67
109
  end
68
110
 
111
+ def last_modified_for(collection)
112
+ return unless collection
113
+
114
+ resources = collection.send(:load_resources).select(&:buildable?)
115
+ dates = resources.filter_map { it.metadata.updated_at || it.metadata.publication_date }
116
+
117
+ dates.max&.iso8601
118
+ end
119
+
69
120
  def routes = Rails.application.routes.url_helpers
70
121
  end
71
122
  end
@@ -42,7 +42,7 @@ module Perron
42
42
  def paths
43
43
  Set.new.tap do |paths|
44
44
  Perron::Site::Builder::AdditionalRoutes.new(paths).get
45
- Perron::Site.collections.each { Perron::Site::Builder::Paths.new(it, paths).get }
45
+ Perron::Site::Builder::Paths.new(paths).get
46
46
  end
47
47
  end
48
48