yarrow 0.8.3 → 0.8.6

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: b4319be245b1b70240d28422cf095d64bb61bdacf1b95c545cd055ed7c9e645b
4
- data.tar.gz: 1c0fc21f3536a09dd5c11a1c359ce11ab128ea9c527f002a6e6a777090848173
3
+ metadata.gz: cb8358e221f56cb48075609b4ae35e07ef7ea1bef577d6a1f929a8eb26ca4242
4
+ data.tar.gz: 8730eec22402eb73b1555a7421fd6ea5f22edb049d8dc7083c0b4de1ea777681
5
5
  SHA512:
6
- metadata.gz: 0c3d3176870c6bc82695a825bfa7dc46908311200a35722142620e2fdfd5ce60ea795ef03b88ee35c206379e9f382bab41e0cd38f96d5cc261ba0578d7fe51ed
7
- data.tar.gz: 3d8b4f4acb48ef94dc75b65ebda43c5a0d0e588679688b090a6ebaba6c5bccc7f7610793d60663a2e7c5cdd6fcdc213be989f064ac8d9092f6dfd191a0b38b90
6
+ metadata.gz: 1571c747f38efd25382c758b9974c1e5b644f541aa93f76f9f8d3082c35db13b30cf39da52487e67ad2611feb73fc24ad39acdf7de57393aaa8e16cebe8dc0ea
7
+ data.tar.gz: aad027b60c53a918b9b96f800e144b8b981f3e083a211524ce329549b8242cbd9100da756b7130adcfac990b7944d557378de963b62042cf2effba12acade81e
@@ -6,6 +6,22 @@ module Mementus
6
6
  # Monkeypatch extension to ensure each pipeline step supports enumerable
7
7
  # methods. Mostly used for #map. API needs to be fixed in the gem itself.
8
8
  include Enumerable
9
+
10
+ def to
11
+ Step.new(map { |edge| edge.to }, Pipe.new(graph), graph)
12
+ end
13
+
14
+ def props
15
+ Step.new(map { |node| node.props }, Pipe.new(graph), graph)
16
+ end
17
+
18
+ # def props
19
+ # node_props = source.inject([]) do |result, node|
20
+ # result.concat(node.props)
21
+ # end
22
+
23
+ # Step.new(node_props, Pipe.new(graph), graph)
24
+ # end
9
25
  end
10
26
  end
11
27
  module Structure
@@ -21,4 +37,11 @@ module Mementus
21
37
  "nodes_count=#{nodes_count} edges_count=#{edges_count}>"
22
38
  end
23
39
  end
40
+
41
+ class Node
42
+ def merge_props(data)
43
+ next_props = props.merge(data)
44
+ @props = next_props.freeze
45
+ end
46
+ end
24
47
  end
@@ -34,6 +34,23 @@ module Yarrow
34
34
  data
35
35
  end
36
36
 
37
+ def populate_collection(node, policy, meta_attrs)
38
+ node.label = :collection
39
+ node.props[:type] = policy.collection
40
+ node.props[:resource] = policy.collection_const.new(meta_attrs)
41
+ end
42
+
43
+ def populate_entity(node, policy, meta_attrs)
44
+ node.label = :item
45
+ node.props[:type] = policy.entity
46
+ node.props[:resource] = policy.entity_const.new(meta_attrs)
47
+ end
48
+
49
+ def merge_collection_index(node, policy, meta_attrs)
50
+ props = { resource: node.props[:resource].merge(meta_attrs) }
51
+ node.merge_props(props)
52
+ end
53
+
37
54
  # Workaround for handling meta and content source in multiple files or a single
38
55
  # file with front matter.
39
56
  def process_content(path)
@@ -21,11 +21,12 @@ module Yarrow
21
21
  end
22
22
 
23
23
  # Extract metadata from given start node
24
- collection_metadata = extract_metadata(start_node, policy.container)
24
+ #collection_metadata = extract_metadata(start_node, policy.container)
25
25
 
26
26
  # Collect all nested collections in the subgraph for this content type
27
27
  subcollections = {}
28
- item_links = []
28
+ entity_links = []
29
+ index_links = []
29
30
  index = nil
