yarrow 0.9.1 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +2 -2
- data/lib/yarrow/config.rb +4 -4
- data/lib/yarrow/format/methods/front_matter.rb +1 -1
- data/lib/yarrow/schema/entity.rb +8 -10
- data/lib/yarrow/schema/value.rb +21 -12
- data/lib/yarrow/symbols.rb +14 -0
- data/lib/yarrow/version.rb +1 -1
- data/lib/yarrow.rb +1 -1
- data/yarrow.gemspec +3 -2
- metadata +33 -9
- data/lib/yarrow/content/expansion/directory_map.rb +0 -21
- data/lib/yarrow/content/expansion/file_list.rb +0 -15
- data/lib/yarrow/content/expansion/strategy.rb +0 -118
- data/lib/yarrow/content/expansion/tree.rb +0 -93
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a2ebdee5113cc218a9744d5da88b01cb0e7604b4ecfe0d133f776d48d2b4b97
|
4
|
+
data.tar.gz: 967ba36fec57253eea8069f903828b8529f43802578c8be009413e4a5373e1f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 982f7c783ed933419504e6722e9ffbacaf513468a16cdb07ca4ec7ffc859d5eaed1470948d8c1ca7ce5aa00f5e0fc05b9c29a38f0f0e2972aa6d2f348aec2c7b
|
7
|
+
data.tar.gz: 4debe486afb56d7f664a0dd730db05429b2d7256a5f2789f1987339b394f404ac0f59f67d9e2f25126ad946675a46b7f910a91881d447afed148cb56eb4b5317
|
data/.github/workflows/ruby.yml
CHANGED
@@ -10,10 +10,10 @@ jobs:
|
|
10
10
|
fail-fast: false
|
11
11
|
matrix:
|
12
12
|
os: [ubuntu-latest, macos-latest]
|
13
|
-
ruby: [
|
13
|
+
ruby: [3.1, 3.2, "3.3.0-preview1"]
|
14
14
|
runs-on: ${{ matrix.os }}
|
15
15
|
steps:
|
16
|
-
- uses: actions/checkout@
|
16
|
+
- uses: actions/checkout@v4
|
17
17
|
- name: Set up Ruby
|
18
18
|
uses: ruby/setup-ruby@v1
|
19
19
|
with:
|
data/lib/yarrow/config.rb
CHANGED
@@ -53,13 +53,13 @@ module Yarrow
|
|
53
53
|
# Yarrow::Schema::Types::Map.of(Symbol)
|
54
54
|
# )
|
55
55
|
|
56
|
-
class Content < Yarrow::Schema::Entity
|
56
|
+
class Content < Yarrow::Schema::Entity
|
57
57
|
attribute :module, :string
|
58
58
|
#attribute :source_map, :__config_source_map
|
59
59
|
attribute :source_map, :hash
|
60
60
|
end
|
61
61
|
|
62
|
-
class Output < Yarrow::Schema::Entity
|
62
|
+
class Output < Yarrow::Schema::Entity
|
63
63
|
attribute :generator, :string
|
64
64
|
attribute :template_dir, :path
|
65
65
|
#attribute :scripts, :array
|
@@ -71,8 +71,8 @@ module Yarrow
|
|
71
71
|
attribute :output_dir, :path
|
72
72
|
attribute :meta, :any
|
73
73
|
attribute :server, :any
|
74
|
-
attribute :content, :
|
75
|
-
attribute :output, :
|
74
|
+
attribute :content, :yarrow_config_content
|
75
|
+
attribute :output, :yarrow_config_output
|
76
76
|
end
|
77
77
|
#
|
78
78
|
# `content_dir` and `output_dir` are placeholders and should be overriden
|
data/lib/yarrow/schema/entity.rb
CHANGED
@@ -20,11 +20,16 @@ module Yarrow
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def inherited(class_name)
|
23
|
+
class_type = Yarrow::Schema::Types::Instance.of(class_name).accept(Hash)
|
24
|
+
|
23
25
|
if @label
|
24
|
-
|
25
|
-
Yarrow::Schema::Definitions.register(@label, class_type)
|
26
|
+
label = @label
|
26
27
|
@label = nil
|
28
|
+
else
|
29
|
+
label = Yarrow::Symbols.from_const(class_name)
|
27
30
|
end
|
31
|
+
|
32
|
+
Yarrow::Schema::Definitions.register(label, class_type)
|
28
33
|
end
|
29
34
|
end
|
30
35
|
|
@@ -32,14 +37,7 @@ module Yarrow
|
|
32
37
|
converted = dictionary.cast(config)
|
33
38
|
|
34
39
|
converted.each_pair do |key, value|
|
35
|
-
#
|
36
|
-
#
|
37
|
-
# defined_type = dictionary[key]
|
38
|
-
#
|
39
|
-
# unless value.is_a?(defined_type)
|
40
|
-
# raise "#{key} accepts #{defined_type} but #{value.class} given"
|
41
|
-
# end
|
42
|
-
|
40
|
+
# TODO: should we represent this as an attribute set rather than instance vars?
|
43
41
|
instance_variable_set("@#{key}", value)
|
44
42
|
end
|
45
43
|
end
|
data/lib/yarrow/schema/value.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
1
|
module Yarrow
|
2
2
|
module Schema
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
3
|
+
class ValueType < Struct
|
4
|
+
def self.register(label)
|
5
|
+
class_type = Yarrow::Schema::Types::Instance.of(self).accept(Hash)
|
6
|
+
Yarrow::Schema::Definitions.register(label, class_type)
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
# Automatically register when struct is defined as a class extension
|
11
|
+
# rather than anonymous struct class.
|
12
|
+
def self.inherited(subclass)
|
13
|
+
if subclass.name
|
14
|
+
self.register(subclass.name.downcase.to_sym)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
13
18
|
|
14
19
|
# Value object (with comparison by value equality). This just chucks back a
|
15
20
|
# Ruby struct but wraps the constructor with method advice that handles
|
@@ -33,12 +38,16 @@ module Yarrow
|
|
33
38
|
|
34
39
|
validator = Dictionary.new(fields_spec)
|
35
40
|
|
36
|
-
struct =
|
41
|
+
struct = ValueType.new(*slots_spec, keyword_init: true, &block)
|
37
42
|
|
38
43
|
struct.define_method :initialize do |*args, **kwargs|
|
39
44
|
attr_values = if args.any?
|
40
45
|
raise ArgumentError.new("cannot mix slots and kwargs") if kwargs.any?
|
41
|
-
|
46
|
+
if args.first.instance_of?(Hash) and args.count == 1
|
47
|
+
args.first
|
48
|
+
else
|
49
|
+
Hash[slots.zip(args)]
|
50
|
+
end
|
42
51
|
else
|
43
52
|
kwargs
|
44
53
|
end
|
data/lib/yarrow/symbols.rb
CHANGED
@@ -19,6 +19,20 @@ module Yarrow
|
|
19
19
|
Object.const_get(Strings::Case.pascalcase(atom.to_s).to_sym)
|
20
20
|
end
|
21
21
|
|
22
|
+
# Converts a string name of class const to a symbol atom
|
23
|
+
#
|
24
|
+
# @param [Class, String, #to_s] const_obj
|
25
|
+
# @return [Symbol]
|
26
|
+
def self.from_const(const_obj)
|
27
|
+
const_lookup = if const_obj.respond_to?(:name)
|
28
|
+
const_obj.name
|
29
|
+
else
|
30
|
+
const_obj.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
Strings::Case.underscore(const_lookup).to_sym
|
34
|
+
end
|
35
|
+
|
22
36
|
# @param [Symbol, String] atom
|
23
37
|
# @return [Symbol]
|
24
38
|
def self.to_singular(atom)
|
data/lib/yarrow/version.rb
CHANGED
data/lib/yarrow.rb
CHANGED
data/yarrow.gemspec
CHANGED
@@ -24,8 +24,9 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_runtime_dependency 'parallel', '~> 1.22.1'
|
25
25
|
spec.add_runtime_dependency 'strings-inflection', '~> 0.1'
|
26
26
|
spec.add_runtime_dependency 'strings-case', '~> 0.3'
|
27
|
-
spec.add_runtime_dependency 'toml', '~>
|
28
|
-
|
27
|
+
spec.add_runtime_dependency 'toml-rb', '~> 2.2.0'
|
28
|
+
spec.add_runtime_dependency 'shale', '~> 1.0.0'
|
29
|
+
spec.add_runtime_dependency 'phlex', '~> 1.8.1'
|
29
30
|
spec.add_runtime_dependency 'kramdown', '~> 2.4.0'
|
30
31
|
spec.add_development_dependency 'rake', '~> 13.0'
|
31
32
|
spec.add_development_dependency 'rspec', '~> 3.11'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yarrow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Rickerby
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09-
|
11
|
+
date: 2023-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -165,19 +165,47 @@ dependencies:
|
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0.3'
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
|
-
name: toml
|
168
|
+
name: toml-rb
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
170
170
|
requirements:
|
171
171
|
- - "~>"
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version:
|
173
|
+
version: 2.2.0
|
174
174
|
type: :runtime
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
|
-
version:
|
180
|
+
version: 2.2.0
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: shale
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: 1.0.0
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 1.0.0
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: phlex
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: 1.8.1
|
202
|
+
type: :runtime
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - "~>"
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: 1.8.1
|
181
209
|
- !ruby/object:Gem::Dependency
|
182
210
|
name: kramdown
|
183
211
|
requirement: !ruby/object:Gem::Requirement
|
@@ -277,13 +305,9 @@ files:
|
|
277
305
|
- lib/yarrow/console_runner.rb
|
278
306
|
- lib/yarrow/content/expansion/aggregator.rb
|
279
307
|
- lib/yarrow/content/expansion/basename_merge.rb
|
280
|
-
- lib/yarrow/content/expansion/directory_map.rb
|
281
308
|
- lib/yarrow/content/expansion/directory_merge.rb
|
282
|
-
- lib/yarrow/content/expansion/file_list.rb
|
283
309
|
- lib/yarrow/content/expansion/filename_map.rb
|
284
|
-
- lib/yarrow/content/expansion/strategy.rb
|
285
310
|
- lib/yarrow/content/expansion/traversal.rb
|
286
|
-
- lib/yarrow/content/expansion/tree.rb
|
287
311
|
- lib/yarrow/content/graph.rb
|
288
312
|
- lib/yarrow/content/model.rb
|
289
313
|
- lib/yarrow/content/policy.rb
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module Yarrow
|
2
|
-
module Content
|
3
|
-
module Expansion
|
4
|
-
class DirectoryMap < Aggregator
|
5
|
-
def expand_source(container, policy)
|
6
|
-
puts "create_node label=:collection type=:#{policy.container} name='#{container.props[:basename]}'"
|
7
|
-
@current_collection = container.props[:basename]
|
8
|
-
end
|
9
|
-
|
10
|
-
def expand_directory(collection, policy)
|
11
|
-
puts "create_node label=:collection type=:#{policy.collection} name='#{collection.props[:basename]}' collection=#{@current_collection}"
|
12
|
-
@current_collection = collection.props[:basename]
|
13
|
-
end
|
14
|
-
|
15
|
-
def expand_file(entity, policy)
|
16
|
-
puts "create_node label=:entity type=:#{policy.entity} name='#{entity.props[:basename]}' collection='#{@current_collection}"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Yarrow
|
2
|
-
module Content
|
3
|
-
module Expansion
|
4
|
-
class FileList < Strategy
|
5
|
-
def expand(policy)
|
6
|
-
start_node = graph.n(:root).out(name: policy.container.to_s)
|
7
|
-
|
8
|
-
start_node.out(:files).each do |file_node|
|
9
|
-
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,118 +0,0 @@
|
|
1
|
-
module Yarrow
|
2
|
-
module Content
|
3
|
-
module Expansion
|
4
|
-
class Strategy
|
5
|
-
include Yarrow::Tools::FrontMatter
|
6
|
-
|
7
|
-
attr_reader :graph
|
8
|
-
|
9
|
-
def initialize(graph)
|
10
|
-
@graph = graph
|
11
|
-
@subcollections = {}
|
12
|
-
@entity_links = []
|
13
|
-
@index_links = []
|
14
|
-
@index = nil
|
15
|
-
end
|
16
|
-
|
17
|
-
# Expand a directory to a collection node representing a collection of entities
|
18
|
-
def expand_directory(policy, node)
|
19
|
-
index = graph.create_node do |collection_node|
|
20
|
-
|
21
|
-
collection_attrs = {
|
22
|
-
name: node.props[:name],
|
23
|
-
title: node.props[:name].capitalize,
|
24
|
-
body: ""
|
25
|
-
}
|
26
|
-
|
27
|
-
populate_collection(collection_node, policy, collection_attrs)
|
28
|
-
end
|
29
|
-
|
30
|
-
# Add this collection id to the lookup table for edge construction
|
31
|
-
@subcollections[node.props[:path]] = index
|
32
|
-
|
33
|
-
# Join the collection to its parent
|
34
|
-
unless node.props[:slug] == policy.collection.to_s || !@subcollections.key?(node.props[:entry].parent.to_s)
|
35
|
-
graph.create_edge do |edge|
|
36
|
-
edge.label = :child
|
37
|
-
edge.from = @subcollections[node.props[:entry].parent.to_s].id
|
38
|
-
edge.to = index.id
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Extract collection level configuration/metadata from the root node for
|
44
|
-
# this content type.
|
45
|
-
def extract_metadata(node, type)
|
46
|
-
# TODO: support _index or _slug convention as well
|
47
|
-
meta_file = node.out(slug: type.to_s).first
|
48
|
-
|
49
|
-
if meta_file
|
50
|
-
# Process metadata and add it to the collection node
|
51
|
-
# TODO: pass in content converter object
|
52
|
-
# TODO: index/body content by default if extracted from frontmatter
|
53
|
-
body, data = process_content(meta_file.props[:entry])
|
54
|
-
else
|
55
|
-
# Otherwise, assume default collection behaviour
|
56
|
-
data = {}
|
57
|
-
end
|
58
|
-
|
59
|
-
# Generate a default title if not provided in metadata
|
60
|
-
unless data.key?(:title)
|
61
|
-
data[:title] = type.to_s.capitalize
|
62
|
-
end
|
63
|
-
|
64
|
-
data
|
65
|
-
end
|
66
|
-
|
67
|
-
def populate_collection(node, policy, meta_attrs)
|
68
|
-
node.label = :collection
|
69
|
-
node.props[:type] = policy.collection
|
70
|
-
node.props[:resource] = policy.collection_const.new(meta_attrs)
|
71
|
-
end
|
72
|
-
|
73
|
-
def populate_entity(node, policy, meta_attrs)
|
74
|
-
node.label = :item
|
75
|
-
node.props[:type] = policy.entity
|
76
|
-
node.props[:resource] = policy.entity_const.new(meta_attrs)
|
77
|
-
end
|
78
|
-
|
79
|
-
def merge_collection_index(node, policy, meta_attrs)
|
80
|
-
props = { resource: node.props[:resource].merge(meta_attrs) }
|
81
|
-
node.merge_props(props)
|
82
|
-
end
|
83
|
-
|
84
|
-
# Workaround for handling meta and content source in multiple files or a single
|
85
|
-
# file with front matter.
|
86
|
-
def process_content(path)
|
87
|
-
case path.extname
|
88
|
-
when '.htm', '.md'
|
89
|
-
read_split_content(path.to_s, symbolize_keys: true)
|
90
|
-
# when '.md'
|
91
|
-
# body, data = read_split_content(path.to_s, symbolize_keys: true)
|
92
|
-
# [Kramdown::Document.new(body).to_html, data]
|
93
|
-
when '.yml'
|
94
|
-
[nil, YAML.load(File.read(path.to_s), symbolize_names: true)]
|
95
|
-
end
|
96
|
-
# TODO: Raise error if unsupported extname reaches here
|
97
|
-
end
|
98
|
-
|
99
|
-
def connect_expanded_entities
|
100
|
-
# Once all files and directories have been expanded, connect all the child
|
101
|
-
# edges between collections and entities
|
102
|
-
@entity_links.each do |entity_link|
|
103
|
-
graph.create_edge do |edge|
|
104
|
-
edge.label = :child
|
105
|
-
edge.from = entity_link[:parent_id].id
|
106
|
-
edge.to = entity_link[:child_id].id
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
# Merge index page body and metadata with their parent collections
|
111
|
-
@index_links.each do |index_link|
|
112
|
-
merge_collection_index(index_link[:parent_id], policy, index_link[:index_attrs])
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
module Yarrow
|
2
|
-
module Content
|
3
|
-
module Expansion
|
4
|
-
class Tree < Strategy
|
5
|
-
def expand(policy)
|
6
|
-
# If match path represents entire content dir, then include the entire
|
7
|
-
# content dir instead of scanning from a subfolder matching the name of
|
8
|
-
# the collection.
|
9
|
-
#start_node = if policy.match_path == "."
|
10
|
-
start_node = if policy.match_path == "."
|
11
|
-
# TODO: match against source_dir
|
12
|
-
graph.n(:root).out(:directory)
|
13
|
-
else
|
14
|
-
graph.n(name: policy.match_path)
|
15
|
-
end
|
16
|
-
|
17
|
-
# Collect all nested collections in the subgraph for this content type
|
18
|
-
@subcollections = {}
|
19
|
-
@entity_links = []
|
20
|
-
@index_links = []
|
21
|
-
@index = nil
|
22
|
-
|
23
|
-
# Scan and collect all nested files from the root
|
24
|
-
start_node.depth_first.each do |node|
|
25
|
-
if node.label == :directory
|
26
|
-
expand_directory(policy, node)
|
27
|
-
elsif node.label == :file
|
28
|
-
expand_file_by_extension(policy, node)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Once all files and directories have been expanded, connect all the child
|
33
|
-
# edges between collections and entities
|
34
|
-
@entity_links.each do |entity_link|
|
35
|
-
graph.create_edge do |edge|
|
36
|
-
edge.label = :child
|
37
|
-
edge.from = entity_link[:parent_id].id
|
38
|
-
edge.to = entity_link[:child_id].id
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Merge index page body and metadata with their parent collections
|
43
|
-
@index_links.each do |index_link|
|
44
|
-
merge_collection_index(index_link[:parent_id], policy, index_link[:index_attrs])
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def expand_file_by_basename(policy, node)
|
49
|
-
body, meta = process_content(node.props[:entry])
|
50
|
-
meta = {} if !meta
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
def expand_file_by_extension(policy, node)
|
56
|
-
body, meta = process_content(node.props[:entry])
|
57
|
-
meta = {} if !meta
|
58
|
-
|
59
|
-
# TODO: document mapping convention for index pages and collection metadata
|
60
|
-
# TODO: underscore _index pattern?
|
61
|
-
bare_basename = node.props[:entry].basename(node.props[:entry].extname)
|
62
|
-
if bare_basename.to_s == "index"
|
63
|
-
@index_links << {
|
64
|
-
parent_id: @subcollections[node.props[:entry].parent.to_s],
|
65
|
-
index_attrs: meta.merge({ body: body})
|
66
|
-
}
|
67
|
-
else
|
68
|
-
# Create an entity node representing a file mapped to a unique content object
|
69
|
-
entity = graph.create_node do |entity_node|
|
70
|
-
|
71
|
-
entity_slug = node.props[:entry].basename(node.props[:entry].extname).to_s
|
72
|
-
|
73
|
-
entity_attrs = {
|
74
|
-
name: entity_slug,
|
75
|
-
title: entity_slug.gsub("-", " ").capitalize,
|
76
|
-
body: body
|
77
|
-
}
|
78
|
-
|
79
|
-
populate_entity(entity_node, policy, entity_attrs.merge(meta || {}))
|
80
|
-
end
|
81
|
-
|
82
|
-
# We may not have an expanded node for the parent collection if this is a
|
83
|
-
# preorder traversal so save it for later
|
84
|
-
@entity_links << {
|
85
|
-
parent_id: @subcollections[node.props[:entry].parent.to_s],
|
86
|
-
child_id: entity
|
87
|
-
}
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|