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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +25 -2
  4. data/app/controllers/perron/searches_controller.rb +48 -0
  5. data/app/helpers/perron/feeds_helper.rb +7 -0
  6. data/app/helpers/perron/markdown_helper.rb +3 -3
  7. data/app/helpers/perron/meta_tags_helper.rb +17 -0
  8. data/bin/release +19 -4
  9. data/lib/generators/perron/templates/README.md.tt +31 -7
  10. data/lib/generators/perron/templates/initializer.rb.tt +11 -11
  11. data/lib/generators/rails/content/USAGE +40 -15
  12. data/lib/generators/rails/content/content_generator.rb +53 -15
  13. data/lib/generators/rails/content/templates/controller.rb.tt +1 -5
  14. data/lib/generators/rails/content/templates/model.rb.tt +11 -0
  15. data/lib/generators/rails/content/templates/root.erb.tt +1 -1
  16. data/lib/generators/rails/content/templates/show.html.erb.tt +1 -1
  17. data/lib/perron/collection.rb +10 -1
  18. data/lib/perron/configuration.rb +14 -6
  19. data/lib/perron/content/data.rb +6 -2
  20. data/lib/perron/data_source/class_methods.rb +58 -0
  21. data/lib/perron/data_source/helper_context.rb +20 -0
  22. data/lib/perron/data_source/item.rb +37 -0
  23. data/lib/perron/{data → data_source}/proxy.rb +1 -1
  24. data/lib/perron/data_source.rb +155 -0
  25. data/lib/perron/engine.rb +12 -0
  26. data/lib/perron/html_processor/syntax_highlight.rb +2 -0
  27. data/lib/perron/output_server.rb +7 -2
  28. data/lib/perron/relation.rb +51 -0
  29. data/lib/perron/resource/associations.rb +2 -2
  30. data/lib/perron/resource/class_methods.rb +14 -4
  31. data/lib/perron/resource/configuration.rb +8 -11
  32. data/lib/perron/resource/core.rb +11 -0
  33. data/lib/perron/resource/metadata.rb +1 -1
  34. data/lib/perron/resource/related/stop_words.rb +20 -20
  35. data/lib/perron/resource/related.rb +73 -52
  36. data/lib/perron/resource/scopes.rb +29 -0
  37. data/lib/perron/resource/searchable.rb +19 -0
  38. data/lib/perron/resource/sourceable.rb +2 -2
  39. data/lib/perron/resource/sweeper.rb +45 -0
  40. data/lib/perron/resource/table_of_content.rb +0 -18
  41. data/lib/perron/resource.rb +34 -24
  42. data/lib/perron/site/builder/additional_routes.rb +23 -0
  43. data/lib/perron/site/builder/feeds/author.rb +2 -1
  44. data/lib/perron/site/builder/paths.rb +2 -9
  45. data/lib/perron/site/builder/sitemap.rb +20 -2
  46. data/lib/perron/site/builder.rb +2 -0
  47. data/lib/perron/site.rb +3 -3
  48. data/lib/perron/tasks/build.rake +8 -1
  49. data/lib/perron/version.rb +1 -1
  50. data/lib/perron.rb +1 -1
  51. data/perron.gemspec +1 -0
  52. metadata +29 -7
  53. data/app/helpers/feeds_helper.rb +0 -5
  54. data/app/helpers/meta_tags_helper.rb +0 -15
  55. data/lib/perron/data.rb +0 -145
  56. data/lib/perron/root.rb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 709c1a304332c522dd7e425824f2718b1a84994db9695b842cddab07fa2a6c2d
4
- data.tar.gz: cd636faddbff65dd1cc27652fb9141a936ce7df7e5f4fca1e156fd7b1ff27522
3
+ metadata.gz: 4b7d197a1f26d5fae6c488fe7be6aef6c3818e408d97b5d789f46111db3207be
4
+ data.tar.gz: 0cb809db9260c8e5ded0626f9d790dfb003b937892c1bbce022bcbdac3e5053d
5
5
  SHA512:
