yarrow 0.8.0 → 0.8.2
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/yarrow/config.rb +18 -3
- data/lib/yarrow/configuration.rb +13 -1
- data/lib/yarrow/content/expansion/strategy.rb +53 -0
- data/lib/yarrow/content/expansion/tree.rb +90 -0
- data/lib/yarrow/content/graph.rb +0 -5
- data/lib/yarrow/content/model.rb +11 -34
- data/lib/yarrow/content/policy.rb +76 -17
- data/lib/yarrow/defaults.yml +6 -3
- data/lib/yarrow/generator.rb +2 -2
- data/lib/yarrow/schema/definitions.rb +44 -7
- data/lib/yarrow/schema/entity.rb +13 -0
- data/lib/yarrow/version.rb +1 -1
- data/lib/yarrow/web/document.rb +13 -8
- data/lib/yarrow/web/manifest.rb +27 -28
- data/lib/yarrow.rb +7 -9
- metadata +4 -5
- data/lib/yarrow/content/expansion.rb +0 -16
- data/lib/yarrow/content/expansion_strategy.rb +0 -51
- data/lib/yarrow/content/tree_expansion.rb +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ea37c60e3c8bfe51e6ceb9425f0d0c81b93757a681c7987f7852c3335644824
|
4
|
+
data.tar.gz: 19cd090d3b3348f633f5d303d8b826fd2b8c054b790136355a41d9b4f0f098c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebd0dd29f6d8800b20560c2e24fd2988caecf19e9ae5f3e44a3d0b427afc9201971be1b086705e72c874683d30625821b270e6ad944c5c28ca9b6cd9ecd65ea2
|
7
|
+
data.tar.gz: 92d36891c8a270ff663a299b2a217ffe3bb6985200e70d84c4c9d29ee87bb00fb7b2e824e0d932d585b5f8b4a22d578748ee58ad3ae022aa8f1b8ab04117ac6f
|
data/lib/yarrow/config.rb
CHANGED
@@ -37,17 +37,32 @@ module Yarrow
|
|
37
37
|
:port,
|
38
38
|
:host,
|
39
39
|
:handler,
|
40
|
-
|
41
|
-
:middleware,
|
42
|
-
#:root_dir
|
40
|
+
:middleware
|
43
41
|
)
|
44
42
|
|
43
|
+
# Yarrow::Schema.define do
|
44
|
+
# type :config_source_map, Yarrow::Schema::Types::Instance.of(Hash).accept(Symbol)
|
45
|
+
# end
|
46
|
+
|
47
|
+
class Content < Yarrow::Schema::Entity[:__config_content]
|
48
|
+
attribute :module, :string
|
49
|
+
attribute :source_map, :hash
|
50
|
+
end
|
51
|
+
|
52
|
+
class Output < Yarrow::Schema::Entity[:__config_output]
|
53
|
+
attribute :generator, :string
|
54
|
+
attribute :template_dir, :path
|
55
|
+
#attribute :scripts, :array
|
56
|
+
end
|
57
|
+
|
45
58
|
# Top level root config namespace.
|
46
59
|
class Instance < Yarrow::Schema::Entity
|
47
60
|
attribute :source_dir, :path
|
48
61
|
attribute :output_dir, :path
|
49
62
|
attribute :meta, :any
|
50
63
|
attribute :server, :any
|
64
|
+
attribute :content, :__config_content
|
65
|
+
#attribute :output, :__config_output
|
51
66
|
end
|
52
67
|
#
|
53
68
|
# `content_dir` and `output_dir` are placeholders and should be overriden
|
data/lib/yarrow/configuration.rb
CHANGED
@@ -48,6 +48,17 @@ module Yarrow
|
|
48
48
|
nil
|
49
49
|
end
|
50
50
|
|
51
|
+
content_obj = if config.key?(:content)
|
52
|
+
Yarrow::Config::Content.new(config[:content])
|
53
|
+
else
|
54
|
+
Yarrow::Config::Content.new({
|
55
|
+
module: "",
|
56
|
+
source_map: {
|
57
|
+
pages: :page
|
58
|
+
}
|
59
|
+
})
|
60
|
+
end
|
61
|
+
|
51
62
|
# TODO: messy hack to get rid of Hashie::Mash, this should either be
|
52
63
|
# automated as part of the schema types or a default value should be
|
53
64
|
# generated here (eg: `"#{Dir.pwd}/docs"`)
|
@@ -58,7 +69,8 @@ module Yarrow
|
|
58
69
|
output_dir: Pathname.new(File.expand_path(out_dir_or_string)),
|
59
70
|
source_dir: Pathname.new(File.expand_path(source_dir_or_string)),
|
60
71
|
meta: meta_obj,
|
61
|
-
server: server_obj
|
72
|
+
server: server_obj,
|
73
|
+
content: content_obj
|
62
74
|
)
|
63
75
|
end
|
64
76
|
end
|
@@ -0,0 +1,53 @@
|
|
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
|
+
end
|
12
|
+
|
13
|
+
# Extract collection level configuration/metadata from the root node for
|
14
|
+
# this content type.
|
15
|
+
def extract_metadata(node, type)
|
16
|
+
# TODO: support _index or _slug convention as well
|
17
|
+
meta_file = node.out(slug: type.to_s).first
|
18
|
+
|
19
|
+
if meta_file
|
20
|
+
# Process metadata and add it to the collection node
|
21
|
+
# TODO: pass in content converter object
|
22
|
+
# TODO: index/body content by default if extracted from frontmatter
|
23
|
+
body, data = process_content(meta_file.props[:entry])
|
24
|
+
else
|
25
|
+
# Otherwise, assume default collection behaviour
|
26
|
+
data = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
# Generate a default title if not provided in metadata
|
30
|
+
unless data.key?(:title)
|
31
|
+
data[:title] = type.to_s.capitalize
|
32
|
+
end
|
33
|
+
|
34
|
+
data
|
35
|
+
end
|
36
|
+
|
37
|
+
# Workaround for handling meta and content source in multiple files or a single
|
38
|
+
# file with front matter.
|
39
|
+
def process_content(path)
|
40
|
+
case path.extname
|
41
|
+
when '.htm', '.md'
|
42
|
+
read_split_content(path.to_s, symbolize_keys: true)
|
43
|
+
# when '.md'
|
44
|
+
# body, data = read_split_content(path.to_s, symbolize_keys: true)
|
45
|
+
# [Kramdown::Document.new(body).to_html, data]
|
46
|
+
when '.yml'
|
47
|
+
[nil, YAML.load(File.read(path.to_s), symbolize_names: true)]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Yarrow
|
2
|
+
module Content
|
3
|
+
module Expansion
|
4
|
+
class Tree < Strategy
|
5
|
+
def expand(policy)
|
6
|
+
#p graph.n(:root).out(:directory).to_a.count
|
7
|
+
#policy.match()
|
8
|
+
|
9
|
+
#p graph.n(:root).out(:directory).first.props[:name]
|
10
|
+
type = policy.container
|
11
|
+
|
12
|
+
# If match path represents entire content dir, then include the entire
|
13
|
+
# content dir instead of scanning from a subfolder matching the name of
|
14
|
+
# the collection.
|
15
|
+
#start_node = if policy.match_path == "."
|
16
|
+
start_node = if true
|
17
|
+
# TODO: match against source_dir
|
18
|
+
graph.n(:root).out(:directory)
|
19
|
+
else
|
20
|
+
graph.n(:root).out(name: policy.container.to_s)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Extract metadata from given start node
|
24
|
+
collection_metadata = extract_metadata(start_node, policy.container)
|
25
|
+
|
26
|
+
# Collect all nested collections in the subgraph for this content type
|
27
|
+
subcollections = {}
|
28
|
+
item_links = []
|
29
|
+
index = nil
|
30
|
+
|
31
|
+
# Scan and collect all nested files from the root
|
32
|
+
start_node.depth_first.each do |node|
|
33
|
+
if node.label == :directory
|
34
|
+
# Create a collection node representing a collection of documents
|
35
|
+
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
|
+
|
40
|
+
# TODO: title needs to be defined from metadata
|
41
|
+
collection_node.props[:title] = node.props[:name].capitalize
|
42
|
+
end
|
43
|
+
|
44
|
+
# Add this collection id to the lookup table for edge construction
|
45
|
+
subcollections[node.props[:path]] = index
|
46
|
+
|
47
|
+
# Join the collection to its parent
|
48
|
+
unless node.props[:slug] == type.to_s || !subcollections.key?(node.props[:entry].parent.to_s)
|
49
|
+
graph.create_edge do |edge|
|
50
|
+
edge.label = :child
|
51
|
+
edge.from = subcollections[node.props[:entry].parent.to_s].id
|
52
|
+
edge.to = index.id
|
53
|
+
end
|
54
|
+
end
|
55
|
+
elsif node.label == :file
|
56
|
+
body, meta = process_content(node.props[:entry])
|
57
|
+
|
58
|
+
# Create an item node representing a file mapped to a unique content object
|
59
|
+
item = graph.create_node do |item_node|
|
60
|
+
item_node.label = :item
|
61
|
+
item_node.props[:type] = policy.entity
|
62
|
+
item_node.props[:name] = node.props[:entry].basename(node.props[:entry].extname).to_s
|
63
|
+
item_node.props[:body] = body if body
|
64
|
+
item_node.props[:title] = meta[:title] if meta
|
65
|
+
# TODO: better handling of metadata on node props
|
66
|
+
end
|
67
|
+
|
68
|
+
# We may not have an expanded node for the parent collection if this is a
|
69
|
+
# preorder traversal so save it for later
|
70
|
+
item_links << {
|
71
|
+
parent_path: node.props[:entry].parent.to_s,
|
72
|
+
item_id: item.id
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Once all files and directories have been expanded, connect all the child
|
78
|
+
# edges between collections and items
|
79
|
+
item_links.each do |item_link|
|
80
|
+
graph.create_edge do |edge|
|
81
|
+
edge.label = :child
|
82
|
+
edge.from = subcollections[item_link[:parent_path]].id
|
83
|
+
edge.to = item_link[:item_id]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/yarrow/content/graph.rb
CHANGED
data/lib/yarrow/content/model.rb
CHANGED
@@ -1,45 +1,22 @@
|
|
1
1
|
module Yarrow
|
2
2
|
module Content
|
3
|
-
ContentSpec = Yarrow::Schema::Value.new(:namespace, :model) do
|
4
|
-
def to_world
|
5
|
-
"world"
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
ContentPolicy = Yarrow::Schema::Value.new(
|
10
|
-
:dir,
|
11
|
-
:file,
|
12
|
-
:expansion,
|
13
|
-
:container,
|
14
|
-
:record
|
15
|
-
)
|
16
|
-
|
17
3
|
class Model
|
18
|
-
def initialize(
|
19
|
-
@
|
20
|
-
|
21
|
-
|
22
|
-
@policies = if spec.nil?
|
23
|
-
spec = {
|
24
|
-
root: ContentPolicy.new(
|
25
|
-
expansion: :tree,
|
26
|
-
dir: "*",
|
27
|
-
file: "*.md",
|
28
|
-
:container => :pages,
|
29
|
-
:record => :page
|
30
|
-
)
|
31
|
-
}
|
32
|
-
else
|
33
|
-
spec.model
|
4
|
+
def initialize(content_config)
|
5
|
+
@policies = {}
|
6
|
+
content_config.source_map.each_entry do |policy_label, policy_spec|
|
7
|
+
@policies[policy_label] = Policy.from_spec(policy_label, policy_spec)
|
34
8
|
end
|
35
9
|
end
|
36
10
|
|
37
|
-
def
|
38
|
-
@policies.each_value
|
11
|
+
def expand(graph)
|
12
|
+
@policies.each_value do |policy|
|
13
|
+
strategy = Expansion::Tree.new(graph)
|
14
|
+
strategy.expand(policy)
|
15
|
+
end
|
39
16
|
end
|
40
17
|
|
41
|
-
def policy_for(
|
42
|
-
@policies[
|
18
|
+
def policy_for(policy_label)
|
19
|
+
@policies[policy_label]
|
43
20
|
end
|
44
21
|
end
|
45
22
|
end
|
@@ -1,45 +1,104 @@
|
|
1
1
|
module Yarrow
|
2
2
|
module Content
|
3
3
|
class Policy
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
:extensions,
|
8
|
-
:match_path
|
9
|
-
)
|
4
|
+
DEFAULT_HOME_NESTING = false
|
5
|
+
|
6
|
+
DEFAULT_EXPANSION = :tree
|
10
7
|
|
11
8
|
DEFAULT_EXTENSIONS = [".md", ".yml", ".htm"]
|
12
9
|
|
13
10
|
DEFAULT_MATCH_PATH = "."
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
# Construct a content policy from the given source specification.
|
13
|
+
def self.from_spec(policy_label, policy_props, module_prefix="")
|
14
|
+
# TODO: validate length, structure etc
|
15
|
+
|
16
|
+
# If the spec holds a symbol value then treat it as a container => entity mapping
|
17
|
+
if policy_props.is_a?(Symbol)
|
18
|
+
new(policy_label, policy_props, DEFAULT_EXPANSION, DEFAULT_EXTENSIONS, DEFAULT_MATCH_PATH, module_prefix)
|
19
|
+
|
20
|
+
# Otherwise scan through all the props and fill in any gaps
|
21
|
+
else
|
22
|
+
# Use explicit container name if provided
|
23
|
+
container = if policy_props.key?(:container)
|
24
|
+
policy_props[:container]
|
25
|
+
else
|
26
|
+
# If an entity name is provided use its plural for the container name
|
27
|
+
if policy_props.key?(:entity)
|
28
|
+
Yarrow::Symbols.to_plural(policy_props[:entity])
|
29
|
+
else
|
30
|
+
Yarrow::Symbols.to_plural(label)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Use explicit entity name if provided
|
35
|
+
entity = if policy_props.key?(:entity)
|
36
|
+
policy_props[:entity]
|
37
|
+
else
|
38
|
+
if policy_props.key?(:container)
|
39
|
+
Yarrow::Symbols.to_singular(policy_props[:container])
|
40
|
+
else
|
41
|
+
Yarrow::Symbols.to_singular(label)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Set expansion strategy
|
46
|
+
expansion = if policy_props.key?(:expansion)
|
47
|
+
policy_props[:expansion]
|
48
|
+
else
|
49
|
+
DEFAULT_EXPANSION
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set list of extensions to turn into documents
|
53
|
+
extensions = if policy_props.key?(:extensions)
|
54
|
+
policy_props[:extensions]
|
55
|
+
else
|
56
|
+
DEFAULT_EXTENSIONS
|
57
|
+
end
|
18
58
|
|
19
|
-
|
20
|
-
|
21
|
-
|
59
|
+
# TODO: handle this in expansion strategies
|
60
|
+
match_path = DEFAULT_MATCH_PATH
|
61
|
+
|
62
|
+
# Construct the new policy
|
63
|
+
new(container, entity, expansion, extensions, match_path, module_prefix)
|
22
64
|
end
|
65
|
+
end
|
66
|
+
|
67
|
+
attr_reader :container, :entity, :expansion, :extensions, :match_path, :module
|
68
|
+
|
69
|
+
def initialize(container, entity, expansion, extensions, match_path, module_prefix)
|
70
|
+
@container = container
|
71
|
+
@entity = entity
|
72
|
+
@expansion = expansion
|
73
|
+
@extensions = extensions
|
74
|
+
@match_path = match_path
|
75
|
+
@module_prefix = module_prefix
|
76
|
+
end
|
77
|
+
|
78
|
+
def container_const
|
79
|
+
@container_const ||= Yarrow::Symbols.to_module_const([module_prefix, container])
|
80
|
+
end
|
23
81
|
|
24
|
-
|
82
|
+
def entity_const
|
83
|
+
@entity_const ||= Yarrow::Symbols.to_module_const([module_prefix, entity])
|
25
84
|
end
|
26
85
|
|
27
|
-
def
|
86
|
+
def _container
|
28
87
|
return @properties.container if @properties.container
|
29
88
|
Yarrow::Symbols.to_plural(@properties.entity)
|
30
89
|
end
|
31
90
|
|
32
|
-
def
|
91
|
+
def _entity
|
33
92
|
return @properties.entity if @properties.entity
|
34
93
|
Yarrow::Symbols.to_singular(@properties.container)
|
35
94
|
end
|
36
95
|
|
37
|
-
def
|
96
|
+
def _extensions
|
38
97
|
return @properties.extensions if @properties.extensions
|
39
98
|
DEFAULT_EXTENSIONS
|
40
99
|
end
|
41
100
|
|
42
|
-
def
|
101
|
+
def _match_path
|
43
102
|
return @properties.match_path if @properties.match_path
|
44
103
|
DEFAULT_MATCH_PATH
|
45
104
|
end
|
data/lib/yarrow/defaults.yml
CHANGED
@@ -5,11 +5,14 @@ output_dir: docs
|
|
5
5
|
meta:
|
6
6
|
title: Default Project
|
7
7
|
author: Default Name
|
8
|
+
content:
|
9
|
+
module: Yarrow::Content
|
10
|
+
source_map:
|
11
|
+
pages:
|
12
|
+
expansion: tree
|
8
13
|
output:
|
9
|
-
|
14
|
+
generator: web
|
10
15
|
template_dir: templates
|
11
|
-
object_map:
|
12
|
-
page: Yarrow::Model::Site.pages
|
13
16
|
assets:
|
14
17
|
input_dir: assets
|
15
18
|
output_dir: public/assets
|
data/lib/yarrow/generator.rb
CHANGED
@@ -13,8 +13,8 @@ module Yarrow
|
|
13
13
|
provides Content::Graph
|
14
14
|
|
15
15
|
def step(content)
|
16
|
-
|
17
|
-
|
16
|
+
model = Content::Model.new(content.config.content)
|
17
|
+
model.expand(content.graph)
|
18
18
|
content
|
19
19
|
end
|
20
20
|
end
|
@@ -4,9 +4,16 @@ module Yarrow
|
|
4
4
|
DEFINED_TYPES = {
|
5
5
|
string: Types::Instance.of(String),
|
6
6
|
integer: Types::Instance.of(Integer),
|
7
|
-
symbol: Types::Instance.of(Symbol),
|
7
|
+
symbol: Types::Instance.of(Symbol).accept(String, :to_sym),
|
8
8
|
path: Types::Instance.of(Pathname).accept(String),
|
9
|
-
any: Types::Any.new
|
9
|
+
any: Types::Any.new,
|
10
|
+
array: Types::List.of(Types::Any),
|
11
|
+
hash: Types::Instance.of(Hash)
|
12
|
+
}
|
13
|
+
|
14
|
+
TEMPLATE_TYPES = {
|
15
|
+
list: Types::List,
|
16
|
+
map: Types::Map
|
10
17
|
}
|
11
18
|
|
12
19
|
def self.register(identifier, type_class)
|
@@ -18,13 +25,43 @@ module Yarrow
|
|
18
25
|
end
|
19
26
|
|
20
27
|
def resolve_type(identifier)
|
21
|
-
#
|
28
|
+
# Type is directly resolvable from the definition table
|
29
|
+
return DEFINED_TYPES[identifier] if DEFINED_TYPES.key?(identifier)
|
22
30
|
|
23
|
-
|
24
|
-
|
25
|
-
|
31
|
+
if identifier.is_a?(Hash)
|
32
|
+
# If type identifier is a compound template extract its key and value mapping
|
33
|
+
key_id = identifier.keys.first
|
34
|
+
value_id = identifier.values.first
|
35
|
+
|
36
|
+
# Check if the given key is defined as a template type
|
37
|
+
unless TEMPLATE_TYPES.key?(key_id)
|
38
|
+
raise "compound type #{key_id} is not defined"
|
39
|
+
end
|
26
40
|
|
27
|
-
|
41
|
+
# Get reference to the type class we want to resolve
|
42
|
+
template_type = TEMPLATE_TYPES[key_id]
|
43
|
+
|
44
|
+
# Resolve the type to an instance depending on structure of its template args
|
45
|
+
resolved_type = if value_id.is_a?(Hash)
|
46
|
+
# Map template with two argument constructor
|
47
|
+
template_type.new(
|
48
|
+
resolve_type(value_id.keys.first),
|
49
|
+
resolve_type(value_id.values.first)
|
50
|
+
)
|
51
|
+
else
|
52
|
+
# Use the single arg constructor with the given unit type
|
53
|
+
template_type.of(resolve_type(value_id).unit)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Cache the resolved type for later reference
|
57
|
+
DEFINED_TYPES[identifier] = resolved_type
|
58
|
+
|
59
|
+
# Return the resolve type
|
60
|
+
resolved_type
|
61
|
+
else
|
62
|
+
# Not a compound template so we know it’s missing in the lookup table
|
63
|
+
raise "type #{identifier} is not defined"
|
64
|
+
end
|
28
65
|
end
|
29
66
|
end
|
30
67
|
end
|
data/lib/yarrow/schema/entity.rb
CHANGED
@@ -13,6 +13,19 @@ module Yarrow
|
|
13
13
|
def dictionary
|
14
14
|
@dictionary ||= Dictionary.new({})
|
15
15
|
end
|
16
|
+
|
17
|
+
def [](label)
|
18
|
+
@label = label
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def inherited(class_name)
|
23
|
+
if @label
|
24
|
+
class_type = Yarrow::Schema::Types::Instance.of(class_name)
|
25
|
+
Yarrow::Schema::Definitions.register(@label, class_type)
|
26
|
+
@label = nil
|
27
|
+
end
|
28
|
+
end
|
16
29
|
end
|
17
30
|
|
18
31
|
def initialize(config)
|
data/lib/yarrow/version.rb
CHANGED
data/lib/yarrow/web/document.rb
CHANGED
@@ -4,10 +4,10 @@ module Yarrow
|
|
4
4
|
# This class is somewhat verbose for simplicity and long-term maintainability
|
5
5
|
# (having a clear and easy to follow construction, rather than doing anything
|
6
6
|
# too clever which has burned this lib in the past).
|
7
|
-
def initialize(item, parent,
|
7
|
+
def initialize(item, parent, is_index)
|
8
8
|
@item = item
|
9
9
|
@parent = parent
|
10
|
-
@
|
10
|
+
@is_index = is_index
|
11
11
|
end
|
12
12
|
|
13
13
|
def name
|
@@ -23,13 +23,18 @@ module Yarrow
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def url
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
if @parent.nil?
|
27
|
+
"/"
|
28
|
+
else
|
29
|
+
segments = [@item.props[:name]]
|
30
|
+
current = @parent
|
31
|
+
|
32
|
+
until current.in(:collection).first.nil? do
|
33
|
+
segments << current.props[:name]
|
34
|
+
current = current.in(:collection).first
|
32
35
|
end
|
36
|
+
|
37
|
+
"/" + segments.reverse.join("/")
|
33
38
|
end
|
34
39
|
end
|
35
40
|
end
|
data/lib/yarrow/web/manifest.rb
CHANGED
@@ -6,20 +6,29 @@ module Yarrow
|
|
6
6
|
|
7
7
|
graph.n(:collection).each do |collection|
|
8
8
|
# TODO: raise error if both content_only and index_only are set
|
9
|
-
|
10
|
-
# If the collection is tagged :content_only then skip top level listing/index
|
11
|
-
unless collection.props[:content_only]
|
12
|
-
manifest.add_document(collection_context(collection))
|
13
|
-
end
|
9
|
+
index = nil
|
14
10
|
|
15
11
|
# If the collection is tagged :index_only then skip adding individual documents
|
16
12
|
unless collection.props[:index_only]
|
17
13
|
collection.out(:item).each do |item|
|
18
14
|
#if item[:entity].status.to_sym == :published
|
19
|
-
|
15
|
+
if item.props[:name] == "index"
|
16
|
+
index = item
|
17
|
+
else
|
18
|
+
manifest.add_document(item_context(item))
|
19
|
+
end
|
20
20
|
#end
|
21
21
|
end
|
22
22
|
end
|
23
|
+
|
24
|
+
# If the collection is tagged :content_only then skip top level listing/index
|
25
|
+
unless collection.props[:content_only]
|
26
|
+
if index
|
27
|
+
manifest.add_document(collection_index_context(collection, index))
|
28
|
+
else
|
29
|
+
manifest.add_document(collection_context(collection))
|
30
|
+
end
|
31
|
+
end
|
23
32
|
end
|
24
33
|
|
25
34
|
manifest
|
@@ -32,28 +41,6 @@ module Yarrow
|
|
32
41
|
@assets = []
|
33
42
|
end
|
34
43
|
|
35
|
-
def self.collection_context(collection)
|
36
|
-
# Yarrow::Output::Context.new(
|
37
|
-
# parent: collection.in(:collection).first,
|
38
|
-
# name: collection.props[:name],
|
39
|
-
# #url: collection.props[:url],
|
40
|
-
# title: collection.props[:title],
|
41
|
-
# type: collection.props[:type]
|
42
|
-
# )
|
43
|
-
Document.new(collection, collection.in(:collection).first, :mirror_source)
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.item_context(item)
|
47
|
-
# Yarrow::Output::Context.new(
|
48
|
-
# parent: item.in(:collection).first,
|
49
|
-
# name: item.props[:name],
|
50
|
-
# #url: item.props[:url],
|
51
|
-
# title: item.props[:title],
|
52
|
-
# type: item.props[:type]
|
53
|
-
# )
|
54
|
-
Document.new(item, item.in(:collection).first, :mirror_source)
|
55
|
-
end
|
56
|
-
|
57
44
|
def add_document(document)
|
58
45
|
@documents << document
|
59
46
|
end
|
@@ -61,6 +48,18 @@ module Yarrow
|
|
61
48
|
def add_asset(asset)
|
62
49
|
@assets << asset
|
63
50
|
end
|
51
|
+
|
52
|
+
def self.collection_context(collection)
|
53
|
+
Document.new(collection, collection.in(:collection).first, true)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.collection_index_context(collection, item)
|
57
|
+
Document.new(item, collection.in(:collection).first, false)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.item_context(item)
|
61
|
+
Document.new(item, item.in(:collection).first, false)
|
62
|
+
end
|
64
63
|
end
|
65
64
|
end
|
66
65
|
end
|
data/lib/yarrow.rb
CHANGED
@@ -14,19 +14,15 @@ require "yarrow/schema/dictionary"
|
|
14
14
|
require "yarrow/schema/entity"
|
15
15
|
require "yarrow/schema/value"
|
16
16
|
require "yarrow/schema/registry"
|
17
|
-
require "yarrow/config"
|
18
|
-
require "yarrow/configuration"
|
19
|
-
require "yarrow/console_runner"
|
20
17
|
require "yarrow/tools/front_matter"
|
21
18
|
require "yarrow/tools/content_utils"
|
22
19
|
require "yarrow/content/graph"
|
20
|
+
require "yarrow/content/policy"
|
21
|
+
require "yarrow/content/model"
|
23
22
|
require "yarrow/content/source"
|
24
|
-
require "yarrow/content/expansion"
|
25
|
-
require "yarrow/content/
|
26
|
-
require "yarrow/content/tree_expansion"
|
23
|
+
require "yarrow/content/expansion/strategy"
|
24
|
+
require "yarrow/content/expansion/tree"
|
27
25
|
require "yarrow/content/resource"
|
28
|
-
require "yarrow/content/model"
|
29
|
-
require "yarrow/content/policy"
|
30
26
|
require "yarrow/web/manifest"
|
31
27
|
require "yarrow/web/document"
|
32
28
|
require "yarrow/web/generator"
|
@@ -36,12 +32,14 @@ require "yarrow/output/web/indexed_file"
|
|
36
32
|
require "yarrow/content_map"
|
37
33
|
require "yarrow/server"
|
38
34
|
require "yarrow/server/livereload"
|
39
|
-
|
40
35
|
require "yarrow/process/workflow"
|
41
36
|
require "yarrow/process/step_processor"
|
42
37
|
require "yarrow/process/expand_content"
|
43
38
|
require "yarrow/process/extract_source"
|
44
39
|
require "yarrow/process/project_manifest"
|
40
|
+
require "yarrow/config"
|
41
|
+
require "yarrow/configuration"
|
42
|
+
require "yarrow/console_runner"
|
45
43
|
|
46
44
|
require "yarrow/generator"
|
47
45
|
|
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.2
|
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-
|
11
|
+
date: 2022-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -260,14 +260,13 @@ files:
|
|
260
260
|
- lib/yarrow/config.rb
|
261
261
|
- lib/yarrow/configuration.rb
|
262
262
|
- lib/yarrow/console_runner.rb
|
263
|
-
- lib/yarrow/content/expansion.rb
|
264
|
-
- lib/yarrow/content/
|
263
|
+
- lib/yarrow/content/expansion/strategy.rb
|
264
|
+
- lib/yarrow/content/expansion/tree.rb
|
265
265
|
- lib/yarrow/content/graph.rb
|
266
266
|
- lib/yarrow/content/model.rb
|
267
267
|
- lib/yarrow/content/policy.rb
|
268
268
|
- lib/yarrow/content/resource.rb
|
269
269
|
- lib/yarrow/content/source.rb
|
270
|
-
- lib/yarrow/content/tree_expansion.rb
|
271
270
|
- lib/yarrow/content_map.rb
|
272
271
|
- lib/yarrow/defaults.yml
|
273
272
|
- lib/yarrow/generator.rb
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module Yarrow
|
2
|
-
module Content
|
3
|
-
class Expansion
|
4
|
-
def initialize(model)
|
5
|
-
@model = model
|
6
|
-
end
|
7
|
-
|
8
|
-
def expand(graph)
|
9
|
-
@model.each_policy do |policy|
|
10
|
-
strategy = TreeExpansion.new(graph)
|
11
|
-
strategy.expand(policy)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
module Yarrow
|
2
|
-
module Content
|
3
|
-
class ExpansionStrategy
|
4
|
-
include Yarrow::Tools::FrontMatter
|
5
|
-
|
6
|
-
attr_reader :graph
|
7
|
-
|
8
|
-
def initialize(graph)
|
9
|
-
@graph = graph
|
10
|
-
end
|
11
|
-
|
12
|
-
# Extract collection level configuration/metadata from the root node for
|
13
|
-
# this content type.
|
14
|
-
def extract_metadata(node, type)
|
15
|
-
# TODO: support _index or _slug convention as well
|
16
|
-
meta_file = node.out(slug: type.to_s).first
|
17
|
-
|
18
|
-
if meta_file
|
19
|
-
# Process metadata and add it to the collection node
|
20
|
-
# TODO: pass in content converter object
|
21
|
-
# TODO: index/body content by default if extracted from frontmatter
|
22
|
-
body, data = process_content(meta_file.props[:entry])
|
23
|
-
else
|
24
|
-
# Otherwise, assume default collection behaviour
|
25
|
-
data = {}
|
26
|
-
end
|
27
|
-
|
28
|
-
# Generate a default title if not provided in metadata
|
29
|
-
unless data.key?(:title)
|
30
|
-
data[:title] = type.to_s.capitalize
|
31
|
-
end
|
32
|
-
|
33
|
-
data
|
34
|
-
end
|
35
|
-
|
36
|
-
# Workaround for handling meta and content source in multiple files or a single
|
37
|
-
# file with front matter.
|
38
|
-
def process_content(path)
|
39
|
-
case path.extname
|
40
|
-
when '.htm', '.md'
|
41
|
-
read_split_content(path.to_s, symbolize_keys: true)
|
42
|
-
# when '.md'
|
43
|
-
# body, data = read_split_content(path.to_s, symbolize_keys: true)
|
44
|
-
# [Kramdown::Document.new(body).to_html, data]
|
45
|
-
when '.yml'
|
46
|
-
[nil, YAML.load(File.read(path.to_s), symbolize_names: true)]
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
module Yarrow
|
2
|
-
module Content
|
3
|
-
class TreeExpansion < ExpansionStrategy
|
4
|
-
def expand(policy)
|
5
|
-
#p graph.n(:root).out(:directory).to_a.count
|
6
|
-
#policy.match()
|
7
|
-
|
8
|
-
#p graph.n(:root).out(:directory).first.props[:name]
|
9
|
-
|
10
|
-
expand_impl(policy)
|
11
|
-
end
|
12
|
-
|
13
|
-
def expand_impl(policy)
|
14
|
-
type = policy.container
|
15
|
-
|
16
|
-
# If match path represents entire content dir, then include the entire
|
17
|
-
# content dir instead of scanning from a subfolder matching the name of
|
18
|
-
# the collection.
|
19
|
-
#start_node = if policy.match_path == "."
|
20
|
-
start_node = if true
|
21
|
-
graph.n(:root)
|
22
|
-
else
|
23
|
-
graph.n(:root).out(name: type.to_s)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Extract metadata from given start node
|
27
|
-
collection_metadata = extract_metadata(start_node, type)
|
28
|
-
|
29
|
-
# Collect all nested collections in the subgraph for this content type
|
30
|
-
subcollections = {}
|
31
|
-
item_links = []
|
32
|
-
index = nil
|
33
|
-
|
34
|
-
# Scan and collect all nested files from the root
|
35
|
-
start_node.depth_first.each do |node|
|
36
|
-
if node.label == :directory
|
37
|
-
# Create a collection node representing a collection of documents
|
38
|
-
index = graph.create_node do |collection_node|
|
39
|
-
collection_node.label = :collection
|
40
|
-
collection_node.props[:type] = type
|
41
|
-
collection_node.props[:name] = node.props[:name]
|
42
|
-
|
43
|
-
# TODO: title needs to be defined from metadata
|
44
|
-
collection_node.props[:title] = node.props[:name].capitalize
|
45
|
-
end
|
46
|
-
|
47
|
-
# Add this collection id to the lookup table for edge construction
|
48
|
-
subcollections[node.props[:path]] = index
|
49
|
-
|
50
|
-
# Join the collection to its parent
|
51
|
-
unless node.props[:slug] == type.to_s || !subcollections.key?(node.props[:entry].parent.to_s)
|
52
|
-
graph.create_edge do |edge|
|
53
|
-
edge.label = :child
|
54
|
-
edge.from = subcollections[node.props[:entry].parent.to_s].id
|
55
|
-
edge.to = index.id
|
56
|
-
end
|
57
|
-
end
|
58
|
-
elsif node.label == :file
|
59
|
-
body, meta = process_content(node.props[:entry])
|
60
|
-
|
61
|
-
# Create an item node representing a file mapped to a unique content object
|
62
|
-
item = graph.create_node do |item_node|
|
63
|
-
item_node.label = :item
|
64
|
-
item_node.props[:type] = policy.record
|
65
|
-
item_node.props[:name] = node.props[:entry].basename(node.props[:entry].extname).to_s
|
66
|
-
item_node.props[:body] = body if body
|
67
|
-
item_node.props[:title] = meta[:title] if meta
|
68
|
-
# TODO: better handling of metadata on node props
|
69
|
-
end
|
70
|
-
|
71
|
-
# We may not have an expanded node for the parent collection if this is a
|
72
|
-
# preorder traversal so save it for later
|
73
|
-
item_links << {
|
74
|
-
parent_path: node.props[:entry].parent.to_s,
|
75
|
-
item_id: item.id
|
76
|
-
}
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# Once all files and directories have been expanded, connect all the child
|
81
|
-
# edges between collections and items
|
82
|
-
item_links.each do |item_link|
|
83
|
-
graph.create_edge do |edge|
|
84
|
-
edge.label = :child
|
85
|
-
edge.from = subcollections[item_link[:parent_path]].id
|
86
|
-
edge.to = item_link[:item_id]
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|