perron 0.11.0 → 0.12.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: 1757232a5830a600f00f1517b0aa26c0f95cc55005c32cdb7865282b522cefec
4
- data.tar.gz: ba9a09ab06d80c8cc266c210d51770c5577d079dbfab6226f058c1f39a974505
3
+ metadata.gz: 70a9d142afefaac54496efbd93312cae38e14525b7f6310be576f74e7f9dfb4d
4
+ data.tar.gz: fd010ef141b72700120c926a104b8f33f72971d94e58ae01c8c793424d4c5319
5
5
  SHA512:
6
- metadata.gz: 82e81dbe7b346d47d539ab50b098c1b2542241d8864e3ed0cddf3e565b39785ae6710718a3db158cacaa27e67ea436960b88b012851ec5e85d7c91d3d0baa718
7
- data.tar.gz: bdc619b82756afede00259a7f89970bac482a2e7204f2b8a66ad80b798467604b934462fe69d93e9d70e359abc298b0f72fcbf6842dde0258b7aaabe14a5042c
6
+ metadata.gz: 9ab5c1d522c8645f3470451e74c7a5a3475f1a683d6dc953e88fd8fb123e4477558ae99839f2db4742741663c2957eec9d94696d05d7936aa1df14880a5d59a4
7
+ data.tar.gz: fb40411ad56556967b82396d1bb540766550e73131b9358f870f441c75bd1538c9ef071b3fcb4a6c371f389ee7a1a031c101c4e24cb76b9875b4841756a68dc7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- perron (0.11.0)
4
+ perron (0.12.0)
5
5
  csv
6
6
  json
7
7
  psych
data/README.md CHANGED
@@ -38,7 +38,7 @@ end
38
38
 
39
39
  ## Mode
40
40
 
41
- Perron can operate in two modes, configured via `config.mode`. This allows you to build either a full static site or integrate pages into a dynamic Rails application.
41
+ Perron can operate in two modes, configured via `config.mode`. This allows a build to be either a full static site or be integrated pages in a dynamic Rails application.
42
42
 
43
43
  | **Mode** | `:standalone` (default) | `:integrated` |
44
44
  | :--- | :--- | :--- |
@@ -54,7 +54,7 @@ Perron is, just like Rails, designed with convention over configuration in mind.
54
54
  The controllers are located in `app/controllers/content/`. To make them available, create a route: `resources :posts, module: :content, only: %w[index show]`.
55
55
 
56
56
 
57
- ### Create content
57
+ ### Create a new collection
58
58
 
59
59
  ```bash
60
60
  bin/rails generate content Post
@@ -66,12 +66,39 @@ This will create the following files:
66
66
  * `app/controllers/content/posts_controller.rb`
67
67
  * `app/views/content/posts/index.html.erb`
68
68
  * `app/views/content/posts/show.html.erb`
69
- * Adds route: `resources :posts, module: :content, only: %w[index show]`
69
+
70
+ And adds a route: `resources :posts, module: :content, only: %w[index show]`
71
+
72
+
73
+ ### Routes
74
+
75
+ Perron uses standard Rails routing, allowing the use of familiar route helpers. For a typical “clean slug”, the filename without extensions serves as the `id` parameter (slug).
76
+ ```ruby
77
+ <%# For app/content/pages/about.md %>
78
+ <%= link_to "About Us", page_path("about") # => <a href="/about/">About Us</a> %>
79
+ ```
80
+
81
+ To create files with specific extensions directly (e.g., `pricing.html`), the route must first be configured to treat the entire filename as the ID. In `config/routes.rb`, modify the generated `resources` line by adding a `constraints` option:
82
+
83
+ ```ruby
84
+ # Change from…
85
+ resources :pages, path: "/", module: :content, only: %w[show]
86
+
87
+ # …to…
88
+ resources :pages, path: "/", module: :content, only: %w[show], constraints: { id: /[^\/]+/ }
89
+ ```
90
+
91
+ With this change, a content file named `app/content/pages/pricing.html.erb` can be linked using the full filename. The builder will then create `pricing.html` in the output directory.
92
+ ```ruby
93
+ <%= link_to "View Pricing", page_path("pricing", format: :html) # => <a href="/pricing.html">View Pricing</a> %>
94
+ ```
70
95
 
71
96
 
72
97
  ### Setting a root page
73
98
 
74
- To set a root page, include `Perron::Root` in your `Content::PagesController` and add a `app/content/pages/root.{md,erb,*}` file. This is automatically added for you when you create a `Page` collection.
99
+ To set a root page, include `Perron::Root` in your `Content::PagesController` and add a `app/content/pages/root.{md,erb,*}` file. Then add `root to: "content/pages#root"` add the bottom of your `config/routes.erb`.
100
+
101
+ This is automatically added for you when you create a `Page` collection.
75
102
 
76
103
 
77
104
  ## Markdown support
@@ -244,9 +271,9 @@ You can render data collections directly using Rails-like partial rendering. Whe
244
271
  <%= render Perron::Site.data.features %>
245
272
  ```