6
- metadata.gz: 40df8027fb5974b0670a80b4c61e918da1f3dd7cec833e5fab48cdb606ddb9d9e56480cb69ad3c3033c2fdf1deb09e0c105cb15730cb92581e1d57c3db57ce3f
7
- data.tar.gz: af0d331538e600ad27d23f9377d6cdabcc1f3142ad7d07de517b002334c0414896edb45d6d0d5d6ab50a6af81be12f4b2bf667c15e8bc50e211a824b6701d1b2
6
+ metadata.gz: cc876ae9e8fb6daf1c6496c381efc05eba46fe5bbff9174502480488c34a4dbf5c94d9a360752942a53c395d7df6cb3b9e8342936e8e795b3ab3ccc20f6b3cdd
7
+ data.tar.gz: 99681d91d5d5ea2ffb8488ba4c93c27b2115aebde8132d6f0c8376a6b3e7819c62aef196b424e244af04d8ada7a12080c181cd0f44cf9d52c79125263c3f849b
data/Gemfile CHANGED
@@ -4,7 +4,7 @@ source "https://rubygems.org"
4
4
 
5
5
  gemspec
6
6
 
7
- gem "rails", "~> 7.2.0"
7
+ gem "appraisal"
8
8
 
9
9
  group :development do
10
10
  gem "standard", "~> 1.50.0"
data/Gemfile.lock CHANGED
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- perron (0.16.0)
4
+ perron (0.18.0)
5
5
  csv
6
6
  json
7
+ mata (~> 0.8.0)
7
8
  psych
8
9
  rails (>= 7.2.0)
9
10
 
@@ -81,6 +82,10 @@ GEM
81
82
  minitest (>= 5.1)
82
83
  securerandom (>= 0.3)
83
84
  tzinfo (~> 2.0, >= 2.0.5)
85
+ appraisal (2.5.0)
86
+ bundler
87
+ rake
88
+ thor (>= 0.14.0)
84
89
  ast (2.4.3)
85
90
  base64 (0.3.0)
86
91
  benchmark (0.4.1)
@@ -97,6 +102,14 @@ GEM
97
102
  drb (2.2.3)
98
103
  erb (5.0.1)
99
104
  erubi (1.13.1)
105
+ ffi (1.17.3-aarch64-linux-gnu)
106
+ ffi (1.17.3-aarch64-linux-musl)
107
+ ffi (1.17.3-arm-linux-gnu)
108
+ ffi (1.17.3-arm-linux-musl)
109
+ ffi (1.17.3-arm64-darwin)
110
+ ffi (1.17.3-x86_64-darwin)
111
+ ffi (1.17.3-x86_64-linux-gnu)
112
+ ffi (1.17.3-x86_64-linux-musl)
100
113
  globalid (1.2.1)
101
114
  activesupport (>= 6.1)
102
115
  i18n (1.14.7)
@@ -109,6 +122,10 @@ GEM
109
122
  json (2.12.2)
110
123
  language_server-protocol (3.17.0.5)
111
124
  lint_roller (1.1.0)
125
+ listen (3.10.0)
126
+ logger
127
+ rb-fsevent (~> 0.10, >= 0.10.3)
128
+ rb-inotify (~> 0.9, >= 0.9.10)
112
129
  logger (1.7.0)
113
130
  loofah (2.24.1)
114
131
  crass (~> 1.0.2)
@@ -119,6 +136,9 @@ GEM
119
136
  net-pop
120
137
  net-smtp
121
138
  marcel (1.0.4)
139
+ mata (0.8.0)
140
+ listen (~> 3.0)
141
+ rack (>= 3.0)
122
142
  mini_mime (1.1.5)
123
143
  mini_portile2 (2.8.9)
124
144
  minitest (5.27.0)
@@ -202,6 +222,9 @@ GEM
202
222
  zeitwerk (~> 2.6)
203
223
  rainbow (3.1.1)
204
224
  rake (13.3.0)
225
+ rb-fsevent (0.11.2)
226
+ rb-inotify (0.11.1)
227
+ ffi (~> 1.0)
205
228
  rdoc (6.14.2)
206
229
  erb
207
230
  psych (>= 4.0.0)
@@ -267,10 +290,10 @@ PLATFORMS
267
290
  x86_64-linux-musl
268
291
 
269
292
  DEPENDENCIES
293
+ appraisal
270
294
  debug (~> 1.11.0)
271
295
  minitest (~> 5.25, >= 5.25.5)
272
296
  perron!
273
- rails (~> 7.2.0)
274
297
  rake (~> 13.3.0)
275
298
  rouge (~> 4.6.0)
