perron 0.5.0 → 0.6.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: aa3f2904ac098e13aff67d7153df4867f4301a9c273d06963d3ff0330f97fe86
4
- data.tar.gz: e53143e80f15e7fd1cf00b789959e2c0399f1f547459a39ecf927f6859bfd8f3
3
+ metadata.gz: f91a86bc7ff57f28eb58ef9aac6b1d3e94c008a3570df359aca5f5e6b416c7e7
4
+ data.tar.gz: 53cd0a23ce3fbcd1fe90aa07a26577f34e66be5a8fd018c596498e34c67c5718
5
5
  SHA512:
6
- metadata.gz: 9e7a6e6594f349a7f6f575d5e7bdc1a54234446846695b848c0ff1e982a2de22898c85749b39af4196f64ba44d991b4d25878f4f8451d5ef4fd8fb9fa093c1e6
7
- data.tar.gz: 7055bbf79e0635ce0e2f38c3397f3e20bb76c70dd228ed5482be2fdf9bfe3d38d986e24a1a632420795adbad36cf5019cde704187fd7d9408cd990122fd209fe
6
+ metadata.gz: dd5f18ac803ece9604f9187047c637e4be0f1ad31fb77bce0d73f97dc633ba60644a4d2638deadc2d2d05e6f5152e8050ecc4823caa1dd2c64d0da487618ee85
7
+ data.tar.gz: e99ffd6732c726f78f4a5e3f2ce07c571019b43d8028539ea1a5d6ad0d75f52f74d254808e3585f0d0e546b2d453e880f2291b2e088dc8e6fe8e63b496f5990d
data/Gemfile.lock CHANGED
@@ -1,7 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- perron (0.5.0)
4
+ perron (0.6.0)
5
+ csv
6
+ json
7
+ psych
5
8
  rails (>= 7.2.0)
6
9
 
7
10
  GEM
@@ -86,6 +89,7 @@ GEM
86
89
  concurrent-ruby (1.3.5)
87
90
  connection_pool (2.5.3)
88
91
  crass (1.0.6)
92
+ csv (3.3.5)
89
93
  date (3.4.1)
90
94
  debug (1.11.0)
91
95
  irb (~> 1.10)
data/README.md CHANGED
@@ -89,6 +89,35 @@ bundle add {commonmarker,kramdown,redcarpet}
89
89
  ```
90
90
 
91
91
 
92
+ ## Data Files
93
+
94
+ Perron can consume structured data from YML, JSON, or CSV files, making them available within your templates.
95
+ This is useful for populating features, team members, or any other repeated data structure.
96
+
97
+ ### Usage
98
+
99
+ To use a data file, instantiate `Perron::Data` with the basename of the file and iterate over the result.
100
+ ```erb
101
+ <% Perron::Data.new("features").each do |feature| %>
102
+ <h4><%= feature.name %></h4>
103
+ <p><%= feature.description %></p>
104
+ <% end %>
105
+ ```
106
+
107
+ ### File Location and Formats
108
+
109
+ 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.
111
+
112
+ ### Accessing Data
113
+
114
+ The wrapper object provides flexible, read-only access to each record's attributes. Both dot notation and hash-like key access are supported.
115
+ ```ruby
116
+ feature.name
117
+ feature[:name]
118
+ ```
119
+
120
+
92
121
  ## Metatags
93
122
 
94
123
  The `meta_tags` helper automatically generates SEO and social sharing meta tags for your pages.
@@ -115,7 +144,7 @@ Or exclude certain tags:
115
144
  <%= meta_tags except: %w[twitter_card twitter_image] %>
116
145
  ```
117
146
 
118
- ### Metadata Priority
147
+ ### Priority
119
148
 
120
149
  Values are determined with the following precedence, from highest to lowest:
121
150
 
@@ -172,6 +201,16 @@ RAILS_ENV=production rails perron:build
172
201
  This will create your static site in the configured output directory (`output` by default).
173
202
 
174
203
 