246
273
 
247
- This expects a partial at `app/views/features/_feature.html.erb` that will be rendered once for each feature in your data file. The individual record is made available as a local variable matching the singular form of the collection name.
274
+ This expects a partial at `app/views/content/features/_feature.html.erb` that will be rendered once for each feature in your data file. The individual record is made available as a local variable matching the singular form of the collection name.
248
275
  ```erb
249
- <!-- app/views/features/_feature.html.erb -->
276
+ <!-- app/views/content/features/_feature.html.erb -->
250
277
  <div class="feature">
251
278
  <h4><%= feature.name %></h4>
252
279
  <p><%= feature.description %></p>
@@ -289,9 +316,14 @@ Feeds are configured within the `Resource` class corresponding to a collection:
289
316
  class Content::Post < Perron::Resource
290
317
  configure do |config|
291
318
  config.feeds.rss.enabled = true
319
+ # config.feeds.rss.title = "My RSS feed" # defaults to configured site_name
320
+ # config.feeds.rss.description = "My RSS feed description" # defaults to configured site_description
292
321
  # config.feeds.rss.path = "path-to-feed.xml"
293
322
  # config.feeds.rss.max_items = 25
323
+ #
294
324
  config.feeds.json.enabled = true
325
+ # config.feeds.json.title = "My JSON feed" # defaults to configured site_name
326
+ # config.feeds.json.description = "My JSON feed description" # defaults to configured site_description
295
327
  # config.feeds.json.max_items = 15
296
328
  # config.feeds.json.path = "path-to-feed.json"
297
329
  end
@@ -362,10 +394,12 @@ Your content here…
362
394
  Set collection defaults in the resource model:
363
395
  ```ruby
364
396
  class Content::Post < Perron::Resource
365
- #
397
+ Perron.configure do |config|
398
+ # …
366
399
 
367
- config.metadata.description = "Put your routine tasks on autopilot"
368
- config.metadata.author = "Helptail team"
400
+ config.metadata.description = "Put your routine tasks on autopilot"
401
+ config.metadata.author = "Helptail team"
402
+ end
369
403
  end
370
404
  ```
371
405
 
@@ -5,6 +5,8 @@ require "rails/generators/base"
5
5
  class ContentGenerator < Rails::Generators::NamedBase
6
6
  source_root File.expand_path("templates", __dir__)
7
7
 
8
+ class_option :force_plural, type: :boolean, default: false, desc: "Forces the use of a plural model name and class"
9
+
8
10
  argument :actions, type: :array, default: %w[index show], desc: "Specify which actions to generate (index/show)"
9
11
 
10
12
  def create_model
@@ -35,8 +37,23 @@ class ContentGenerator < Rails::Generators::NamedBase
35
37
  route "resources :#{plural_file_name}, module: :content, only: %w[#{actions.join(" ")}]"
36
38
  end
37
39
 
40
+ def add_root_route
41
+ return unless pages_controller?
42
+ return if root_route_exists?
43
+
44
+ inject_into_file "config/routes.rb", " root to: \"content/pages#show\"\n", before: /^\s*end\s*$/
45
+ end
46
+
38
47
  private
39
48
 
49
+ def file_name
50
+ options[:force_plural] ? super.pluralize : super.singularize
51
+ end
52
+
53
+ def class_name
54
+ options[:force_plural] ? super.pluralize : super.singularize
55
+ end
56
+
40
57
  def view_directory = Rails.root.join("app", "views", "content", plural_file_name)
41
58
 
42
59
  def content_directory = Rails.root.join("app", "content", plural_file_name)
@@ -44,4 +61,12 @@ class ContentGenerator < Rails::Generators::NamedBase
44
61
  def plural_class_name = plural_name.camelize
45
62
 
46
63
  def pages_controller? = plural_file_name == "pages"
64
+
65
+ def root_route_exists?
66
+ routes = Rails.root.join("config", "routes.rb")
67
+
68
+ return false unless File.exist?(routes)
69
+
70
+ File.read(routes).match?(/\broot\s+to:/)
71
+ end
47
72
  end
@@ -25,5 +25,9 @@ module Perron
25
25
  # gem "redcarpet"
26
26
  RUBY
27
27
  end
28
+
29
+ def gitignore_output_folder
30
+ append_to_file ".gitignore", "/#{Perron.configuration.output}/\n"
31
+ end
28
32
  end
29
33
  end
@@ -17,7 +17,7 @@ To use a data file, you can access it through the `Perron::Site.data` object fol
17
17
 
18
18
  This is a convenient shorthand for `Perron::Data.new("features")`, which can also be used directly:
19
19
  ```ruby