276
299
  standard (~> 1.50.0)
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Perron
4
+ class SearchesController < ApplicationController
5
+ before_action :force_json, only: %w[show]
6
+
7
+ def show
8
+ resources = search_scope.flat_map(&:all)
9
+ index = resources.map do |resource|
10
+ base_fields(resource).merge(additional_search_fields(resource))
11
+ end
12
+
13
+ render json: index
14
+ end
15
+
16
+ private
17
+
18
+ def force_json
19
+ request.format = :json
20
+ end
21
+
22
+ def search_scope
23
+ Perron
24
+ .configuration
25
+ .search_scope
26
+ .map { "Content::#{it.classify}".safe_constantize }
27
+ .compact_blank
28
+ end
29
+
30
+ def base_fields(resource)
31
+ {
32
+ slug: polymorphic_path(resource),
33
+ href: polymorphic_path(resource),
34
+ title: resource.try(:title) || resource.metadata.title,
35
+ headings: resource.extracted_headings.flatten.join(" "),
36
+ body: resource.sweeped_content
37
+ }
38
+ end
39
+
40
+ def additional_search_fields(resource)
41
+ return {} unless resource.class.search_fields_list.any?
42
+
43
+ resource.class.search_fields_list.to_h do |field|
44
+ [field, resource.public_send(field)]
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Perron
4
+ module FeedsHelper
5
+ def feeds(options = {}) = Perron::Feeds.new.render(options)
6
+ end
7
+ end
@@ -4,10 +4,10 @@ require "perron/markdown"
4
4
 
5
5
  module Perron
6
6
  module MarkdownHelper
7
- def markdownify(content = nil, options = {}, &block)
8
- processors = options.fetch(:process, [])
7
+ def markdownify(content = nil, process: [], &block)
8
+ text = block_given? ? capture(&block).strip_heredoc : content
9
9
 
10
- Perron::Markdown.render(content || capture(&block).strip_heredoc, processors: processors)
10
+ Perron::Markdown.render(text, processors: process)
11
11
  end
12
12
  end
13
13
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Perron
4
+ module MetaTagsHelper
5
+ def meta_tags(options = {}) = Perron::Metatags.new(resource.metadata).render(options)
6
+
7
+ private
8
+
9
+ Resource = Data.define(:path, :collection, :metadata, :published_at)
10
+
11
+ def resource
12
+ return Resource.new(request.path, nil, @metadata, nil) if @metadata.present?
13
+
14
+ @resource || Resource.new(request.path, nil, {}, nil)
15
+ end
16
+ end
17
+ end
data/bin/release CHANGED
@@ -3,17 +3,32 @@
3
3
  VERSION=$1
4
4
 
5
5
  if [ -z "$VERSION" ]; then
6
- echo "Error: The version number is required."
7
- echo "Usage: $0 <version-number>"
6
+ echo "Error: Version number or bump type is required"
7
+ echo " Usage: $0 <major|minor|patch|version-number>"
8
+
8
9
  exit 1
9
10
  fi
10
11
 
12
+ if [[ "$VERSION" =~ ^(major|minor|patch)$ ]]; then
13
+ CURRENT=$(grep -o '"[^"]*"' ./lib/perron/version.rb | tr -d '"')
14
+ IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
15
+
16
+ case $VERSION in
17
+ major) VERSION="$((MAJOR + 1)).0.0" ;;
18
+ minor) VERSION="$MAJOR.$((MINOR + 1)).0" ;;
19
+ patch) VERSION="$MAJOR.$MINOR.$((PATCH + 1))" ;;
20
+ esac
21
+ fi
22
+
11
23
  printf "module Perron\n VERSION = \"$VERSION\"\nend\n" > ./lib/perron/version.rb
24
+
12
25
  bundle
26
+
13
27
  git add Gemfile.lock lib/perron/version.rb
14
28
  git commit -m "Bump version for $VERSION"
15
29
  git push
16
30
  git tag v$VERSION
17
31
  git push --tags
18
- gem build perron.gemspec
19
- gem push "perron-$VERSION.gem"
32
+
33
+ bundle exec rake build
34
+ gem push "pkg/perron-$VERSION.gem"
@@ -1,22 +1,24 @@
1
1
  # Data
2
2
 