30
31
 
31
32
  # Scan and collect all nested files from the root
@@ -33,12 +34,14 @@ module Yarrow
33
34
  if node.label == :directory
34
35
  # Create a collection node representing a collection of documents
35
36
  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
37
 
40
- # TODO: title needs to be defined from metadata
41
- collection_node.props[:title] = node.props[:name].capitalize
38
+ collection_attrs = {
39
+ name: node.props[:name],
40
+ title: node.props[:name].capitalize,
41
+ body: ""
42
+ }
43
+
44
+ populate_collection(collection_node, policy, collection_attrs)
42
45
  end
43
46
 
44
47
  # Add this collection id to the lookup table for edge construction
@@ -54,38 +57,55 @@ module Yarrow
54
57
  end
55
58
  elsif node.label == :file
56
59
  body, meta = process_content(node.props[:entry])
60
+ meta = {} if !meta
57
61
 
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
62
+ # TODO: document mapping convention for index pages and collection metadata
63
+ # TODO: underscore _index pattern?
64
+ bare_basename = node.props[:entry].basename(node.props[:entry].extname)
65
+ if bare_basename.to_s == "index"
66
+ index_links << {
67
+ parent_id: subcollections[node.props[:entry].parent.to_s],
68
+ index_attrs: meta.merge({ body: body})
69
+ }
70
+ else
71
+ # Create an entity node representing a file mapped to a unique content object
72
+ entity = graph.create_node do |entity_node|
73
+
74
+ entity_slug = node.props[:entry].basename(node.props[:entry].extname).to_s
70
75
 
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
- }
76
+ entity_attrs = {
77
+ name: entity_slug,
78
+ title: entity_slug.gsub("-", " ").capitalize,
79
+ body: body
80
+ }
81
+
82
+ populate_entity(entity_node, policy, entity_attrs.merge(meta || {}))
83
+ end
84
+
85
+ # We may not have an expanded node for the parent collection if this is a
86
+ # preorder traversal so save it for later
87
+ entity_links << {
88
+ parent_id: subcollections[node.props[:entry].parent.to_s],
89
+ child_id: entity
90
+ }
91
+ end
77
92
  end
78
93
  end
79
94
 
80
95
  # 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|
96
+ # edges between collections and entities
97
+ entity_links.each do |entity_link|
83
98
  graph.create_edge do |edge|
84
99
  edge.label = :child
85
- edge.from = subcollections[item_link[:parent_path]].id
86
- edge.to = item_link[:item_id]
100
+ edge.from = entity_link[:parent_id].id
101
+ edge.to = entity_link[:child_id].id
87
102
  end
88
103
  end
104
+
105
+ # Merge index page body and metadata with their parent collections
106
+ index_links.each do |index_link|
107
+ merge_collection_index(index_link[:parent_id], policy, index_link[:index_attrs])
108
+ end
89
109
  end
90
110
  end
91
111
  end
@@ -81,6 +81,9 @@ module Yarrow
81
81
  @container_const ||= Yarrow::Symbols.to_module_const([*module_prefix, container])
82
82
  end
83
83
 
84
+ alias_method :collection, :container
85
+ alias_method :collection_const, :container_const
86
+
84
87
  def entity_const
85
88
  @entity_const ||= Yarrow::Symbols.to_module_const([*module_prefix, entity])
86
89
  end
@@ -58,7 +58,7 @@ module Yarrow
58
58
  end
59
59
  end
60
60
 
61
- private
61
+ #private
62
62
 
63
63
  attr_reader :config, :workflow
64
64
 
@@ -8,7 +8,8 @@ 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::Instance.of(Hash)
11
+ hash: Types::Instance.of(Hash),
12
+ markdown: Types::Instance.of(Kramdown::Document).accept(String)
12
13
  }
13
14
 
