yarrow 0.8.0 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|