yarrow 0.8.2 → 0.8.5
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 +4 -4
- data/lib/extensions/mementus.rb +7 -0
- data/lib/yarrow/config.rb +1 -1
- data/lib/yarrow/configuration.rb +8 -1
- data/lib/yarrow/content/expansion/strategy.rb +25 -7
- data/lib/yarrow/content/expansion/tree.rb +49 -26
- data/lib/yarrow/content/model.rb +5 -1
- data/lib/yarrow/content/policy.rb +11 -26
- data/lib/yarrow/generator.rb +1 -1
- data/lib/yarrow/schema/definitions.rb +2 -1
- data/lib/yarrow/schema/dictionary.rb +2 -2
- data/lib/yarrow/schema/entity.rb +1 -1
- data/lib/yarrow/schema/types.rb +15 -4
- data/lib/yarrow/version.rb +1 -1
- data/lib/yarrow/web/document.rb +45 -5
- data/lib/yarrow/web/generator.rb +3 -1
- data/lib/yarrow/web/manifest.rb +2 -2
- data/lib/yarrow/web/template.rb +3 -2
- data/lib/yarrow.rb +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33fcaf7b1d66e43c0bd0a83967d0cc8b4891b96ec652bbfd28e5e91e7991b7e2
|
4
|
+
data.tar.gz: ccf4513eee11929864f76386e22adf39224a1898f5b207a3cefb11a30a5ed6e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20272c169c2dd1280c06b80277a981217ae08c77c80892b4c8d07bac0564099ea0cbc248d54ac3160522e7e38c7dbe129aa703e6cb9e9cb848619f2009e15273
|
7
|
+
data.tar.gz: 143a1bbb91909f1e8921b9b587f611293b2f20de9f50d66b18a7346c00e6ebc2078d98ac0e9abcb88cad258e2f3a2d902f1f2eda811d27abd0348ece8f7cf87e
|
data/lib/extensions/mementus.rb
CHANGED
data/lib/yarrow/config.rb
CHANGED
@@ -62,7 +62,7 @@ module Yarrow
|
|
62
62
|
attribute :meta, :any
|
63
63
|
attribute :server, :any
|
64
64
|
attribute :content, :__config_content
|
65
|
-
|
65
|
+
attribute :output, :__config_output
|
66
66
|
end
|
67
67
|
#
|
68
68
|
# `content_dir` and `output_dir` are placeholders and should be overriden
|
data/lib/yarrow/configuration.rb
CHANGED
@@ -59,6 +59,12 @@ module Yarrow
|
|
59
59
|
})
|
60
60
|
end
|
61
61
|
|
62
|
+
output_obj = if config.key?(:output)
|
63
|
+
Yarrow::Config::Output.new(config[:output])
|
64
|
+
else
|
65
|
+
Yarrow::Config::Output.new({ generator: "web", template_dir: "templates" })
|
66
|
+
end
|
67
|
+
|
62
68
|
# TODO: messy hack to get rid of Hashie::Mash, this should either be
|
63
69
|
# automated as part of the schema types or a default value should be
|
64
70
|
# generated here (eg: `"#{Dir.pwd}/docs"`)
|
@@ -70,7 +76,8 @@ module Yarrow
|
|
70
76
|
source_dir: Pathname.new(File.expand_path(source_dir_or_string)),
|
71
77
|
meta: meta_obj,
|
72
78
|
server: server_obj,
|
73
|
-
content: content_obj
|
79
|
+
content: content_obj,
|
80
|
+
output: output_obj
|
74
81
|
)
|
75
82
|
end
|
76
83
|
end
|
@@ -3,19 +3,19 @@ module Yarrow
|
|
3
3
|
module Expansion
|
4
4
|
class Strategy
|
5
5
|
include Yarrow::Tools::FrontMatter
|
6
|
-
|
6
|
+
|
7
7
|
attr_reader :graph
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(graph)
|
10
10
|
@graph = graph
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
# Extract collection level configuration/metadata from the root node for
|
14
14
|
# this content type.
|
15
15
|
def extract_metadata(node, type)
|
16
16
|
# TODO: support _index or _slug convention as well
|
17
17
|
meta_file = node.out(slug: type.to_s).first
|
18
|
-
|
18
|
+
|
19
19
|
if meta_file
|
20
20
|
# Process metadata and add it to the collection node
|
21
21
|
# TODO: pass in content converter object
|
@@ -25,15 +25,32 @@ module Yarrow
|
|
25
25
|
# Otherwise, assume default collection behaviour
|
26
26
|
data = {}
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
# Generate a default title if not provided in metadata
|
30
30
|
unless data.key?(:title)
|
31
31
|
data[:title] = type.to_s.capitalize
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
data
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
|
+
def populate_collection(node, policy, meta_attrs)
|
38
|
+
node.label = :collection
|
39
|
+
node.props[:type] = policy.collection
|
40
|
+
node.props[:resource] = policy.collection_const.new(meta_attrs)
|
41
|
+
end
|
42
|
+
|
43
|
+
def populate_entity(node, policy, meta_attrs)
|
44
|
+
node.label = :item
|
45
|
+
node.props[:type] = policy.entity
|
46
|
+
node.props[:resource] = policy.entity_const.new(meta_attrs)
|
47
|
+
end
|
48
|
+
|
49
|
+
def merge_collection_index(node, policy, meta_attrs)
|
50
|
+
props = { resource: node.props[:resource].merge(meta_attrs) }
|
51
|
+
node.merge_props(props)
|
52
|
+
end
|
53
|
+
|
37
54
|
# Workaround for handling meta and content source in multiple files or a single
|
38
55
|
# file with front matter.
|
39
56
|
def process_content(path)
|
@@ -46,6 +63,7 @@ module Yarrow
|
|
46
63
|
when '.yml'
|
47
64
|
[nil, YAML.load(File.read(path.to_s), symbolize_names: true)]
|
48
65
|
end
|
66
|
+
# TODO: Raise error if unsupported extname reaches here
|
49
67
|
end
|
50
68
|
end
|
51
69
|
end
|
@@ -21,11 +21,12 @@ module Yarrow
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# Extract metadata from given start node
|
24
|
-
collection_metadata = extract_metadata(start_node, policy.container)
|
24
|
+
#collection_metadata = extract_metadata(start_node, policy.container)
|
25
25
|
|
26
26
|
# Collect all nested collections in the subgraph for this content type
|
27
27
|
subcollections = {}
|
28
|
-
|
28
|
+
entity_links = []
|
29
|
+
index_links = []
|
29
30
|
index = nil
|
30
31
|
|
31
32
|
# Scan and collect all nested files from the root
|
@@ -33,12 +34,14 @@ module Yarrow
|
|
33
34
|
if node.label == :directory
|
34
35
|
# Create a collection node representing a collection of documents
|
35
36
|
index = graph.create_node do |collection_node|
|
36
|
-
collection_node.label = :collection
|
37
|
-
collection_node.props[:type] = policy.container
|
38
|
-
collection_node.props[:name] = node.props[:name]
|
39
37
|
|
40
|
-
|
41
|
-
|
38
|
+
collection_attrs = {
|
39
|
+
name: node.props[:name],
|
40
|
+
title: node.props[:name].capitalize,
|
41
|
+
body: ""
|
42
|
+
}
|
43
|
+
|
44
|
+
populate_collection(collection_node, policy, collection_attrs)
|
42
45
|
end
|
43
46
|
|
44
47
|
# Add this collection id to the lookup table for edge construction
|
@@ -54,35 +57,55 @@ module Yarrow
|
|
54
57
|
end
|
55
58
|
elsif node.label == :file
|
56
59
|
body, meta = process_content(node.props[:entry])
|
60
|
+
meta = {} if !meta
|
57
61
|
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
# TODO: document mapping convention for index pages and collection metadata
|
63
|
+
# TODO: underscore _index pattern?
|
64
|
+
bare_basename = node.props[:entry].basename(node.props[:entry].extname)
|
65
|
+
if bare_basename.to_s == "index"
|
66
|
+
index_links << {
|
67
|
+
parent_id: subcollections[node.props[:entry].parent.to_s],
|
68
|
+
index_attrs: meta.merge({ body: body})
|
69
|
+
}
|
70
|
+
else
|
71
|
+
# Create an entity node representing a file mapped to a unique content object
|
72
|
+
entity = graph.create_node do |entity_node|
|
73
|
+
|
74
|
+
entity_slug = node.props[:entry].basename(node.props[:entry].extname).to_s
|
67
75
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
76
|
+
entity_attrs = {
|
77
|
+
name: entity_slug,
|
78
|
+
title: entity_slug.gsub("-", " ").capitalize,
|
79
|
+
body: body
|
80
|
+
}
|
81
|
+
|
82
|
+
populate_entity(entity_node, policy, entity_attrs.merge(meta || {}))
|
83
|
+
end
|
84
|
+
|
85
|
+
# We may not have an expanded node for the parent collection if this is a
|
86
|
+
# preorder traversal so save it for later
|
87
|
+
entity_links << {
|
88
|
+
parent_id: subcollections[node.props[:entry].parent.to_s],
|
89
|
+
child_id: entity
|
90
|
+
}
|
91
|
+
end
|
74
92
|
end
|
75
93
|
end
|
76
94
|
|
77
95
|
# Once all files and directories have been expanded, connect all the child
|
78
|
-
# edges between collections and
|
79
|
-
|
96
|
+
# edges between collections and entities
|
97
|
+
entity_links.each do |entity_link|
|
80
98
|
graph.create_edge do |edge|
|
81
99
|
edge.label = :child
|
82
|
-
edge.from =
|
83
|
-
edge.to =
|
100
|
+
edge.from = entity_link[:parent_id].id
|
101
|
+
edge.to = entity_link[:child_id].id
|
84
102
|
end
|
85
103
|
end
|
104
|
+
|
105
|
+
# Merge index page body and metadata with their parent collections
|
106
|
+
index_links.each do |index_link|
|
107
|
+
merge_collection_index(index_link[:parent_id], policy, index_link[:index_attrs])
|
108
|
+
end
|
86
109
|
end
|
87
110
|
end
|
88
111
|
end
|
data/lib/yarrow/content/model.rb
CHANGED
@@ -4,7 +4,11 @@ module Yarrow
|
|
4
4
|
def initialize(content_config)
|
5
5
|
@policies = {}
|
6
6
|
content_config.source_map.each_entry do |policy_label, policy_spec|
|
7
|
-
@policies[policy_label] = Policy.from_spec(
|
7
|
+
@policies[policy_label] = Policy.from_spec(
|
8
|
+
policy_label,
|
9
|
+
policy_spec,
|
10
|
+
content_config.module
|
11
|
+
)
|
8
12
|
end
|
9
13
|
end
|
10
14
|
|
@@ -9,6 +9,8 @@ module Yarrow
|
|
9
9
|
|
10
10
|
DEFAULT_MATCH_PATH = "."
|
11
11
|
|
12
|
+
MODULE_SEPARATOR = "::"
|
13
|
+
|
12
14
|
# Construct a content policy from the given source specification.
|
13
15
|
def self.from_spec(policy_label, policy_props, module_prefix="")
|
14
16
|
# TODO: validate length, structure etc
|
@@ -27,7 +29,7 @@ module Yarrow
|
|
27
29
|
if policy_props.key?(:entity)
|
28
30
|
Yarrow::Symbols.to_plural(policy_props[:entity])
|
29
31
|
else
|
30
|
-
Yarrow::Symbols.to_plural(
|
32
|
+
Yarrow::Symbols.to_plural(policy_label)
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
@@ -38,7 +40,7 @@ module Yarrow
|
|
38
40
|
if policy_props.key?(:container)
|
39
41
|
Yarrow::Symbols.to_singular(policy_props[:container])
|
40
42
|
else
|
41
|
-
Yarrow::Symbols.to_singular(
|
43
|
+
Yarrow::Symbols.to_singular(policy_label)
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
@@ -64,7 +66,7 @@ module Yarrow
|
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
67
|
-
attr_reader :container, :entity, :expansion, :extensions, :match_path, :
|
69
|
+
attr_reader :container, :entity, :expansion, :extensions, :match_path, :module_prefix
|
68
70
|
|
69
71
|
def initialize(container, entity, expansion, extensions, match_path, module_prefix)
|
70
72
|
@container = container
|
@@ -72,35 +74,18 @@ module Yarrow
|
|
72
74
|
@expansion = expansion
|
73
75
|
@extensions = extensions
|
74
76
|
@match_path = match_path
|
75
|
-
@module_prefix = module_prefix
|
77
|
+
@module_prefix = module_prefix.split(MODULE_SEPARATOR)
|
76
78
|
end
|
77
79
|
|
78
80
|
def container_const
|
79
|
-
@container_const ||= Yarrow::Symbols.to_module_const([module_prefix, container])
|
80
|
-
end
|
81
|
-
|
82
|
-
def entity_const
|
83
|
-
@entity_const ||= Yarrow::Symbols.to_module_const([module_prefix, entity])
|
84
|
-
end
|
85
|
-
|
86
|
-
def _container
|
87
|
-
return @properties.container if @properties.container
|
88
|
-
Yarrow::Symbols.to_plural(@properties.entity)
|
81
|
+
@container_const ||= Yarrow::Symbols.to_module_const([*module_prefix, container])
|
89
82
|
end
|
90
83
|
|
91
|
-
|
92
|
-
|
93
|
-
Yarrow::Symbols.to_singular(@properties.container)
|
94
|
-
end
|
84
|
+
alias_method :collection, :container
|
85
|
+
alias_method :collection_const, :container_const
|
95
86
|
|
96
|
-
def
|
97
|
-
|
98
|
-
DEFAULT_EXTENSIONS
|
99
|
-
end
|
100
|
-
|
101
|
-
def _match_path
|
102
|
-
return @properties.match_path if @properties.match_path
|
103
|
-
DEFAULT_MATCH_PATH
|
87
|
+
def entity_const
|
88
|
+
@entity_const ||= Yarrow::Symbols.to_module_const([*module_prefix, entity])
|
104
89
|
end
|
105
90
|
end
|
106
91
|
end
|
data/lib/yarrow/generator.rb
CHANGED
@@ -8,7 +8,8 @@ module Yarrow
|
|
8
8
|
path: Types::Instance.of(Pathname).accept(String),
|
9
9
|
any: Types::Any.new,
|
10
10
|
array: Types::List.of(Types::Any),
|
11
|
-
hash: Types::Instance.of(Hash)
|
11
|
+
hash: Types::Instance.of(Hash),
|
12
|
+
markdown: Types::Instance.of(Kramdown::Document).accept(String)
|
12
13
|
}
|
13
14
|
|
14
15
|
TEMPLATE_TYPES = {
|
@@ -28,13 +28,13 @@ module Yarrow
|
|
28
28
|
|
29
29
|
if missing_attrs.any?
|
30
30
|
missing_attrs.each do |name|
|
31
|
-
raise "wrong number of attributes" unless @attrs_spec[name].is_a?(Types::Any)
|
31
|
+
raise "#{missing_attrs} wrong number of attributes" unless @attrs_spec[name].is_a?(Types::Any)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
mismatching_attrs = input.keys.difference(@attrs_spec.keys)
|
36
36
|
|
37
|
-
raise "attribute does not exist" if mismatching_attrs.any?
|
37
|
+
raise "attribute #{mismatching_attrs} does not exist" if mismatching_attrs.any?
|
38
38
|
|
39
39
|
input.reduce({}) do |converted, (name, value)|
|
40
40
|
converted[name] = @attrs_spec[name].cast(value)
|
data/lib/yarrow/schema/entity.rb
CHANGED
data/lib/yarrow/schema/types.rb
CHANGED
@@ -31,8 +31,13 @@ module Yarrow
|
|
31
31
|
@accepts = {}
|
32
32
|
end
|
33
33
|
|
34
|
-
def accept(type, constructor=:new)
|
35
|
-
accepts[type] =
|
34
|
+
def accept(type, constructor=:new, options=nil)
|
35
|
+
accepts[type] = if options.nil?
|
36
|
+
[constructor]
|
37
|
+
else
|
38
|
+
[constructor, options]
|
39
|
+
end
|
40
|
+
|
36
41
|
self
|
37
42
|
end
|
38
43
|
|
@@ -41,8 +46,14 @@ module Yarrow
|
|
41
46
|
end
|
42
47
|
|
43
48
|
def coerce(input)
|
44
|
-
constructor = accepts[input.class]
|
45
|
-
|
49
|
+
constructor, options = accepts[input.class]
|
50
|
+
|
51
|
+
# TODO: should we clone all input so copy is stored rather than ref?
|
52
|
+
if options.nil?
|
53
|
+
unit.send(constructor, input)
|
54
|
+
else
|
55
|
+
unit.send(constructor, input, options.clone)
|
56
|
+
end
|
46
57
|
end
|
47
58
|
|
48
59
|
def check_instance_of!(input)
|
data/lib/yarrow/version.rb
CHANGED
data/lib/yarrow/web/document.rb
CHANGED
@@ -6,35 +6,75 @@ module Yarrow
|
|
6
6
|
# too clever which has burned this lib in the past).
|
7
7
|
def initialize(item, parent, is_index)
|
8
8
|
@item = item
|
9
|
+
@resource = item.props[:resource]
|
9
10
|
@parent = parent
|
10
11
|
@is_index = is_index
|
11
12
|
end
|
12
13
|
|
14
|
+
def resource
|
15
|
+
@resource
|
16
|
+
end
|
17
|
+
|
18
|
+
def index
|
19
|
+
_index = @item.out_e(:index)
|
20
|
+
unless _index.first.nil?
|
21
|
+
_index.first.to.props
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def index_body
|
28
|
+
@item.props[:index_body]
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO: manage behaviour with and without current item
|
32
|
+
# TODO: link to manifest
|
33
|
+
def breadcrumbs
|
34
|
+
path = []
|
35
|
+
|
36
|
+
current_parent = @item.in(:collection)
|
37
|
+
|
38
|
+
while !current_parent.first.nil?
|
39
|
+
path << current_parent.first.props[:resource]
|
40
|
+
current_parent = current_parent.in(:collection)
|
41
|
+
end
|
42
|
+
|
43
|
+
path.reverse
|
44
|
+
end
|
45
|
+
|
13
46
|
def name
|
14
|
-
@
|
47
|
+
@resource.name
|
15
48
|
end
|
16
49
|
|
17
50
|
def title
|
18
|
-
@
|
51
|
+
@resource.title
|
19
52
|
end
|
20
53
|
|
21
54
|
def type
|
22
55
|
@item.props[:type]
|
23
56
|
end
|
24
57
|
|
58
|
+
def body
|
59
|
+
return @resource.body.to_html if @resource.respond_to?(:body)
|
60
|
+
""
|
61
|
+
end
|
62
|
+
|
25
63
|
def url
|
26
64
|
if @parent.nil?
|
27
65
|
"/"
|
28
66
|
else
|
29
|
-
segments = [@
|
67
|
+
segments = [@resource.name]
|
30
68
|
current = @parent
|
31
69
|
|
32
70
|
until current.in(:collection).first.nil? do
|
33
|
-
segments << current.props[:name
|
71
|
+
segments << current.props[:resource].name
|
34
72
|
current = current.in(:collection).first
|
35
73
|
end
|
36
74
|
|
37
|
-
"/"
|
75
|
+
suffix = @is_index ? "/" : ""
|
76
|
+
|
77
|
+
"/" + segments.reverse.join("/") + suffix
|
38
78
|
end
|
39
79
|
end
|
40
80
|
end
|
data/lib/yarrow/web/generator.rb
CHANGED
@@ -18,7 +18,7 @@ module Yarrow
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def write_document(document)
|
21
|
-
template = Template.for_document(document)
|
21
|
+
template = Template.for_document(document, config)
|
22
22
|
write_output_file(document.url, template.render(document))
|
23
23
|
end
|
24
24
|
|
@@ -39,6 +39,8 @@ module Yarrow
|
|
39
39
|
File.open(path.to_s, 'w+:UTF-8') do |file|
|
40
40
|
file.puts(content)
|
41
41
|
end
|
42
|
+
|
43
|
+
puts "[write] #{path} → #{url}"
|
42
44
|
end
|
43
45
|
|
44
46
|
def generate_sitemap(manifest)
|
data/lib/yarrow/web/manifest.rb
CHANGED
@@ -12,7 +12,7 @@ module Yarrow
|
|
12
12
|
unless collection.props[:index_only]
|
13
13
|
collection.out(:item).each do |item|
|
14
14
|
#if item[:entity].status.to_sym == :published
|
15
|
-
if item.props[:name
|
15
|
+
if item.props[:resource].name == "index"
|
16
16
|
index = item
|
17
17
|
else
|
18
18
|
manifest.add_document(item_context(item))
|
@@ -25,7 +25,7 @@ module Yarrow
|
|
25
25
|
unless collection.props[:content_only]
|
26
26
|
if index
|
27
27
|
manifest.add_document(collection_index_context(collection, index))
|
28
|
-
else
|
28
|
+
else
|
29
29
|
manifest.add_document(collection_context(collection))
|
30
30
|
end
|
31
31
|
end
|
data/lib/yarrow/web/template.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
module Yarrow
|
2
2
|
module Web
|
3
3
|
class Template
|
4
|
-
def self.for_document(document)
|
4
|
+
def self.for_document(document, config)
|
5
5
|
layout_name = if document.respond_to?(:layout)
|
6
6
|
document.layout || document.type
|
7
7
|
else
|
8
8
|
document.type
|
9
9
|
end
|
10
10
|
|
11
|
-
@template_dir =
|
11
|
+
@template_dir = config.output.template_dir
|
12
|
+
#@template_dir = "./spec/fixtures/templates/doctest"
|
12
13
|
@template_ext = ".html"
|
13
14
|
|
14
15
|
template_file = "#{layout_name}#{@template_ext}"
|
data/lib/yarrow.rb
CHANGED
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.8.
|
4
|
+
version: 0.8.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Rickerby
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|