204
+ ## Sites using Perron
205
+
206
+ Sites that use Perron.
207
+
208
+ ### Standalone (as a SSG)
209
+ - [AppRefresher](https://apprefresher.com)
210
+
211
+ ### Integrated (part of a Rails app)
212
+ - [Rails Designers (private community for Rails UI engineers](https://railsdesigners.com)
213
+
175
214
  ## Contributing
176
215
 
177
216
  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`.
@@ -5,5 +5,12 @@ module Perron
5
5
  def copy_initializer
6
6
  template "initializer.rb.tt", "config/initializers/perron.rb"
7
7
  end
8
+
9
+ def create_data_directory
10
+ data_directory = Rails.root.join("app", "views", "content", "data")
11
+ empty_directory data_directory
12
+
13
+ template "README.md.tt", File.join(data_directory, "README.md")
14
+ end
8
15
  end
9
16
  end
@@ -0,0 +1,30 @@
1
+ # Data
2
+
3
+ Perron can consume structured data from YML, JSON, or CSV files, making them available within your templates.
4
+ This is useful for populating features, team members, or any other repeated data structure.
5
+
6
+
7
+ ## Usage
8
+
9
+ To use a data file, instantiate `Perron::Data` with the basename of the file and iterate over the result.
10
+ ```erb
11
+ <% Perron::Data.new("features").each do |feature| %>
12
+ <h4><%= feature.name %></h4>
13
+
14
+ <p><%= feature.description %></p>
15
+ <% end %>
16
+ ```
17
+
18
+ ## File Location and Formats
19
+
20
+ By default, Perron looks up `app/content/data/` for files with a `.yml`, `.json`, or `.csv` extension.
21
+ 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.
22
+
23
+
24
+ ## Accessing Data
25
+
26
+ The wrapper object provides flexible, read-only access to each record's attributes. Both dot notation and hash-like key access are supported.
27
+ ```ruby
28
+ feature.name
29
+ feature[:name]
30
+ ```
data/lib/perron/errors.rb CHANGED
@@ -5,5 +5,9 @@ module Perron
5
5
  class FileNotFoundError < StandardError; end
6
6
 
7
7
  class ResourceNotFoundError < StandardError; end
8
+
9
+ class UnsupportedDataFormatError < StandardError; end
10
+
11
+ class DataParseError < StandardError; end
8
12
  end
9
13
  end
@@ -30,7 +30,7 @@ module Perron
30
30
  private
31
31
 
32
32
  def paths
33
- @paths ||= Dir.glob("#{@public_dir}/*", File::FNM_DOTMATCH).reject do |path|
33
+ @paths ||= Dir.glob(File.join(@public_dir, "*")).reject do |path|
34
34
  Set.new(Perron.configuration.exclude_from_public + %w[. ..]).include?(File.basename(path))
35
35
  end
36
36
  end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "csv"
4
+
5
+ module Perron
6
+ class Data < SimpleDelegator
7
+ def initialize(identifier)
8
+ @file_path = path_for(identifier)
9
+ @records = records
10
+
11
+ super(records)
12
+ end
13
+
14
+ private
15
+
16
+ PARSER_METHODS = {
17
+ ".yml" => :parse_yaml, ".yaml" => :parse_yaml,
18
+ ".json" => :parse_json, ".csv" => :parse_csv
19
+ }.freeze
20
+ SUPPORTED_EXTENSIONS = PARSER_METHODS.keys
21
+
22
+ def path_for(identifier)
23
+ path = Pathname.new(identifier)
24
+
25
+ return path.to_s if path.file? && path.absolute?
26
+
27
+ path = SUPPORTED_EXTENSIONS.lazy.map { Rails.root.join("app", "content", "data").join("#{identifier}#{it}") }.find(&:exist?)
28
+ path&.to_s or raise Errors::FileNotFoundError, "No data file found for '#{identifier}'"
29
+ end
30
+
31
+ def records
32
+ content = File.read(@file_path)
33
+ extension = File.extname(@file_path)
34
+ parser = PARSER_METHODS.fetch(extension) do
35
+ raise Errors::UnsupportedDataFormatError, "Unsupported data format: #{extension}"
36
+ end
37
+
38
+ data = send(parser, content)
39
+
40
+ unless data.is_a?(Array)
41
+ raise Errors::DataParseError, "Data in '#{@file_path}' must be an array of objects."
42
+ end
43
+
44
+ data.map { Item.new(it) }
45
+ rescue Psych::SyntaxError, JSON::ParserError, CSV::MalformedCSVError => error
46
+ raise Errors::DataParseError, "Failed to parse '#{@file_path}': #{error.message}"
47
+ end
48
+
49
+ def parse_yaml(content)
50
+ YAML.safe_load(content, permitted_classes: [Symbol], aliases: true)
51
+ end
52
+
53
+ def parse_json(content)
54
+ JSON.parse(content, symbolize_names: true)
55
+ end
56
+
57
+ def parse_csv(content)
58
+ CSV.new(content, headers: true, header_converters: :symbol).to_a.map(&:to_h)
59
+ end
60
+
61
+ class Item
62
+ def initialize(attributes)
63
+ @attributes = attributes.transform_keys(&:to_sym)
64
+ end
65
+
66
+ def [](key) = @attributes[key.to_sym]
67
+
68
+ def method_missing(method_name, *arguments, &block)
69
+ return super if !@attributes.key?(method_name) || arguments.any? || block
70
+
71
+ @attributes[method_name]
72
+ end
73
+
74
+ def respond_to_missing?(method_name, include_private = false)
75
+ @attributes.key?(method_name) || super
76
+ end
77
+ end
78
+ private_constant :Item
79
+ end
80
+ end
81
+
82
+ # require "csv"
83
+
84
+ # module Perron
85
+ # class Data
86
+ # include Enumerable
87
+
88
+ # def initialize(resource)
89
+ # @file_path = path_for(resource)
90
+ # @data = data
91
+ # end
92
+
93
+ # def each(&block)
94
+ # @data.each(&block)
95
+ # end
96
+
97
+ # private
98
+
99
+ # PARSER_METHODS = {
100
+ # ".csv" => :parse_csv,
101
+ # ".json" => :parse_json,
102
+ # ".yaml" => :parse_yaml,
103
+ # ".yml" => :parse_yaml
104
+ # }.freeze
105
+ # SUPPORTED_EXTENSIONS = PARSER_METHODS.keys.freeze
106
+
107
+ # def path_for(identifier)
108
+ # path = Pathname.new(identifier)
109
+
110
+ # return path.to_s if path.file? && path.absolute?
111
+
112
+ # found_path = SUPPORTED_EXTENSIONS.lazy.map do |extension|
113
+ # Rails.root.join("app", "content", "data").join("#{identifier}#{extension}")
114
+ # end.find(&:exist?)
115
+
116
+ # found_path&.to_s or raise Errors::FileNotFoundError, "No data file found for '#{identifier}'"
117
+ # end
118
+
119
+ # def data
120
+ # content = File.read(@file_path)
121
+ # extension = File.extname(@file_path)
122
+ # parser = PARSER_METHODS.fetch(extension) do
123
+ # raise Errors::UnsupportedDataFormatError, "Unsupported data format: #{extension}"
124
+ # end
125
+
126
+ # raw_data = send(parser, content)
127
+
128
+ # unless raw_data.is_a?(Array)
129
+ # raise Errors::DataParseError, "Data in '#{@file_path}' must be an array of objects."
130
+ # end
131
+
132
+ # struct = Struct.new(*raw_data.first.keys, keyword_init: true)
133
+ # raw_data.map { struct.new(**it) }
134
+ # rescue Psych::SyntaxError, JSON::ParserError, CSV::MalformedCSVError => error
135
+ # raise Errors::DataParseError, "Failed to parse '#{@file_path}': #{error.message}"
136
+ # end
137
+
138
+ # def parse_yaml(content) = YAML.safe_load(content, permitted_classes: [Symbol], aliases: true)
139
+
140
+ # def parse_json(content) = JSON.parse(content, symbolize_names: true)
141
+
142
+ # def parse_csv(content) = CSV.new(content, headers: true, header_converters: :symbol).to_a.map(&:to_h)
143
+ # end
144
+ # end
@@ -4,7 +4,6 @@ require "perron/site/resource/core"
4
4
  require "perron/site/resource/class_methods"
5
5
  require "perron/site/resource/publishable"
6
6
  require "perron/site/resource/slug"
7
- require "perron/site/resource/context"
8
7
  require "perron/site/resource/separator"
9
8
 
10
9
  module Perron
@@ -31,11 +30,14 @@ module Perron
31
30
  alias_method :to_param, :slug
32
31
 
33
32
  def content
34
- if processable?
35
- ERB.new(Perron::Resource::Separator.new(raw_content).content).result(context.binding)
36
- else
37
- Perron::Resource::Separator.new(raw_content).content
38
- end
33
+ return Perron::Resource::Separator.new(raw_content).content unless processable?
34
+
35
+ ::ApplicationController
36
+ .renderer
37
+ .render(
38
+ inline: Perron::Resource::Separator.new(raw_content).content,
39
+ assigns: {resource: self}
40
+ )
39
41
  end
40
42
 
41
43
  def metadata = Perron::Resource::Separator.new(raw_content).metadata
@@ -56,7 +58,5 @@ module Perron
56
58
  @file_path.delete_prefix(Perron.configuration.input).parameterize
57
59
  ).first(ID_LENGTH)
58
60
  end
59
-
60
- def context = Perron::Resource::Context.new(self)
61
61
  end
62
62
  end
data/lib/perron/site.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "perron/site/builder"
4
4
  require "perron/site/collection"
5
5
  require "perron/site/resource"
6
+ require "perron/site/data"
6
7
 
7
8
  module Perron
8
9
  module Site
@@ -1,3 +1,3 @@
1
1
  module Perron
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
data/perron.gemspec CHANGED
@@ -19,4 +19,8 @@ Gem::Specification.new do |spec|
19
19
  spec.required_ruby_version = ">= 3.4.0"
20
20
 
21
21
  spec.add_dependency "rails", ">= 7.2.0"
22
+
23
+ spec.add_runtime_dependency "csv"
24
+ spec.add_runtime_dependency "json"
25
+ spec.add_runtime_dependency "psych"
22
26
  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.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rails Designer Developers
@@ -23,6 +23,48 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: 7.2.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: csv
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: json
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: psych
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
26
68
  description: Perron is a Rails-based static site generator that follows Rails conventions.
27
69
  It allows you to create content collections with markdown or ERB, configure SEO
28
70
  metadata, and build production-ready static sites while leveraging your existing
@@ -49,6 +91,7 @@ files:
49
91
  - lib/generators/content/templates/root.erb.tt
50
92
  - lib/generators/content/templates/show.html.erb.tt
51
93
  - lib/generators/perron/install_generator.rb
94
+ - lib/generators/perron/templates/README.md.tt
52
95
  - lib/generators/perron/templates/initializer.rb.tt
53
96
  - lib/perron.rb
54
97
  - lib/perron/configuration.rb
@@ -67,9 +110,9 @@ files:
67
110
  - lib/perron/site/builder/paths.rb
68
111
  - lib/perron/site/builder/public_files.rb
69
112
  - lib/perron/site/collection.rb
113
+ - lib/perron/site/data.rb
70
114
  - lib/perron/site/resource.rb
71
115
  - lib/perron/site/resource/class_methods.rb
72
- - lib/perron/site/resource/context.rb
73
116
  - lib/perron/site/resource/core.rb
74
117
  - lib/perron/site/resource/publishable.rb
75
118
  - lib/perron/site/resource/separator.rb
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Perron
4
- class Resource
5
- class Context
6
- def initialize(resource)
7
- @resource = resource
8
- end
9
-
10
- def binding = super
11
- end
12
- end
13
- end