yarrow 0.8.1 → 0.8.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d26941f2b464c551d825b73999b0365637b9e4feec159ee74de51a1abca3416e
4
- data.tar.gz: 34081172c945cf83e97ab3ee03484436ba380de6c809c2dd87caaa50f1bba315
3
+ metadata.gz: b4319be245b1b70240d28422cf095d64bb61bdacf1b95c545cd055ed7c9e645b
4
+ data.tar.gz: 1c0fc21f3536a09dd5c11a1c359ce11ab128ea9c527f002a6e6a777090848173
5
5
  SHA512:
6
- metadata.gz: 6300e2c0f83c741862cf6c49acc42118636a3832401706057b483d942c07a947ea1124b2c10e056cc5052e25a28a59f896216d0156911026639d16e0faa5ba73
7
- data.tar.gz: 0b897183df8712cbc08d0a479ed1e1d129a90305ad76f9bc802ea262707026ff5ffa57890d0d26d49955f55721ffd0887510671c86430e4780564266758ebaed
6
+ metadata.gz: 0c3d3176870c6bc82695a825bfa7dc46908311200a35722142620e2fdfd5ce60ea795ef03b88ee35c206379e9f382bab41e0cd38f96d5cc261ba0578d7fe51ed
7
+ data.tar.gz: 3d8b4f4acb48ef94dc75b65ebda43c5a0d0e588679688b090a6ebaba6c5bccc7f7610793d60663a2e7c5cdd6fcdc213be989f064ac8d9092f6dfd191a0b38b90
data/lib/yarrow/config.rb CHANGED
@@ -40,7 +40,16 @@ module Yarrow
40
40
  :middleware
41
41
  )
42
42
 
43
- class Output < Yarrow::Schema::Entity[:output]
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]
44
53
  attribute :generator, :string
45
54
  attribute :template_dir, :path
46
55
  #attribute :scripts, :array
@@ -52,7 +61,8 @@ module Yarrow
52
61
  attribute :output_dir, :path
53
62
  attribute :meta, :any
54
63
  attribute :server, :any
55
- #attribute :output, :output
64
+ attribute :content, :__config_content
65
+ attribute :output, :__config_output
56
66
  end
57
67
  #
58
68
  # `content_dir` and `output_dir` are placeholders and should be overriden
@@ -48,6 +48,23 @@ 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
+
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
+
51
68
  # TODO: messy hack to get rid of Hashie::Mash, this should either be
52
69
  # automated as part of the schema types or a default value should be
53
70
  # generated here (eg: `"#{Dir.pwd}/docs"`)
@@ -58,7 +75,9 @@ module Yarrow
58
75
  output_dir: Pathname.new(File.expand_path(out_dir_or_string)),
59
76
  source_dir: Pathname.new(File.expand_path(source_dir_or_string)),
60
77
  meta: meta_obj,
61
- server: server_obj
78
+ server: server_obj,
79
+ content: content_obj,
80
+ output: output_obj
62
81
  )
63
82
  end
64
83
  end
@@ -0,0 +1,54 @@
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
+ # TODO: Raise error if unsupported extname reaches here
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,93 @@
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
+
67
+ # TODO: new schema edits go here
68
+ #puts policy.entity_const
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
93
+ 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,26 @@
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(
8
+ policy_label,
9
+ policy_spec,
10
+ content_config.module
11
+ )
34
12
  end
35
13
  end
36
14
 
37
- def each_policy(&block)
38
- @policies.each_value(&block)
15
+ def expand(graph)
16
+ @policies.each_value do |policy|
17
+ strategy = Expansion::Tree.new(graph)
18
+ strategy.expand(policy)
19
+ end
39
20
  end
40
21
 
41
- def policy_for(policy_key)
42
- @policies[policy_key]
22
+ def policy_for(policy_label)
23
+ @policies[policy_label]
43
24
  end
44
25
  end
45
26
  end
@@ -1,49 +1,88 @@
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
- )
10
-
11
4
  DEFAULT_HOME_NESTING = false
12
5
 