20
- <%% Perron::Data.new("features").each do |feature| %>
20
+ <%% Perron::Data.new("features").each do |feature| %>
21
21
  <h4><%%= feature.name %></h4>
22
22
 
23
23
  <p><%%= feature.description %></p>
@@ -38,3 +38,20 @@ The wrapper object provides flexible, read-only access to each record's attribut
38
38
  feature.name
39
39
  feature[:name]
40
40
  ```
41
+
42
+
43
+ ## Rendering
44
+
45
+ You can render data collections directly using Rails-like partial rendering. When you call `render` on a data collection, Perron will automatically render a partial for each item.
46
+ ```erb
47
+ <%= render Perron::Site.data.features %>
48
+ ```
49
+
50
+ This expects a partial at `app/views/content/features/_feature.html.erb` that will be rendered once for each feature in your data file. The individual record is made available as a local variable matching the singular form of the collection name.
51
+ ```erb
52
+ <!-- app/views/content/features/_feature.html.erb -->
53
+ <div class="feature">
54
+ <h4><%= feature.name %></h4>
55
+ <p><%= feature.description %></p>
56
+ </div>
57
+ ```
@@ -12,7 +12,7 @@ module Perron
12
12
  end
13
13
 
14
14
  def configuration(resource_class = "Content::#{name.classify}".safe_constantize)
15
- resource_class.configuration
15
+ resource_class&.configuration
16
16
  end
17
17
 
18
18
  def all(resource_class = "Content::#{name.classify}".safe_constantize)
data/lib/perron/data.rb CHANGED
@@ -94,6 +94,8 @@ module Perron
94
94
  end
95
95
 
96
96
  def get_binding = binding
97
+
98
+ def default_url_options = Perron.configuration.default_url_options || {}
97
99
  end
98
100
  private_constant :HelperContext
99
101
 
data/lib/perron/feeds.rb CHANGED
@@ -13,6 +13,7 @@ module Perron
13
13
 
14
14
  next if options[:only]&.map(&:to_s)&.exclude?(collection_name)
15
15
  next if options[:except]&.map(&:to_s)&.include?(collection_name)
16
+ next if collection.configuration.blank?
16
17
 
17
18
  collection.configuration.feeds.each do |type, feed|
18
19
  next unless feed.enabled && feed.path && MIME_TYPES.key?(type)
@@ -1,11 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Perron
2
- module SuffixStripping
3
- refine String do
4
- def delete_suffixes(suffixes)
5
- suffixes
6
- .sort_by(&:length)
7
- .reverse_each
8
- .reduce(self, :delete_suffix)
4
+ module Refinements
5
+ module DeleteSuffixes
6
+ refine String do
7
+ def delete_suffixes(suffixes)
8
+ suffixes
9
+ .sort_by(&:length)
10
+ .reverse_each
11
+ .reduce(self, :delete_suffix)
12
+ end
9
13
  end
10
14
  end
11
15
  end
@@ -70,6 +70,7 @@ module Perron
70
70
  @tokenized_content ||= {}
71
71
 
72
72
  return @tokenized_content[target_resource] if @tokenized_content.key?(target_resource)
73
+ return [] if target_resource.content.blank?
73
74
 
74
75
  content = target_resource.content.gsub(/<[^>]*>/, " ")
75
76
  tokens = content.downcase.scan(/\w+/).reject { StopWords.all.include?(it) || it.length < 3 }
@@ -5,7 +5,7 @@ require "perron/refinements/delete_suffixes"
5
5
  module Perron
6
6
  class Resource
7
7
  class Slug
8
- using Perron::SuffixStripping
8
+ using Perron::Refinements::DeleteSuffixes
9
9
 
10
10
  def initialize(resource, frontmatter)
11
11
  @resource = resource
@@ -34,14 +34,6 @@ module Perron
34
34
  alias_method :path, :slug
35
35
  alias_method :to_param, :slug
36
36
 
37
- def content
38
- page_content = Perron::Resource::Separator.new(raw_content).content
39
-
40
- return page_content unless erb_processing?
41
-
42
- Perron::Resource::Renderer.erb(page_content, {resource: self})
43
- end
44
-
45
37
  def metadata
46
38
  Perron::Resource::Metadata.new(
47
39
  resource: self,
@@ -53,6 +45,14 @@ module Perron
53
45
  def raw_content = File.read(@file_path)
54
46
  alias_method :raw, :raw_content
55
47
 
48
+ def content
49
+ page_content = Perron::Resource::Separator.new(raw_content).content
50
+
51
+ return page_content unless erb_processing?
52
+
53
+ Perron::Resource::Renderer.erb(page_content, {resource: self})
54
+ end
55
+
56
56
  def to_partial_path
57
57
  @to_partial_path ||= begin
58
58
  element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self.class.model_name))
@@ -62,10 +62,6 @@ module Perron
62
62
  end
63
63
  end
64
64
 
65
- def root?
66
- collection.name.inquiry.pages? && File.basename(filename) == "root"
67
- end
68
-
69
65
  def collection = Collection.new(self.class.model_name.collection)
70
66
 
71
67
  def related_resources(limit: 5) = Perron::Site::Resource::Related.new(self).find(limit:)
@@ -77,14 +73,18 @@ module Perron
77
73
  @frontmatter ||= Perron::Resource::Separator.new(raw_content).frontmatter
78
74
  end
79
75
 
80
- def erb_processing?
81
- @file_path.ends_with?(".erb") || metadata.erb == true
82
- end
83
-
84
76
  def generate_id
85
77
  Digest::SHA1.hexdigest(
86
78
  @file_path.delete_prefix(Perron.configuration.input.to_s).parameterize
87
79
  ).first(ID_LENGTH)
88
80
  end
81
+
82
+ def erb_processing?
83
+ @file_path.ends_with?(".erb") || metadata.erb == true
84
+ end
85
+
86
+ def root?
87
+ collection.name.inquiry.pages? && File.basename(filename) == "root"
88
+ end
89
89
  end
90
90
  end
@@ -29,7 +29,8 @@ module Perron
29
29
  end
30
30
 
31
31
  FileUtils.mkdir_p(destination)
32
- FileUtils.cp_r(Dir.glob("#{source}/*"), destination)
32
+ FileUtils.move(Dir.glob("#{source}/*"), destination, force: true)
33
+ FileUtils.remove_dir(source)
33
34
 
34
35
  puts " Copied assets to `#{destination.relative_path_from(Rails.root)}`"