3
- Perron can consume structured data from YML, JSON, or CSV files, making them available within your templates. This is useful for populating features, team members, or any other repeated data structure.
3
+ Perron can consume structured data from YML, JSON or CSV files, making them available within views. This is useful for populating features, team members or any other repeated data structure.
4
4
 
5
5
 
6
6
  ## Usage
7
7
 
8
- To use a data file, instantiate `Perron::Site.data` with the basename of the file and iterate over the result.
8
+ Access data sources using the `Content::Data` namespace with the class name matching the file's basename:
9
9
  ```erb
10
- <%% Perron::Site.data.features.each do |feature| %>
10
+ <%% Content::Data::Features.all.each do |feature| %>
11
11
  <h4><%%= feature.name %></h4>
12
12
  <p><%%= feature.description %></p>
13
13
  <%% end %>
14
14
  ```
15
15
 
16
+ Look up a single entry with `Content::Data::Features.find("advanced-search")`, where `"advanced-search"` matches the value of the entry's `id` field.
17
+
16
18
 
17
19
  ## File location and formats
18
20
 
19
- By default, Perron looks up `app/content/data/` for files with a `.yml`, `.json`, or `.csv` extension. 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")`.
21
+ By default, Perron looks up `app/content/data/` for files with a `.yml`, `.json` or `.csv` extension. For a `features` call, it would find `features.yml`, `features.json` or `features.csv`. Provide a path to any data resource in `/app/content/data/`, via `Content::Data.new("path/to/data-resource")`.
20
22
 
21
23
 
22
24
  ## Accessing data
@@ -30,12 +32,12 @@ feature[:name]
30
32
 
31
33
  ## Rendering
32
34
 
33
- 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.
35
+ Render data collections directly using Rails-like partial rendering:
34
36
  ```erb
35
- <%%= render Perron::Site.data.features %>
37
+ <%%= render Content::Data::Features.all %>
36
38
  ```
37
39
 
38
- 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.
40
+ This expects a partial at `app/views/content/features/_feature.html.erb` that will be rendered once for each item in `Content::Data::Features.all`. The individual record is made available as a local variable matching the singular form of the collection name.
39
41
  ```erb
40
42
  <!-- app/views/content/features/_feature.html.erb -->
41
43
  <div class="feature">
@@ -43,3 +45,25 @@ This expects a partial at `app/views/content/features/_feature.html.erb` that wi
43
45
  <p><%%= feature.description %></p>
44
46
  </div>
45
47
  ```
