yarrow 0.8.1 → 0.8.3

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: 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