35
36
 
@@ -18,14 +18,14 @@ module Perron
18
18
  hash = Rails.application.routes.url_helpers.with_options(@configuration.default_url_options) do |url|
19
19
  {
20
20
  version: "https://jsonfeed.org/version/1.1",
21
- title: @configuration.site_name,
22
21
  home_page_url: @configuration.url,
23
- description: @configuration.site_description,
22
+ title: feed_configuration.title.presence || @configuration.site_name,
23
+ description: feed_configuration.description.presence || @configuration.site_description,
24
24
  items: resources.map do |resource|
25
25
  {
26
26
  id: resource.id,
27
27
  url: url.polymorphic_url(resource),
28
- date_published: (resource.metadata.published_at || resource.metadata.updated_at)&.iso8601,
28
+ date_published: resource.published_at&.iso8601,
29
29
  title: resource.metadata.title,
30
30
  content_html: Perron::Markdown.render(resource.content)
31
31
  }
@@ -43,8 +43,10 @@ module Perron
43
43
  .reject { it.metadata.feed == false }
44
44
  .sort_by { it.metadata.published_at || it.metadata.updated_at || Time.current }
45
45
  .reverse
46
- .take(@collection.configuration.feeds.json.max_items)
46
+ .take(feed_configuration.max_items)
47
47
  end
48
+
49
+ def feed_configuration = @collection.configuration.feeds.json
48
50
  end
49
51
  end
50
52
  end
@@ -18,8 +18,8 @@ module Perron
18
18
  Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
19
19
  xml.rss(:version => "2.0", "xmlns:atom" => "http://www.w3.org/2005/Atom") do
20
20
  xml.channel do
21
- xml.title @configuration.site_name
22
- xml.description @configuration.site_description
21
+ xml.title feed_configuration.title.presence || @configuration.site_name
22
+ xml.description feed_configuration.description.presence || @configuration.site_description
23
23
  xml.link @configuration.url
24
24
  xml.generator "Perron (#{Perron::VERSION})"
25
25
 
@@ -28,7 +28,7 @@ module Perron
28
28
  xml.item do
29
29
  xml.guid resource.id
30
30
  xml.link url.polymorphic_url(resource), isPermaLink: true
31
- xml.pubDate((resource.metadata.published_at || resource.metadata.updated_at)&.rfc822)
31
+ xml.pubDate(resource.published_at&.rfc822)
32
32
  xml.title resource.metadata.title
33
33
  xml.description { xml.cdata(Perron::Markdown.render(resource.content)) }
34
34
  end
@@ -46,8 +46,10 @@ module Perron
46
46
  .reject { it.metadata.feed == false }
47
47
  .sort_by { it.metadata.published_at || it.metadata.updated_at || Time.current }
48
48
  .reverse
49
- .take(@collection.configuration.feeds.rss.max_items)
49
+ .take(feed_configuration.max_items)
50
50
  end
51
+
52
+ def feed_configuration = @collection.configuration.feeds.rss
51
53
  end
52
54
  end
53
55
  end
@@ -13,6 +13,8 @@ module Perron
13
13
 
14
14
  def generate
15
15
  Perron::Site.collections.each do |collection|
16
+ next if collection.configuration.blank?
17
+
16
18
  config = collection.configuration.feeds
17
19
 
18
20
  if config.rss.enabled
@@ -29,10 +29,12 @@ module Perron
29
29
  private
30
30
 
31
31
  def save_html(html)
32
- directory_path = @output_path.join(@path.delete_prefix("/"))
33
- file_path = directory_path.join("index.html")
32
+ prefixless_path = @path.delete_prefix("/")
34
33
 
35
- FileUtils.mkdir_p(directory_path)
34
+ file_path = @output_path.join(prefixless_path)
35
+ file_path = file_path.join("index.html") if File.extname(prefixless_path).empty?
36
+
37
+ FileUtils.mkdir_p(file_path.dirname)
36
38
  File.write(file_path, html)
37
39
 
38
40
  print "\e[32m.\e[0m"
@@ -25,6 +25,7 @@ module Perron
25
25
  private
26
26
 
27
27
  def add_urls_for(collection, with:)
28
+ return if collection.configuration.blank?
28
29
  return if collection.configuration.sitemap.exclude == true
29
30
 
30
31
  collection.resources.each do |resource|
@@ -39,7 +39,7 @@ module Perron
39
39
  def paths
40
40
  Set.new.tap do |paths|
41
41
  Perron::Site.collections.each { Perron::Site::Builder::Paths.new(it, paths).get }
42
- end.to_a
42
+ end
43
43
  end
44
44
 
45
45
  def render_page(path) = Perron::Site::Builder::Page.new(path).render
@@ -1,3 +1,3 @@
1
1
  module Perron
2
- VERSION = "0.11.0"
2
+ VERSION = "0.12.0"
3
3
  end
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.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rails Designer Developers