perron 0.6.0 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f91a86bc7ff57f28eb58ef9aac6b1d3e94c008a3570df359aca5f5e6b416c7e7
4
- data.tar.gz: 53cd0a23ce3fbcd1fe90aa07a26577f34e66be5a8fd018c596498e34c67c5718
3
+ metadata.gz: 9605385eb10b149f1f679d423a2367ed7f35684d381699692d329a1d05a2f6ea
4
+ data.tar.gz: e3803d65512af0de01c0cee99afdbbd2a8fed6d54afb823a9b64716469ea0fc6
5
5
  SHA512:
6
- metadata.gz: dd5f18ac803ece9604f9187047c637e4be0f1ad31fb77bce0d73f97dc633ba60644a4d2638deadc2d2d05e6f5152e8050ecc4823caa1dd2c64d0da487618ee85
7
- data.tar.gz: e99ffd6732c726f78f4a5e3f2ce07c571019b43d8028539ea1a5d6ad0d75f52f74d254808e3585f0d0e546b2d453e880f2291b2e088dc8e6fe8e63b496f5990d
6
+ metadata.gz: '090e644895f9f3fabb0b1ca3839592838864c5c6cb0a1c31acd83dbe2ef8c0ee2cbad5a13308e8986928f660d54c342f494858fcd7eb33038dc57d32834b03f8'
7
+ data.tar.gz: 1647b01aed3aa31f8aaac047ffe7880f58fe47530d81445077652c15635efdc99eeaedfdc7e12af87ab914ae3e4e80edda0eeec42404bdb5d50813527ac642a0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- perron (0.6.0)
4
+ perron (0.8.0)
5
5
  csv
6
6
  json
7
7
  psych
data/README.md CHANGED
@@ -17,19 +17,21 @@ A Rails-based static site generator.
17
17
 
18
18
  ### Installation
19
19
 
20
- Start by generating the configuration file:
20
+ Start by adding Perron:
21
+ ```bash
22
+ bundle add perron
23
+ ```
24
+
25
+ Then generate the initializer:
21
26
  ```bash
22
27
  rails generate perron:install
23
28
  ```
24
29
 
30
+
25
31
  This creates an initializer:
26
32
  ```ruby
27
33
  Perron.configure do |config|
28
34
  config.site_name = "AppRefresher"
29
-
30
- # Override the defaults (meta) title suffix
31
- # Default: `— Perron.configuration.site_name`
32
- # config.title_suffix = nil
33
35
  end
34
36
  ```
35
37
 
@@ -89,6 +91,46 @@ bundle add {commonmarker,kramdown,redcarpet}
89
91
  ```
90
92
 
91
93
 
94
+ ## HTML Transformations
95
+
96
+ Perron can post-process the HTML generated from your Markdown content.
97
+
98
+
99
+ ### Usage
100
+
101
+ Apply transformations by passing an array of processor names or classes to the `markdownify` helper via the `process` option.
102
+ ```erb
103
+ <%= markdownify @resource.content, process: %w[target_blank lazy_load_images] %>
104
+ ```
105
+
106
+
107
+ ### Available Processors
108
+
109
+ The following processors are built-in and can be activated by passing their string name:
110
+
111
+ - `target_blank`: Adds `target="_blank"` to all external links;
112
+ - `lazy_load_images`: Adds `loading="lazy"` to all `<img>` tags.
113
+
114
+
115
+ ### Creating Your Own
116
+
117
+ You can create your own processor by defining a class that inherits from `Perron::HtmlProcessor::Base` and implements a `process` method.
118
+ Then, pass the class constant directly in the `process` array.
119
+
120
+ ```ruby
121
+ # app/processors/add_nofollow_processor.rb
122
+ class AddNofollowProcessor < Perron::HtmlProcessor::Base
123
+ def process
124
+ @html.css("a[target=_blank]").each { it["rel"] = "nofollow" }
125
+ end
126
+ end
127
+ ```
128
+
129
+ ```erb
130
+ <%= markdownify @resource.content, process: ["target_blank", AddNofollowProcessor] %>
131
+ ```
132
+
133
+
92
134
  ## Data Files
93
135
 
94
136
  Perron can consume structured data from YML, JSON, or CSV files, making them available within your templates.
@@ -96,9 +138,9 @@ This is useful for populating features, team members, or any other repeated data
96
138
 
97
139
  ### Usage
98
140
 
99
- To use a data file, instantiate `Perron::Data` with the basename of the file and iterate over the result.
141
+ To use a data file, instantiate `Perron::Site.data` with the basename of the file and iterate over the result.
100
142
  ```erb