14
15
  TEMPLATE_TYPES = {
@@ -28,13 +28,13 @@ module Yarrow
28
28
 
29
29
  if missing_attrs.any?
30
30
  missing_attrs.each do |name|
31
- raise "wrong number of attributes" unless @attrs_spec[name].is_a?(Types::Any)
31
+ raise "#{missing_attrs} wrong number of attributes" unless @attrs_spec[name].is_a?(Types::Any)
32
32
  end
33
33
  end
34
34
 
35
35
  mismatching_attrs = input.keys.difference(@attrs_spec.keys)
36
36
 
37
- raise "attribute does not exist" if mismatching_attrs.any?
37
+ raise "attribute #{mismatching_attrs} does not exist" if mismatching_attrs.any?
38
38
 
39
39
  input.reduce({}) do |converted, (name, value)|
40
40
  converted[name] = @attrs_spec[name].cast(value)
@@ -59,7 +59,7 @@ module Yarrow
59
59
  end
60
60
 
61
61
  def merge(other)
62
- unless other.is_a?(self.class)
62
+ unless other.is_a?(self.class) || other.is_a?(Hash)
63
63
  raise ArgumentError.new("cannot merge entities that are not the same type")
64
64
  end
65
65
 
@@ -31,8 +31,13 @@ module Yarrow
31
31
  @accepts = {}
32
32
  end
33
33
 
34
- def accept(type, constructor=:new)
35
- accepts[type] = constructor
34
+ def accept(type, constructor=:new, options=nil)
35
+ accepts[type] = if options.nil?
36
+ [constructor]
37
+ else
38
+ [constructor, options]
39
+ end
40
+
36
41
  self
37
42
  end
38
43
 
@@ -41,8 +46,14 @@ module Yarrow
41
46
  end
42
47
 
43
48
  def coerce(input)
44
- constructor = accepts[input.class]
45
- unit.send(constructor, input)
49
+ constructor, options = accepts[input.class]
50
+
51
+ # TODO: should we clone all input so copy is stored rather than ref?
52
+ if options.nil?
53
+ unit.send(constructor, input)
54
+ else
55
+ unit.send(constructor, input, options.clone)
56
+ end
46
57
  end
47
58
 
48
59
  def check_instance_of!(input)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module Yarrow
3
3
  APP_NAME = "Yarrow"
4
- VERSION = "0.8.3"
4
+ VERSION = "0.8.6"
5
5
  end
@@ -1,42 +1,125 @@
1
- module Yarrow
1
+ module Yarrow
2
2
  module Web
3
- class Document
4
- # This class is somewhat verbose for simplicity and long-term maintainability
5
- # (having a clear and easy to follow construction, rather than doing anything
6
- # too clever which has burned this lib in the past).
7
- def initialize(item, parent, is_index)
8
- @item = item
9
- @parent = parent
10
- @is_index = is_index
3
+ class BaseDocument
4
+ def resource
5
+ @resource
6
+ end
7
+
8
+ def type
9
+ @type
10
+ end
11
+
12
+ # TODO: confirm this can be deleted
13
+ def index
14
+ _index = @item.out_e(:index)
15
+ unless _index.first.nil?
16
+ _index.first.to.props
17
+ else
18
+ nil
19
+ end
20
+ end
21
+
22
+ # TODO: confirm this can be deleted
23
+ def index_body
24
+ @item.props[:index_body]
25
+ end
26
+
27
+ # TODO: manage behaviour with and without current item
28
+ # TODO: link to manifest
29
+ #
30
+ # TODO: replace @item and @collection with @node internally and in class interface
31
+ def breadcrumbs
32
+ path = []
33
+
34
+ current_parent = @node.in(:collection)
35
+
36
+ while !current_parent.first.nil?
37
+ path << current_parent.first.props[:resource]
38
+ current_parent = current_parent.in(:collection)
39
+ end
40
+
41
+ path.reverse
11
42
  end
12
43
 
13
44
  def name
14
- @item.props[:name]
45
+ @resource.name
15
46
  end
16
47
 
17
48
  def title
18
- @item.props[:title]
49
+ @resource.title
19
50
  end
20
51
 
21
- def type
22
- @item.props[:type]
52
+ def body
53
+ return @resource.body.to_html if @resource.respond_to?(:body)
54
+ ""
23
55
  end
24
56
 
25
57
  def url
26
58
  if @parent.nil?
27
59
  "/"
28
60
  else
29
- segments = [@item.props[:name]]
61
+ segments = [@resource.name]
30
62
  current = @parent
31
63
 
32
64
  until current.in(:collection).first.nil? do
33
- segments << current.props[:name]
65
+ segments << current.props[:resource].name
34
66
  current = current.in(:collection).first
35
67
  end
36
68
 
37
- "/" + segments.reverse.join("/")
69
+ suffix = @is_index ? "/" : ""
70
+
71
+ "/" + segments.reverse.join("/") + suffix
38
72
  end
39
73
  end
40
74
  end
75
+
76
+ class IndexDocument < BaseDocument
77
+ # Represents the index document of a collection. This contains
78
+ # a reference to the individual items in the collection as well as
79
+ # any document content itself.
80
+ def initialize(collection, item=nil, is_index=false)
81
+ @collection = collection
82
+ @item = item
83
+ # The parent node of the collection is the first incoming node link
84
+ @parent = collection.in(:collection).first
85
+ @is_index = is_index
86
+
87
+ template_map = collection.out_e(:child).to.all.inject([]) do |result, node|
88
+ result << Document.new(node, false)
89
+ end
90
+
91
+ instance_variable_set("@children", template_map)
92
+ define_singleton_method(:children){ template_map }
93
+
94
+ if @item.nil?
95
+ @resource = collection.props[:resource]
96
+ @type = collection.props[:type]
97
+ @node = collection
98
+ else
99
+ @resource = item.props[:resource]
100
+ @type = item.props[:type]
101
+ @node = item
102
+ end
103
+
104
+ instance_variable_set("@#{@type}", @resource)
105
+ define_singleton_method(@type){ @resource }
106
+ end
107
+ end
108
+
109
+ class Document < BaseDocument
110
+ # This class is somewhat verbose for simplicity and long-term maintainability
111
+ # (having a clear and easy to follow construction, rather than doing anything
112
+ # too clever which has burned this lib in the past).
113
+ def initialize(item, is_index)
114
+ @item = item
115
+ @type = item.props[:type]
116
+ @parent = item.in(:collection).first
117
+ @node = item
118
+ @is_index = is_index
119
+ @resource = item.props[:resource]
120
+ instance_variable_set("@#{item.props[:type]}", @resource)
121
+ define_singleton_method(item.props[:type]){ @resource }
122
+ end
123
+ end
41
124
  end
42
125
  end
@@ -12,7 +12,7 @@ module Yarrow
12
12
  unless collection.props[:index_only]
13
13
  collection.out(:item).each do |item|
14
14
  #if item[:entity].status.to_sym == :published
15
- if item.props[:name] == "index"
15
+ if item.props[:resource].name == "index"
16
16
  index = item
17
17
  else
18
18
  manifest.add_document(item_context(item))
@@ -25,7 +25,7 @@ module Yarrow
25
25
  unless collection.props[:content_only]
26
26
  if index
27
27
  manifest.add_document(collection_index_context(collection, index))
28
- else
28
+ else
29
29
  manifest.add_document(collection_context(collection))
30
30
  end
31
31
  end
@@ -50,15 +50,15 @@ module Yarrow
50
50
  end
51
51
 
52
52
  def self.collection_context(collection)
53
- Document.new(collection, collection.in(:collection).first, true)
53
+ IndexDocument.new(collection, nil, true)
54
54
  end
55
55
 
56
56
  def self.collection_index_context(collection, item)
57
- Document.new(item, collection.in(:collection).first, false)
57
+ IndexDocument.new(collection, item, false)
58
58
  end
59
59
 
60
60
  def self.item_context(item)
61
- Document.new(item, item.in(:collection).first, false)
61
+ Document.new(item, false)
62
62
  end
63
63
  end
64
64
  end
data/lib/yarrow.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "pathname"
2
2
  require "yaml"
3
+ require "kramdown"
3
4
  require "mustache"
4
5
  require "parallel"
5
6
 
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.3
4
+ version: 0.8.6
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-07 00:00:00.000000000 Z
11
+ date: 2022-12-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable