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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c362ba73bcbfc325bad91668bf1a1a006dd6355579a36ce96cc60323944804b
4
- data.tar.gz: 51db819b1f52b3b75a9b39287359e22dfb6d52833ce0089636694f4d9feeda15
3
+ metadata.gz: 4ea37c60e3c8bfe51e6ceb9425f0d0c81b93757a681c7987f7852c3335644824
4
+ data.tar.gz: 19cd090d3b3348f633f5d303d8b826fd2b8c054b790136355a41d9b4f0f098c4
5
5
  SHA512:
6
- metadata.gz: c7a41bb6ee0a6bcfd27c83d7780cc70f7b41131daaa4decd02e591346f9270318951b1a68d0f390ac81843593e0bf97a8062da51f3f7f1e2be78b6711af132d6
7
- data.tar.gz: 866d6683eb25f9154d9582266b45016b680d622fca57a71c88b55f8c0ee41dc76076e839684026df8759e72ba73cf2597b96cbe05b83d3c877cc5ebcf6252bcf
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
- #:docroot,
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
@@ -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
@@ -17,11 +17,6 @@ module Yarrow
17
17
  @config = config
18
18
  end
19
19
 
20
- def expand_pages
21
- expander = Yarrow::Content::Expansion.new(Yarrow::Content::Model.new)
22
- expander.expand(graph)
23
- end
24
-
25
20
  # List of source files.
26
21
  def files
27
22
  graph.nodes(:file)
@@ -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(spec=nil, namespace=nil)
19
- @namespace = []
20
- @namespace << namespace unless namespace.nil?
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 each_policy(&block)
38
- @policies.each_value(&block)
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(policy_key)
42
- @policies[policy_key]
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
- Options = Yarrow::Schema::Value.new(
5
- :container,
6
- :entity,
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
- def self.from_name(name)
16
- new(Options.new(container: name.to_sym))
17
- end
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
- def initialize(properties)
20
- unless properties.respond_to?(:container) || properties.respond_to?(:entity)
21
- raise "Must provide a container name or entity name"
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
- @properties = properties
82
+ def entity_const
83
+ @entity_const ||= Yarrow::Symbols.to_module_const([module_prefix, entity])
25
84
  end
26
85
 
27
- def container
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 entity
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 extensions
96
+ def _extensions
38
97
  return @properties.extensions if @properties.extensions
39
98
  DEFAULT_EXTENSIONS
40
99
  end
41
100
 
42
- def match_path
101
+ def _match_path
43
102
  return @properties.match_path if @properties.match_path
44
103
  DEFAULT_MATCH_PATH
45
104
  end
@@ -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
- target_dir: public
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
@@ -13,8 +13,8 @@ module Yarrow
13
13
  provides Content::Graph
14
14
 
15
15
  def step(content)
16
- expander = Content::Expansion.new(Yarrow::Content::Model.new)
17
- expander.expand(content.graph)
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
- #return identifier unless identifier.is_a?(Symbol)
28
+ # Type is directly resolvable from the definition table
29
+ return DEFINED_TYPES[identifier] if DEFINED_TYPES.key?(identifier)
22
30
 
23
- unless DEFINED_TYPES.key?(identifier)
24
- raise "#{identifier} is not defined"
25
- end
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
- return DEFINED_TYPES[identifier]
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
@@ -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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module Yarrow
3
3
  APP_NAME = "Yarrow"
4
- VERSION = "0.8.0"
4
+ VERSION = "0.8.2"
5
5
  end
@@ -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, url_strategy)
7
+ def initialize(item, parent, is_index)
8
8
  @item = item
9
9
  @parent = parent
10
- @url_strategy = url_strategy
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
- case @url_strategy
27
- when :mirror_source
28
- unless @parent.nil?
29
- "/#{@parent.props[:name]}/#{name}"
30
- else
31
- "/#{name}/"
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
@@ -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
- manifest.add_document(item_context(item))
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/expansion_strategy"
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.0
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-01 00:00:00.000000000 Z
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/expansion_strategy.rb
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