101
- <% Perron::Data.new("features").each do |feature| %>
143
+ <% Perron::Site.data.features.each do |feature| %>
102
144
  <h4><%= feature.name %></h4>
103
145
  <p><%= feature.description %></p>
104
146
  <% end %>
@@ -107,7 +149,7 @@ To use a data file, instantiate `Perron::Data` with the basename of the file and
107
149
  ### File Location and Formats
108
150
 
109
151
  By default, Perron looks up `app/content/data/` for files with a `.yml`, `.json`, or `.csv` extension.
110
- For a `new("features")` call, it would find `features.yml`, `features.json`, or `features.csv`. You can also provide a full, absolute path to any data file.
152
+ For a `features` call, it would find `features.yml`, `features.json`, or `features.csv`. You can also provide a path to any data file, via `Perron::Data.new("path/to/data.json")`.
111
153
 
112
154
  ### Accessing Data
113
155
 
@@ -122,7 +164,6 @@ feature[:name]
122
164
 
123
165
  The `meta_tags` helper automatically generates SEO and social sharing meta tags for your pages.
124
166
 
125
-
126
167
  ### Usage
127
168
 
128
169
  In your layout (e.g., `app/views/layouts/application.html.erb`), add the helper to the `<head>` section:
@@ -211,6 +252,7 @@ Sites that use Perron.
211
252
  ### Integrated (part of a Rails app)