6
+ DEFAULT_EXPANSION = :tree
7
+
13
8
  DEFAULT_EXTENSIONS = [".md", ".yml", ".htm"]
14
9
 
15
10
  DEFAULT_MATCH_PATH = "."
16
11
 
17
- def self.from_name(name)
18
- new(Options.new(container: name.to_sym))
19
- end
12
+ MODULE_SEPARATOR = "::"
20
13
 
21
- def initialize(properties)
22
- unless properties.respond_to?(:container) || properties.respond_to?(:entity)
23
- raise "Must provide a container name or entity name"
24
- end
14
+ # Construct a content policy from the given source specification.
15
+ def self.from_spec(policy_label, policy_props, module_prefix="")
16
+ # TODO: validate length, structure etc
25
17
 
26
- @properties = properties
27
- end
18
+ # If the spec holds a symbol value then treat it as a container => entity mapping
19
+ if policy_props.is_a?(Symbol)
20
+ new(policy_label, policy_props, DEFAULT_EXPANSION, DEFAULT_EXTENSIONS, DEFAULT_MATCH_PATH, module_prefix)
21
+
22
+ # Otherwise scan through all the props and fill in any gaps
23
+ else
24
+ # Use explicit container name if provided
25
+ container = if policy_props.key?(:container)
26
+ policy_props[:container]
27
+ else
28
+ # If an entity name is provided use its plural for the container name
29
+ if policy_props.key?(:entity)
30
+ Yarrow::Symbols.to_plural(policy_props[:entity])
31
+ else
32
+ Yarrow::Symbols.to_plural(policy_label)
33
+ end
34
+ end
28
35
 
29
- def container
30
- return @properties.container if @properties.container
31
- Yarrow::Symbols.to_plural(@properties.entity)
36
+ # Use explicit entity name if provided
37
+ entity = if policy_props.key?(:entity)
38
+ policy_props[:entity]
39
+ else
40
+ if policy_props.key?(:container)
41
+ Yarrow::Symbols.to_singular(policy_props[:container])
42
+ else
43
+ Yarrow::Symbols.to_singular(policy_label)
44
+ end
45
+ end
46
+
47
+ # Set expansion strategy
48
+ expansion = if policy_props.key?(:expansion)
49
+ policy_props[:expansion]
50
+ else
51
+ DEFAULT_EXPANSION
52
+ end
53
+
54
+ # Set list of extensions to turn into documents
55
+ extensions = if policy_props.key?(:extensions)
56
+ policy_props[:extensions]
57
+ else
58
+ DEFAULT_EXTENSIONS
59
+ end
60
+
61
+ # TODO: handle this in expansion strategies
62
+ match_path = DEFAULT_MATCH_PATH
63
+
64
+ # Construct the new policy
65
+ new(container, entity, expansion, extensions, match_path, module_prefix)
66
+ end
32
67
  end
33
68
 
34
- def entity
35
- return @properties.entity if @properties.entity
36
- Yarrow::Symbols.to_singular(@properties.container)
69
+ attr_reader :container, :entity, :expansion, :extensions, :match_path, :module_prefix
70
+
71
+ def initialize(container, entity, expansion, extensions, match_path, module_prefix)
72
+ @container = container
73
+ @entity = entity
74
+ @expansion = expansion
75
+ @extensions = extensions
76
+ @match_path = match_path
77
+ @module_prefix = module_prefix.split(MODULE_SEPARATOR)
37
78
  end
38
79
 
39
- def extensions
40
- return @properties.extensions if @properties.extensions
41
- DEFAULT_EXTENSIONS
80
+ def container_const
81
+ @container_const ||= Yarrow::Symbols.to_module_const([*module_prefix, container])
42
82
  end
43
83
 
44
- def match_path
45
- return @properties.match_path if @properties.match_path
46
- DEFAULT_MATCH_PATH
84
+ def entity_const
85
+ @entity_const ||= Yarrow::Symbols.to_module_const([*module_prefix, entity])
47
86
  end
48
87
  end
49
88
  end
@@ -5,6 +5,11 @@ 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
@@ -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
@@ -8,7 +8,7 @@ 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::Map.of(Symbol => Types::Any)
11
+ hash: Types::Instance.of(Hash)
12
12
  }