48
+
49
+
50
+ ## Data structure
51
+
52
+ Data resources must contain an array of objects. Each record should include an `id` field if used with [associations](/docs/resources/#associations) or with the `find` method:
53
+ ```yaml
54
+ # app/content/data/authors.yml
55
+ - id: rails-designer
56
+ name: Rails Designer
57
+ bio: Creator of Perron
58
+
59
+ - id: cam
60
+ name: Cam
61
+ bio: Contributing author
62
+ ```
63
+
64
+
65
+ ## Enumerable methods
66
+
67
+ [!label v0.17.0+]
68
+
69
+ All data objects support enumerable methods like `select`, `sort_by`, `first` and `count`. See [Enumerable methods](/docs/rendering/#enumerable-methods) for the full list of available methods.
@@ -1,25 +1,25 @@
1
1
  Perron.configure do |config|
2
- # config.output = "output"
2
+ # For all options check out:
3
+ # https://perron.railsdesigner.com/docs/configuration/
3
4
 
4
- # config.site_name = "Chirp Form"
5
-
6
- # The build mode for Perron. Can be :standalone or :integrated
5
+ # The build mode for Perron. Can be :standalone (SSG, default) or :integrated (alongside your Rails app)
7
6
  # config.mode = :standalone
8
7
 
9
- # In `integrated` mode, the root is skipped by default. Set to `true` to enable
10
- # config.include_root = false
8
+ # Enable Live Reload with DOM Morphing in development
9
+ # config.live_reload = true
11
10
 
12
- # config.default_url_options = {host: "helptail.com", protocol: "https", trailing_slash: true}
11
+ # config.default_url_options = {host: "chirpform.com", protocol: "https", trailing_slash: true}
13
12
 
14
13
  # The options hash is passed directly to the chosen library
15
14
  # config.markdown_options = {}
16
15
 
17
- # Set default meta values
18
- # Examples:
19
- # - `config.metadata.description = "Add forms to any static site. Display responses anywhere."`
20
- # - `config.metadata.author = "Chirp Form Team"`
16
+ # config.site_name = "Chirp Form"
21
17
 
22
18
  # Set meta title suffix
23
19
  # config.metadata.title_suffix = nil
24
20
  # config.metadata.title_separator = " — "
21
+
22
+ # Set default meta values
23
+ # config.metadata.description = "Add forms to any static site. Display responses anywhere."
24
+ # config.metadata.author = "Chirp Form Team"
25
25
  end
@@ -2,40 +2,65 @@ Description:
2
2
  Generates content model scaffold (controller, views, routes) or creates
3
3
  new content files from templates.
4
4
 
5
+ Arguments:
6
+ NAME Name of the content model (singular or plural)
7
+ actions Actions to generate (default: index show)
8
+
9
+ Options:
10
+ --new [TITLE] Create new content file instead of scaffold
11
+ --data [SOURCE...] Create data source files (default extension: .yml)
12
+ --force-plural Use plural form for model name and class
13
+ --[no-]include-root Include root action and route
14
+ (default: true for pages, false otherwise)
15
+
5
16
  Examples:
6
- Generate content scaffold:
17
+ Generate basic content scaffold:
7
18
  rails generate content Post
8
19
  rails generate content Post index
9
20
  rails generate content Post index show
10
21
 
11
- This will create:
22
+ Creates:
12
23
  app/content/posts/
13
24
  app/models/content/post.rb
14
25
  app/controllers/content/posts_controller.rb
15
26
  app/views/content/posts/index.html.erb
16
27
  app/views/content/posts/show.html.erb
17
28
 
18
- And adds: resources :posts, module: :content, only: %w[index show]
29
+ Adds route:
30
+ resources :posts, module: :content, only: %w[index show]
31
+
32
+ Generate pages scaffold with root:
33
+ rails generate content Page
34
+
35
+ Additionally creates:
36
+ app/content/pages/root.erb
37
+ root action in app/controllers/content/pages_controller.rb
38
+ root route in config/routes.rb
39
+
40
+ Control root generation:
41
+ rails generate content Page --no-include-root
42
+ rails generate content Article --include-root
43
+
44
+ Generate scaffold with data sources:
45
+ rails generate content Product --data countries products
46
+ rails generate content Product --data countries.json products.yml
47
+
48
+ Creates data source files in app/content/data/ and adds
49
+ .sources and .template_source class methods to the model.
50
+
51
+ Adds .sources and .template_source class methods to model.
19
52
 
20
53
  Create new content file from template:
21
54
  rails generate content Post --new
22
55
  rails generate content Post --new "My First Post"
23
56
 
24
- This will create a new content file in app/content/posts/ using:
25
- - YYYY-MM-DD-template.*.tt (if exists, with date prefix)
26
- - template.*.tt (if exists, without date prefix)
27
- - Empty file with frontmatter dashes (if no template)
57
+ Creates a new content file in app/content/posts/ using:
58
+ 1. YYYY-MM-DD-template.*.tt (if exists, with date prefix)
59
+ 2. template.*.tt (if exists, without date prefix)
60
+ 3. Empty file with frontmatter dashes (if no template)
28
61
 
29
62
  Template files support ERB:
30
63
  ---
31
64
  title: <%= @title %>
32
65
  published_at: <%= Time.current %>
33
66
  ---
34
-
35
- Arguments:
36
- NAME: Name of the content model (singular or plural)
37
- actions: Actions to generate (default: index show)
38
-
39
- Options:
40
- --new [TITLE]: Create new content file instead of scaffold
41
- --force-plural: Use plural form for model name and class
@@ -8,8 +8,11 @@ module Rails
8
8
  source_root File.expand_path("templates", __dir__)
9
9
 
10
10
  class_option :force_plural, type: :boolean, default: false, desc: "Forces the use of a plural model name and class"
11
+ class_option :include_root, type: :boolean, default: nil, desc: "Include root action and route (defaults to true for pages)"
11
12
  class_option :new, type: :string, default: nil, banner: "TITLE",
12
13
  desc: "Create a new content file from template instead of generating scaffold"
14
+ class_option :data, type: :array, default: [], banner: "source1(.ext) source2(.ext)",
15
+ desc: "Specify data sources with optional extensions (defaults to .yml)"
13
16
 
14
17
  argument :actions, type: :array, default: %w[index show], banner: "actions", desc: "Specify which actions to generate (index/show)"
15
18
 
@@ -60,25 +63,51 @@ module Rails
60
63
  FileUtils.mkdir_p(content_directory)
61
64
  end
62
65
 
63
- def create_pages_root
66
+ def add_content_route
64
67
  return if @content_mode
65
- return unless pages_controller?
66
68
 
67
- template "root.erb.tt", File.join(content_directory, "root.erb")
69
+ route "resources :#{plural_file_name}, module: :content, only: %w[#{actions.join(" ")}]"
68
70
  end
69
71
 
70
- def add_content_route
72
+ def create_data_sources
71
73
  return if @content_mode
74
+ return if options[:data].empty?
72
75
 
73
- route "resources :#{plural_file_name}, module: :content, only: %w[#{actions.join(" ")}]"
76
+ options[:data].each do |source|
77
+ name, extension = source.split(".", 2)
78
+
79
+ create_file File.join("app", "content", "data", "#{name}.#{extension || "yml"}"), ""
80
+ end
81
+ end
82
+
83
+ def add_root_action
84
+ return if @content_mode
85
+ return unless should_include_root?
86
+
87
+ inject_into_class "app/controllers/content/#{plural_file_name}_controller.rb", "Content::#{plural_class_name}Controller" do
88
+ <<~RUBY
89
+ def root
90
+ @resource = Content::#{class_name}.root
91
+
92
+ render :show
93
+ end
94
+ RUBY
95
+ end
96
+ end
97
+
98
+ def create_root_content_file
99
+ return if @content_mode
100
+ return unless should_include_root?
101
+
102
+ template "root.erb.tt", File.join(content_directory, "root.erb")
74
103
  end
75
104
 
76
105
  def add_root_route
77
106
  return if @content_mode
78
- return unless pages_controller?
107
+ return unless should_include_root?
79
108
  return if root_route_exists?
80
109
 
81
- inject_into_file "config/routes.rb", " root to: \"content/pages#root\"\n", before: /^\s*end\s*$/
110
+ route "root to: \"content/#{plural_file_name}#root\""
82
111
  end
83
112
 
84
113
  private
@@ -99,14 +128,6 @@ module Rails
99
128
 
100
129
  def pages_controller? = plural_file_name == "pages"
101
130
 
102
- def root_route_exists?
103
- routes = File.join(destination_root, "config", "routes.rb")
104
-
105
- return false unless File.exist?(routes)
106
-
107
- File.read(routes).match?(/\broot\s+to:/)
108
- end
109
-
110
131
  def template_file
111
132
  @template_file ||= Dir.glob(File.join(content_directory, "{YYYY-MM-DD-,}template.*.tt")).first
112
133
  end
@@ -121,6 +142,23 @@ module Rails
121
142
  end
122
143
  end
123
144
  end
145
+
146
+ def should_include_root?
147
+ options[:include_root].nil? ? pages_controller? : options[:include_root]
148
+ end
149
+
150
+ def root_route_exists?
151
+ routes = File.join(destination_root, "config", "routes.rb")
152
+ return false unless File.exist?(routes)
153
+
154
+ File.read(routes).match?(/\broot\s+to:/)
155
+ end
156
+
157
+ def data_sources
158
+ options[:data].map { it.split(".").first }
159
+ end
160
+
161
+ def data_sources? = !options[:data].empty?
124
162
  end
125
163
  end
126
164
  end
@@ -1,8 +1,4 @@
1
1
  class Content::<%= plural_class_name %>Controller < ApplicationController
2
- <%- if pages_controller? -%>
3
- include Perron::Root
4
- <%- end -%>
5
-
6
2
  <%- if actions.include?("index") -%>
7
3
  def index
8
4
  @resources = Content::<%= class_name %>.all
@@ -11,7 +7,7 @@ class Content::<%= plural_class_name %>Controller < ApplicationController
11
7
  <%- end -%>
12
8
  <%- if actions.include?("show") -%>
13
9
  def show
14
- @resource = Content::<%= class_name %>.find(params[:id])
10
+ @resource = Content::<%= class_name %>.find!(params[:id])
15
11
  end
16
12
  <%- end -%>
17
13
  end
@@ -1,2 +1,13 @@
1
1
  class Content::<%= class_name %> < Perron::Resource
2
+ <% if data_sources? -%>
3
+ sources <%= data_sources.map { ":#{it}" }.join(", ") %>
4
+
5
+ def self.source_template(sources)
6
+ <<~MARKDOWN
7
+ ---
8
+ ---
9
+
10
+ MARKDOWN
11
+ end
12
+ <% end -%>
2
13
  end
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  ---
3
3
 
4
- Find me in `app/content/pages/root.erb`
4
+ Find me in `app/content/<%= plural_file_name %>/root.erb`
@@ -3,5 +3,5 @@
3
3
  <%%= @resource.filename %>
4
4
  </h1>
5
5
 
6
- <%%= markdownify @resource.content %>
6
+ <%%= @resource.content %>
7
7
  </article>
@@ -16,11 +16,20 @@ module Perron
16
16
  end
17
17
 
18
18
  def all(resource_class = "Content::#{name.classify}".safe_constantize)
19
- load_resources(resource_class).select(&:published?)
19
+ Perron::Relation.new(load_resources(resource_class).select(&:published?))
20
20
  end
21
21
  alias_method :resources, :all
22
22
 
23
23
  def find(slug, resource_class = Resource)
24
+ Perron.deprecator.deprecation_warning(
25
+ :find,
26
+ "Collection#find will return nil instead of raising in the next major version. Use #find! to raise an error."
27
+ )
28
+
29
+ find!(slug, resource_class)
30
+ end
31
+
32
+ def find!(slug, resource_class = Resource)
24
33
  resource = load_resources(resource_class).find { it.slug == slug }
25
34
 
26
35
  return resource if resource
@@ -13,18 +13,17 @@ module Perron
13
13
  def initialize
14
14
  @config = ActiveSupport::OrderedOptions.new
15
15
 
16
- @config.site_name = nil
17
- @config.site_description = nil
18
-
19
16
  @config.output = "output"
20
17
 
21
18
  @config.mode = :standalone
22
- @config.include_root = false
23
19
 
24
- @config.allowed_extensions = %w[erb md]
20
+ @config.live_reload = false
21
+ @config.live_reload_watch_paths = %w[app/content app/views app/assets]
22
+ @config.live_reload_skip_paths = %w[app/assets/builds]
25
23
 
26
24
  @config.exclude_from_public = %w[assets storage]
27
25
  @config.excluded_assets = %w[action_cable actioncable actiontext activestorage rails-ujs trix turbo]
26
+ @config.allowed_extensions = %w[erb md]
28
27
 
29
28
  @config.view_unpublished = Rails.env.development?
30
29
 
@@ -36,11 +35,16 @@ module Perron
36
35
 
37
36
  @config.markdown_options = {}
38
37
 
38
+ @config.search_scope = []
39
+
39
40
  @config.sitemap = ActiveSupport::OrderedOptions.new
40
41
  @config.sitemap.enabled = false
41
42
  @config.sitemap.priority = 0.5
42
43
  @config.sitemap.change_frequency = :monthly
43
44
 
45
+ @config.site_name = nil
46
+ @config.site_description = nil
47
+
44
48
  @config.metadata = ActiveSupport::OrderedOptions.new
45
49
  @config.metadata.title_separator = " — "
46
50
  end
@@ -53,7 +57,11 @@ module Perron
53
57
 
54
58
  def mode = @config.mode.to_s.inquiry
55
59
 
56
- def exclude_root? = !@config.include_root
60
+ def additional_routes
61
+ @additional_routes || (mode.integrated? ? [] : %w[root_path])
62
+ end
63
+
64
+ attr_writer :additional_routes
57
65
 
58
66
  def url
59
67
  options = Perron.configuration.default_url_options
@@ -2,9 +2,13 @@
2
2
 
3
3
  module Content
4
4
  module Data
5
+ def self.new(identifier)
6
+ Perron::DataSource.new(identifier)
7
+ end
8
+
5
9
  def self.const_missing(name)
6
- klass = Class.new(Perron::Data) do
7
- def self.const_missing(nested_name) = const_set(nested_name, Class.new(Perron::Data))
10
+ klass = Class.new(Perron::DataSource) do
11
+ def self.const_missing(nested_name) = const_set(nested_name, Class.new(Perron::DataSource))
8
12
  end
9
13
 
10
14
  const_set(name, klass)