212
253
  - [Rails Designers (private community for Rails UI engineers](https://railsdesigners.com)
213
254
 
255
+
214
256
  ## Contributing
215
257
 
216
258
  This project uses [Standard](https://github.com/testdouble/standard) for formatting Ruby code. Please run `be standardrb` before submitting pull requests. Run tests with `rails test`.
@@ -4,6 +4,10 @@ require "perron/markdown"
4
4
 
5
5
  module Perron
6
6
  module MarkdownHelper
7
- def markdownify(content = nil, &block) = Perron::Markdown.render(content || capture(&block).strip_heredoc)
7
+ def markdownify(content = nil, options = {}, &block)
8
+ processors = options.fetch(:process, [])
9
+
10
+ Perron::Markdown.render(content || capture(&block).strip_heredoc, processors: processors)
11
+ end
8
12
  end
9
13
  end
@@ -8,11 +8,11 @@ This is useful for populating features, team members, or any other repeated data
8
8
 
9
9
  To use a data file, instantiate `Perron::Data` with the basename of the file and iterate over the result.
10
10
  ```erb
11
- <% Perron::Data.new("features").each do |feature| %>
12
- <h4><%= feature.name %></h4>
11
+ <%% Perron::Data.new("features").each do |feature| %>
12
+ <h4><%%= feature.name %></h4>
13
13
 
14
- <p><%= feature.description %></p>
15
- <% end %>
14
+ <p><%%= feature.description %></p>
15
+ <%% end %>
16
16
  ```
17
17
 
18
18
  ## File Location and Formats
@@ -11,12 +11,12 @@ Perron.configure do |config|
11
11
 
12
12
  # config.default_url_options = {host: "apprefresher.com", protocol: "https", trailing_slash: true}
13
13
 
14
- # Override the defaults (meta) title suffix
15
- # Default: `— Perron.configuration.site_name`
16
- # config.title_suffix = nil
17
-
18
14
  # Set default meta values
19
15
  # Examples:
20
16
  # - `config.metadata.description = "AI-powered tool to keep your knowledge base articles images/screenshots and content up-to-date"`
21
17
  # - `config.metadata.author = "Rails Designer"`
18
+
19
+ # Set meta title suffix
20
+ # config.metadata.title_suffix = nil
21
+ # config.metadata.title_separator = " — "
22
22
  end
@@ -9,10 +9,6 @@ module Perron
9
9
  yield(configuration)
10
10
  end
11
11
 
12
- def self.reset_configuration!
13
- @configuration = Configuration.new
14
- end
15
-
16
12
  class Configuration
17
13
  def initialize
18
14
  @config = ActiveSupport::OrderedOptions.new
@@ -23,7 +19,6 @@ module Perron
23
19
  @config.include_root = false
24
20
 
25
21
  @config.site_name = nil
26
- @config.title_suffix = nil
27
22
 
28
23
  @config.allowed_extensions = [".erb", ".md"]
29
24
  @config.exclude_from_public = %w[assets storage]
@@ -35,10 +30,16 @@ module Perron
35
30
  trailing_slash: ENV.fetch("PERRON_TRAILING_SLASH", "true") == "true"
36
31
  }
37
32
 
33
+ @config.sitemap = ActiveSupport::OrderedOptions.new
34
+ @config.sitemap.enabled = false
35
+ @config.sitemap.priority = 0.5
36
+ @config.sitemap.change_frequency = :monthly
37
+
38
38
  @config.metadata = ActiveSupport::OrderedOptions.new
39
+ @config.metadata.title_separator = " — "
39
40
  end
40
41
 
41
- def input = "app/content"
42
+ def input = Rails.root.join("app", "content")
42
43
 
43
44
  def output
44
45
  mode.integrated? ? "public" : @config.output
data/lib/perron/errors.rb CHANGED
@@ -9,5 +9,9 @@ module Perron
9
9
  class UnsupportedDataFormatError < StandardError; end
10
10
 
11
11
  class DataParseError < StandardError; end
12
+
13
+ class ProcessorNotFoundError < StandardError; end
14
+
15
+ class InvalidProcessorError < StandardError; end
12
16
  end
13
17
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Perron
4
+ class HtmlProcessor
5
+ class Base
6
+ def initialize(html)
7
+ @html = html
8
+ end
9
+
10
+ def process
11
+ raise NotImplementedError
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Perron
4
+ class HtmlProcessor
5
+ class LazyLoadImages < HtmlProcessor::Base
6
+ def process
7
+ @html.css("img").each do |image|
8
+ image["loading"] = "lazy"
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,21 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "perron/html_processor/base"
4
+
3
5
  module Perron
4
6
  class HtmlProcessor
5
- class TargetBlank
6
- def initialize(html)
7
- @html = html
8
- end
9
-
7
+ class TargetBlank < HtmlProcessor::Base
10
8
  def process
11
9
  @html.css("a").each do |link|
12
10
  href = link["href"]
13
11
 
14
- next unless href
15
- next if href.start_with?("/", "#", "mailto:")
12
+ next if href.blank? || href.start_with?("/", "#", "mailto:")
16
13
 
17
14
  link["target"] = "_blank"
18
- link["rel"] = "noopener"
19
15
  end
20
16
  end
21
17
  end
@@ -1,28 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "perron/html_processor/target_blank"
4
+ require "perron/html_processor/lazy_load_images"
4
5
 
5
6
  module Perron
6
7
  class HtmlProcessor
7
- def initialize(html)
8
+ def initialize(html, processors: [])
8
9
  @html = html
10
+ @processors = processors.map { find_by(it) }
9
11
  end
10
12
 
11
13
  def process
12
14
  document = Nokogiri::HTML::DocumentFragment.parse(@html)
13
15
 
14
- PROCESSORS.each do |processor|
15
- processor.new(document).process
16
- end
16
+ @processors.each { it.new(document).process }
17
17
 
18
18
  document.to_html
19
19
  end
20
20
 
21
21
  private
22
22
 
23
- # TODO: should be a configuration option
24
- PROCESSORS = [
25
- Perron::HtmlProcessor::TargetBlank
26
- ]
23
+ BUILT_IN = {
24
+ "target_blank" => Perron::HtmlProcessor::TargetBlank,
25
+ "lazy_load_images" => Perron::HtmlProcessor::LazyLoadImages
26
+ }
27
+
28
+ def find_by(identifier)
29
+ case identifier
30
+ when String, Symbol
31
+ key = identifier.to_s
32
+
33
+ BUILT_IN[key] || find_class_by(key)
34
+ when Class
35
+ identifier
36
+ else
37
+ raise Perron::Errors::InvalidProcessorError, "Processor must be a String, Symbol, or Class, but got #{identifier.class.name}."
38
+ end
39
+ end
40
+
41
+ def find_class_by(name)
42
+ processor = name.camelize.safe_constantize
43
+
44
+ return processor if processor
45
+
46
+ raise Perron::Errors::ProcessorNotFoundError,
47
+ "Could not find processor `#{name}`. It is not a Perron-included processor and the constant `#{name.camelize}` could not be found."
48
+ end
27
49
  end
28
50
  end
@@ -5,9 +5,9 @@ require "perron/html_processor"
5
5
  module Perron
6
6
  class Markdown
7
7
  class << self
8
- def render(text)
8
+ def render(text, processors: [])
9
9
  parser.parse(text)
10
- .then { Perron::HtmlProcessor.new(it).process }
10
+ .then { Perron::HtmlProcessor.new(it, processors: processors).process }
11
11
  .html_safe
12
12
  end
13
13
 
@@ -20,6 +20,7 @@ module Perron
20
20
  private
21
21
 
22
22
  FRONTMATTER_KEY_MAP = {
23
+ "locale" => %w[og:locale],
23
24
  "image" => %w[og:image twitter:image],
24
25
  "author" => %w[og:author]
25
26
  }.freeze
@@ -29,39 +30,47 @@ module Perron
29
30
  frontmatter = @resource&.metadata&.stringify_keys || {}
30
31
  defaults = @config.metadata
31
32
 
32
- title = frontmatter["title"] || defaults["title"] || @config.site_name
33
+ title = frontmatter["title"] || defaults["title"] || @config.site_name || Rails.application.name.underscore.camelize
34
+ type = frontmatter["type"] || defaults["type"]
33
35
  description = frontmatter["description"] || defaults["description"]
36
+ logo = frontmatter["logo"] || defaults["logo"]
34
37
  author = frontmatter["author"] || defaults["author"]
35
38
  image = frontmatter["image"] || defaults["image"]
39
+ locale = frontmatter["locale"] || defaults["locale"]
36
40
  og_image = frontmatter["og:image"] || image
37
41
  twitter_image = frontmatter["twitter:image"] || og_image
38
42
 
39
43
  {
40
44
  title: title_tag(title),
41
45
  description: meta_tag(name: "description", content: description),
46
+ article_published: meta_tag(property: "article:published_time", content: @resource&.published_at),
42
47
 
43
- og_type: meta_tag(property: "og:type", content: frontmatter["og:type"] || "article"),
44
48
  og_title: meta_tag(property: "og:title", content: frontmatter["og:title"] || title),
49
+ og_type: meta_tag(property: "og:type", content: frontmatter["og:type"] || type),
50
+ og_url: meta_tag(property: "og:url", content: canonical_url),
51
+ og_image: meta_tag(property: "og:image", content: og_image),
52
+
45
53
  og_description: meta_tag(property: "og:description", content: frontmatter["og:description"] || description),
46
54
  og_site_name: meta_tag(property: "og:site_name", content: @config.site_name),
47
- og_image: meta_tag(property: "og:image", content: og_image),
55
+ og_logo: meta_tag(property: "og:logo", content: frontmatter["og:logo"] || logo),
48
56
  og_author: meta_tag(property: "og:author", content: frontmatter["og:author"] || author),
57
+ og_locale: meta_tag(property: "og:locale", content: frontmatter["og:locale"] || locale),
49
58
 
50
59
  twitter_card: meta_tag(name: "twitter:card", content: frontmatter["twitter:card"] || "summary_large_image"),
51
60
  twitter_title: meta_tag(name: "twitter:title", content: frontmatter["twitter:title"] || title),
52
61
  twitter_description: meta_tag(name: "twitter:description", content: frontmatter["twitter:description"] || description),
53
- twitter_image: meta_tag(name: "twitter:image", content: twitter_image),
54
- article_published: meta_tag(property: "article:published_time", content: @resource&.published_at),
55
-
56
- og_url: meta_tag(property: "og:url", content: canonical_url)
62
+ twitter_image: meta_tag(name: "twitter:image", content: twitter_image)
57
63
  }
58
64
  end
59
65
  end
60
66
 
61
67
  def title_tag(content)
62
- tag.title(
63
- content.then { (it == @config.site_name) ? it : "#{it} #{@config.title_suffix || "— #{@config.site_name}"}" }
64
- )
68
+ resource_title = content.to_s.strip
69
+ title_suffix = Perron.configuration.metadata.title_suffix&.strip
70
+
71
+ suffix = (title_suffix if title_suffix.present? && resource_title != title_suffix)
72
+
73
+ tag.title([resource_title, suffix].compact.join(Perron.configuration.metadata.title_separator))
65
74
  end
66
75
 
67
76
  def meta_tag(attributes)
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Perron
4
+ module Site
5
+ class Builder
6
+ class Sitemap
7
+ def initialize(output_path)
8
+ @output_path = output_path
9
+ end
10
+
11
+ def generate
12
+ return if !Perron.configuration.sitemap.enabled
13
+
14
+ puts "Generating sitemap.xml…"
15
+
16
+ xml = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |builder|
17
+ builder.urlset(xmlns: "http://www.sitemaps.org/schemas/sitemap/0.9") do
18
+ Perron::Site.collections.each do |collection|
19
+ add_urls_for(collection, with: builder)
20
+ end
21
+ end
22
+ end.to_xml
23
+
24
+ File.write(@output_path.join("sitemap.xml"), xml)
25
+
26
+ puts "Sitemap generated at `#{@output_path.join("sitemap.xml")}`"
27
+ end
28
+
29
+ private
30
+
31
+ def add_urls_for(collection, with:)
32
+ return if collection.configuration.sitemap.exclude == true
33
+
34
+ collection.resources.each do |resource|
35
+ next if resource.metadata.sitemap == false
36
+
37
+ root = resource.slug == "/"
38
+ priority = resource.metadata.sitemap_priority || collection.configuration.sitemap.priority || Perron.configuration.sitemap.priority
39
+ change_frequency = resource.metadata.sitemap_change_frequency || collection.configuration.sitemap.change_frequency || Perron.configuration.sitemap.change_frequency
40
+
41
+ Rails.application.routes.url_helpers.with_options(Perron.configuration.default_url_options) do |url|
42
+ with.url do
43
+ with.loc root ? url.root_url : url.polymorphic_url(resource)
44
+ with.priority priority
45
+ with.changefreq change_frequency
46
+ begin
47
+ with.lastmod resource.metadata.updated_at.iso8601
48
+ rescue
49
+ Time.current.iso8601
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "perron/site/builder/assets"
4
+ require "perron/site/builder/sitemap"
4
5
  require "perron/site/builder/public_files"
5
6
  require "perron/site/builder/paths"
6
7
  require "perron/site/builder/page"
@@ -15,6 +16,7 @@ module Perron
15
16
  def build
16
17
  if Perron.configuration.mode.standalone?
17
18
  puts "🧹 Cleaning previous build…"
19
+
18
20
  FileUtils.rm_rf(Dir.glob("#{@output_path}/*"))
19
21
 
20
22
  Perron::Site::Builder::Assets.new.prepare
@@ -26,6 +28,8 @@ module Perron
26
28
 
27
29
  paths.each { render_page(it) }
28
30
 
31
+ Perron::Site::Builder::Sitemap.new(@output_path).generate
32
+
29
33
  puts "-" * 15
30
34
  puts "✅ Build complete"
31
35
  end
@@ -6,16 +6,21 @@ module Perron
6
6
 
7
7
  def initialize(name)
8
8
  @name = name
9
- @collection_path = File.join(Rails.root, Perron.configuration.input, name)
9
+ @collection_path = File.join(Perron.configuration.input, name)
10
10
 
11
11
  raise Errors::CollectionNotFoundError, "No such collection: #{name}" unless File.exist?(@collection_path) && File.directory?(@collection_path)
12
12
  end
13
13
 
14
+ def configuration(resource_class = "Content::#{name.classify}".safe_constantize)
15
+ resource_class.configuration
16
+ end
17
+
14
18
  def all(resource_class = "Content::#{name.classify}".safe_constantize)
15
19
  @all ||= Dir.glob("#{@collection_path}/**/*.*").map do |file_path|
16
20
  resource_class.new(file_path)
17
21
  end.select(&:published?)
18
22
  end
23
+ alias_method :resources, :all
19
24
 
20
25
  def find(slug, resource_class = Resource)
21
26
  resource = all(resource_class).find { it.slug == slug }
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Perron
4
+ class Data
5
+ class Proxy
6
+ def method_missing(method_name, *arguments, &block)
7
+ raise ArgumentError, "Data `#{method_name}` does not accept arguments" if arguments.any?
8
+
9
+ Perron::Data.new(method_name.to_s)
10
+ end
11
+
12
+ def respond_to_missing?(method_name, include_private = false)
13
+ true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Perron
4
+ class Resource
5
+ module Configuration
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ def configuration
10
+ @configuration ||= Options.new.tap do |config|
11
+ config.feeds = Options.new
12
+
13
+ config.feeds.rss = ActiveSupport::OrderedOptions.new
14
+ config.feeds.atom = ActiveSupport::OrderedOptions.new
15
+ config.feeds.json = ActiveSupport::OrderedOptions.new
16
+
17
+ config.linked_data = ActiveSupport::OrderedOptions.new
18
+
19
+ config.related_posts = ActiveSupport::OrderedOptions.new
20
+ config.related_posts.enabled = false
21
+ config.related_posts.max = 5
22
+
23
+ config.sitemap = ActiveSupport::OrderedOptions.new
24
+ config.sitemap.exclude = false
25
+ end
26
+ end
27
+
28
+ def configure
29
+ yield(configuration)
30
+ end
31
+ end
32
+
33
+ class Options < ActiveSupport::OrderedOptions
34
+ def method_missing(name, *arguments)
35
+ if name.to_s.end_with?("=")
36
+ key = name.to_s.chomp("=").to_sym
37
+ value = arguments.first
38
+
39
+ return self[key].merge!(value) if self[key].is_a?(ActiveSupport::OrderedOptions) && value.is_a?(Hash)
40
+ end
41
+
42
+ super
43
+ end
44
+
45
+ def respond_to_missing?(name, include_private = false) = super
46
+ end
47
+ private_constant :Options
48
+ end
49
+ end
50
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "perron/site/resource/configuration"
3
4
  require "perron/site/resource/core"
4
5
  require "perron/site/resource/class_methods"
5
6
  require "perron/site/resource/publishable"
@@ -10,6 +11,7 @@ module Perron
10
11
  class Resource
11
12
  ID_LENGTH = 8
12
13
 
14
+ include Perron::Resource::Configuration
13
15
  include Perron::Resource::Core
14
16
  include Perron::Resource::ClassMethods
15
17
  include Perron::Resource::Publishable
@@ -55,7 +57,7 @@ module Perron
55
57
 
56
58
  def generate_id
57
59
  Digest::SHA1.hexdigest(
58
- @file_path.delete_prefix(Perron.configuration.input).parameterize
60
+ @file_path.delete_prefix(Perron.configuration.input.to_s).parameterize
59
61
  ).first(ID_LENGTH)
60
62
  end
61
63
  end
data/lib/perron/site.rb CHANGED
@@ -4,6 +4,7 @@ require "perron/site/builder"
4
4
  require "perron/site/collection"
5
5
  require "perron/site/resource"
6
6
  require "perron/site/data"
7
+ require "perron/site/data/proxy"
7
8
 
8
9
  module Perron
9
10
  module Site
@@ -24,11 +25,14 @@ module Perron
24
25
  def collections
25
26
  @collections ||= Dir.children(Perron.configuration.input)
26
27
  .select { File.directory?(File.join(Perron.configuration.input, it)) }
28
+ .reject { it == "data" }
27
29
  .map { Collection.new(it) }
28
30
  end
29
31
 
30
- def collection(name)
31
- Collection.new(name)
32
+ def collection(name) = Collection.new(name)
33
+
34
+ def data(name = nil)
35
+ (name && Perron::Data.new(name)) || Perron::Data::Proxy.new
32
36
  end
33
37
  end
34
38
  end
@@ -1,3 +1,3 @@
1
1
  module Perron
2
- VERSION = "0.6.0"
2
+ VERSION = "0.8.0"
3
3
  end
data/perron.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
 
9
9
  spec.summary = "Rails-based static site generator"
10
10
  spec.description = "Perron is a Rails-based static site generator that follows Rails conventions. It allows you to create content collections with markdown or ERB, configure SEO metadata, and build production-ready static sites while leveraging your existing Rails knowledge with familiar patterns and minimal configuration."
11
- spec.homepage = "https://github.com/Rails-Designer/perron"
11
+ spec.homepage = "https://railsdesigner.com/perron/"
12
12
  spec.license = "MIT"
13
13
 
14
14
  spec.metadata["homepage_uri"] = spec.homepage
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perron
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rails Designer Developers
@@ -98,6 +98,8 @@ files:
98
98
  - lib/perron/engine.rb
99
99
  - lib/perron/errors.rb
100
100
  - lib/perron/html_processor.rb
101
+ - lib/perron/html_processor/base.rb
102
+ - lib/perron/html_processor/lazy_load_images.rb
101
103
  - lib/perron/html_processor/target_blank.rb
102
104
  - lib/perron/markdown.rb
103
105
  - lib/perron/metatags.rb
@@ -109,10 +111,13 @@ files:
109
111
  - lib/perron/site/builder/page.rb
110
112
  - lib/perron/site/builder/paths.rb
111
113
  - lib/perron/site/builder/public_files.rb
114
+ - lib/perron/site/builder/sitemap.rb
112
115
  - lib/perron/site/collection.rb
113
116
  - lib/perron/site/data.rb
117
+ - lib/perron/site/data/proxy.rb
114
118
  - lib/perron/site/resource.rb
115
119
  - lib/perron/site/resource/class_methods.rb
120
+ - lib/perron/site/resource/configuration.rb
116
121
  - lib/perron/site/resource/core.rb
117
122
  - lib/perron/site/resource/publishable.rb
118
123
  - lib/perron/site/resource/separator.rb
@@ -120,11 +125,11 @@ files:
120
125
  - lib/perron/tasks/perron.rake
121
126
  - lib/perron/version.rb
122
127
  - perron.gemspec
123
- homepage: https://github.com/Rails-Designer/perron
128
+ homepage: https://railsdesigner.com/perron/
124
129
  licenses:
125
130
  - MIT
126
131
  metadata:
127
- homepage_uri: https://github.com/Rails-Designer/perron
132
+ homepage_uri: https://railsdesigner.com/perron/
128
133
  source_code_uri: https://github.com/Rails-Designer/perron/
129
134
  rdoc_options: []
130
135
  require_paths:
@@ -140,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
145
  - !ruby/object:Gem::Version
141
146
  version: '0'
142
147
  requirements: []
143
- rubygems_version: 3.6.7
148
+ rubygems_version: 3.6.9
144
149
  specification_version: 4
145
150
  summary: Rails-based static site generator
146
151
  test_files: []