13
13
 
14
14
  TEMPLATE_TYPES = {
@@ -40,7 +40,7 @@ module Yarrow
40
40
 
41
41
  # Get reference to the type class we want to resolve
42
42
  template_type = TEMPLATE_TYPES[key_id]
43
-
43
+
44
44
  # Resolve the type to an instance depending on structure of its template args
45
45
  resolved_type = if value_id.is_a?(Hash)
46
46
  # Map template with two argument constructor
@@ -55,7 +55,7 @@ module Yarrow
55
55
 
56
56
  # Cache the resolved type for later reference
57
57
  DEFINED_TYPES[identifier] = resolved_type
58
-
58
+
59
59
  # Return the resolve type
60
60
  resolved_type
61
61
  else
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module Yarrow
3
3
  APP_NAME = "Yarrow"
4
- VERSION = "0.8.1"
4
+ VERSION = "0.8.3"
5
5
  end
@@ -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)
@@ -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 = "./spec/fixtures/templates/doctest"
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
@@ -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.1
4
+ version: 0.8.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Rickerby
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-04 00:00:00.000000000 Z
11
+ date: 2022-11-07 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,88 +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
- type = policy.container
10
-
11
- # If match path represents entire content dir, then include the entire
12
- # content dir instead of scanning from a subfolder matching the name of
13
- # the collection.
14
- #start_node = if policy.match_path == "."
15
- start_node = if true
16
- # TODO: match against source_dir
17
- graph.n(:root).out(:directory)
18
- else
19
- graph.n(:root).out(name: policy.container.to_s)
20
- end
21
-
22
- # Extract metadata from given start node
23
- collection_metadata = extract_metadata(start_node, policy.container)
24
-
25
- # Collect all nested collections in the subgraph for this content type
26
- subcollections = {}
27
- item_links = []
28
- index = nil
29
-
30
- # Scan and collect all nested files from the root
31
- start_node.depth_first.each do |node|
32
- if node.label == :directory
33
- # Create a collection node representing a collection of documents
34
- index = graph.create_node do |collection_node|
35
- collection_node.label = :collection
36
- collection_node.props[:type] = policy.container
37
- collection_node.props[:name] = node.props[:name]
38
-
39
- # TODO: title needs to be defined from metadata
40
- collection_node.props[:title] = node.props[:name].capitalize
41
- end
42
-
43
- # Add this collection id to the lookup table for edge construction
44
- subcollections[node.props[:path]] = index
45
-
46
- # Join the collection to its parent
47
- unless node.props[:slug] == type.to_s || !subcollections.key?(node.props[:entry].parent.to_s)
48
- graph.create_edge do |edge|
49
- edge.label = :child
50
- edge.from = subcollections[node.props[:entry].parent.to_s].id
51
- edge.to = index.id
52
- end
53
- end
54
- elsif node.label == :file
55
- body, meta = process_content(node.props[:entry])
56
-
57
- # Create an item node representing a file mapped to a unique content object
58
- item = graph.create_node do |item_node|
59
- item_node.label = :item
60
- item_node.props[:type] = policy.record
61
- item_node.props[:name] = node.props[:entry].basename(node.props[:entry].extname).to_s
62
- item_node.props[:body] = body if body
63
- item_node.props[:title] = meta[:title] if meta
64
- # TODO: better handling of metadata on node props
65
- end
66
-
67
- # We may not have an expanded node for the parent collection if this is a
68
- # preorder traversal so save it for later
69
- item_links << {
70
- parent_path: node.props[:entry].parent.to_s,
71
- item_id: item.id
72
- }
73
- end
74
- end
75
-
76
- # Once all files and directories have been expanded, connect all the child
77
- # edges between collections and items
78
- item_links.each do |item_link|
79
- graph.create_edge do |edge|
80
- edge.label = :child
81
- edge.from = subcollections[item_link[:parent_path]].id
82
- edge.to = item_link[:item_id]
83
- end
84
- end
85
- end
86
- end
87
- end
88
- end