yarrow 0.8.2 → 0.8.5

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: 4ea37c60e3c8bfe51e6ceb9425f0d0c81b93757a681c7987f7852c3335644824
4
- data.tar.gz: 19cd090d3b3348f633f5d303d8b826fd2b8c054b790136355a41d9b4f0f098c4
3
+ metadata.gz: 33fcaf7b1d66e43c0bd0a83967d0cc8b4891b96ec652bbfd28e5e91e7991b7e2
4
+ data.tar.gz: ccf4513eee11929864f76386e22adf39224a1898f5b207a3cefb11a30a5ed6e7
5
5
  SHA512:
6
- metadata.gz: ebd0dd29f6d8800b20560c2e24fd2988caecf19e9ae5f3e44a3d0b427afc9201971be1b086705e72c874683d30625821b270e6ad944c5c28ca9b6cd9ecd65ea2
7
- data.tar.gz: 92d36891c8a270ff663a299b2a217ffe3bb6985200e70d84c4c9d29ee87bb00fb7b2e824e0d932d585b5f8b4a22d578748ee58ad3ae022aa8f1b8ab04117ac6f
6
+ metadata.gz: 20272c169c2dd1280c06b80277a981217ae08c77c80892b4c8d07bac0564099ea0cbc248d54ac3160522e7e38c7dbe129aa703e6cb9e9cb848619f2009e15273
7
+ data.tar.gz: 143a1bbb91909f1e8921b9b587f611293b2f20de9f50d66b18a7346c00e6ebc2078d98ac0e9abcb88cad258e2f3a2d902f1f2eda811d27abd0348ece8f7cf87e
@@ -21,4 +21,11 @@ module Mementus
21
21
  "nodes_count=#{nodes_count} edges_count=#{edges_count}>"
22
22
  end
23
23
  end
24
+
25
+ class Node
26
+ def merge_props(data)
27
+ next_props = props.merge(data)
28
+ @props = next_props.freeze
29
+ end
30
+ end
24
31
  end
data/lib/yarrow/config.rb CHANGED
@@ -62,7 +62,7 @@ module Yarrow
62
62
  attribute :meta, :any
63
63
  attribute :server, :any
64
64
  attribute :content, :__config_content
65
- #attribute :output, :__config_output
65
+ attribute :output, :__config_output
66
66
  end
67
67
  #
68
68
  # `content_dir` and `output_dir` are placeholders and should be overriden
@@ -59,6 +59,12 @@ module Yarrow
59
59
  })
60
60
  end
61
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
+
62
68
  # TODO: messy hack to get rid of Hashie::Mash, this should either be
63
69
  # automated as part of the schema types or a default value should be
64
70
  # generated here (eg: `"#{Dir.pwd}/docs"`)
@@ -70,7 +76,8 @@ module Yarrow
70
76
  source_dir: Pathname.new(File.expand_path(source_dir_or_string)),
71
77
  meta: meta_obj,
72
78
  server: server_obj,
73
- content: content_obj
79
+ content: content_obj,
80
+ output: output_obj
74
81
  )
75
82
  end
76
83
  end
@@ -3,19 +3,19 @@ module Yarrow
3
3
  module Expansion
4
4
  class Strategy
5
5
  include Yarrow::Tools::FrontMatter
6
-
6
+
7
7
  attr_reader :graph
8
-
8
+
9
9
  def initialize(graph)
10
10
  @graph = graph
11
11
  end
12
-
12
+
13
13
  # Extract collection level configuration/metadata from the root node for
14
14
  # this content type.
15
15
  def extract_metadata(node, type)
16
16
  # TODO: support _index or _slug convention as well
17
17
  meta_file = node.out(slug: type.to_s).first
18
-
18
+
19
19
  if meta_file
20
20
  # Process metadata and add it to the collection node
21
21
  # TODO: pass in content converter object
@@ -25,15 +25,32 @@ module Yarrow
25
25
  # Otherwise, assume default collection behaviour
26
26
  data = {}
27
27
  end
28
-
28
+
29
29
  # Generate a default title if not provided in metadata
30
30
  unless data.key?(:title)
31
31
  data[:title] = type.to_s.capitalize
32
32
  end
33
-
33
+
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)
@@ -46,6 +63,7 @@ module Yarrow
46
63
  when '.yml'
47
64
  [nil, YAML.load(File.read(path.to_s), symbolize_names: true)]
48
65
  end
66
+ # TODO: Raise error if unsupported extname reaches here
49
67
  end
50
68
  end
51
69
  end
@@ -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,35 +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
- 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
67
75
 
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
- }
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
74
92
  end
75
93
  end
76
94
 
77
95
  # 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|
96
+ # edges between collections and entities
97
+ entity_links.each do |entity_link|
80
98
  graph.create_edge do |edge|
81
99
  edge.label = :child
82
- edge.from = subcollections[item_link[:parent_path]].id
83
- edge.to = item_link[:item_id]
100
+ edge.from = entity_link[:parent_id].id
101
+ edge.to = entity_link[:child_id].id
84
102
  end
85
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
86
109
  end
87
110
  end
88
111
  end
@@ -4,7 +4,11 @@ module Yarrow
4
4
  def initialize(content_config)
5
5
  @policies = {}
6
6
  content_config.source_map.each_entry do |policy_label, policy_spec|
7
- @policies[policy_label] = Policy.from_spec(policy_label, policy_spec)
7
+ @policies[policy_label] = Policy.from_spec(
8
+ policy_label,
9
+ policy_spec,
10
+ content_config.module
11
+ )
8
12
  end
9
13
  end
10
14
 
@@ -9,6 +9,8 @@ module Yarrow
9
9
 
10
10
  DEFAULT_MATCH_PATH = "."
11
11
 
12
+ MODULE_SEPARATOR = "::"
13
+
12
14
  # Construct a content policy from the given source specification.
13
15
  def self.from_spec(policy_label, policy_props, module_prefix="")
14
16
  # TODO: validate length, structure etc
@@ -27,7 +29,7 @@ module Yarrow
27
29
  if policy_props.key?(:entity)
28
30
  Yarrow::Symbols.to_plural(policy_props[:entity])
29
31
  else
30
- Yarrow::Symbols.to_plural(label)
32
+ Yarrow::Symbols.to_plural(policy_label)
31
33
  end
32
34
  end
33
35
 
@@ -38,7 +40,7 @@ module Yarrow
38
40
  if policy_props.key?(:container)
39
41
  Yarrow::Symbols.to_singular(policy_props[:container])
40
42
  else
41
- Yarrow::Symbols.to_singular(label)
43
+ Yarrow::Symbols.to_singular(policy_label)
42
44
  end
43
45
  end
44
46
 
@@ -64,7 +66,7 @@ module Yarrow
64
66
  end
65
67
  end
66
68
 
67
- attr_reader :container, :entity, :expansion, :extensions, :match_path, :module
69
+ attr_reader :container, :entity, :expansion, :extensions, :match_path, :module_prefix
68
70
 
69
71
  def initialize(container, entity, expansion, extensions, match_path, module_prefix)
70
72
  @container = container
@@ -72,35 +74,18 @@ module Yarrow
72
74
  @expansion = expansion
73
75
  @extensions = extensions
74
76
  @match_path = match_path
75
- @module_prefix = module_prefix
77
+ @module_prefix = module_prefix.split(MODULE_SEPARATOR)
76
78
  end
77
79
 
78
80
  def container_const
79
- @container_const ||= Yarrow::Symbols.to_module_const([module_prefix, container])
80
- end
81
-
82
- def entity_const
83
- @entity_const ||= Yarrow::Symbols.to_module_const([module_prefix, entity])
84
- end
85
-
86
- def _container
87
- return @properties.container if @properties.container
88
- Yarrow::Symbols.to_plural(@properties.entity)
81
+ @container_const ||= Yarrow::Symbols.to_module_const([*module_prefix, container])
89
82
  end
90
83
 
91
- def _entity
92
- return @properties.entity if @properties.entity
93
- Yarrow::Symbols.to_singular(@properties.container)
94
- end
84
+ alias_method :collection, :container
85
+ alias_method :collection_const, :container_const
95
86
 
96
- def _extensions
97
- return @properties.extensions if @properties.extensions
98
- DEFAULT_EXTENSIONS
99
- end
100
-
101
- def _match_path
102
- return @properties.match_path if @properties.match_path
103
- DEFAULT_MATCH_PATH
87
+ def entity_const
88
+ @entity_const ||= Yarrow::Symbols.to_module_const([*module_prefix, entity])
104
89
  end
105
90
  end
106
91
  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.2"
4
+ VERSION = "0.8.5"
5
5
  end
@@ -6,35 +6,75 @@ module Yarrow
6
6
  # too clever which has burned this lib in the past).
7
7
  def initialize(item, parent, is_index)
8
8
  @item = item
9
+ @resource = item.props[:resource]
9
10
  @parent = parent
10
11
  @is_index = is_index
11
12
  end
12
13
 
14
+ def resource
15
+ @resource
16
+ end
17
+
18
+ def index
19
+ _index = @item.out_e(:index)
20
+ unless _index.first.nil?
21
+ _index.first.to.props
22
+ else
23
+ nil
24
+ end
25
+ end
26
+
27
+ def index_body
28
+ @item.props[:index_body]
29
+ end
30
+
31
+ # TODO: manage behaviour with and without current item
32
+ # TODO: link to manifest
33
+ def breadcrumbs
34
+ path = []
35
+
36
+ current_parent = @item.in(:collection)
37
+
38
+ while !current_parent.first.nil?
39
+ path << current_parent.first.props[:resource]
40
+ current_parent = current_parent.in(:collection)
41
+ end
42
+
43
+ path.reverse
44
+ end
45
+
13
46
  def name
14
- @item.props[:name]
47
+ @resource.name
15
48
  end
16
49
 
17
50
  def title
18
- @item.props[:title]
51
+ @resource.title
19
52
  end
20
53
 
21
54
  def type
22
55
  @item.props[:type]
23
56
  end
24
57
 
58
+ def body
59
+ return @resource.body.to_html if @resource.respond_to?(:body)
60
+ ""
61
+ end
62
+
25
63
  def url
26
64
  if @parent.nil?
27
65
  "/"
28
66
  else
29
- segments = [@item.props[:name]]
67
+ segments = [@resource.name]
30
68
  current = @parent
31
69
 
32
70
  until current.in(:collection).first.nil? do
33
- segments << current.props[:name]
71
+ segments << current.props[:resource].name
34
72
  current = current.in(:collection).first
35
73
  end
36
74
 
37
- "/" + segments.reverse.join("/")
75
+ suffix = @is_index ? "/" : ""
76
+
77
+ "/" + segments.reverse.join("/") + suffix
38
78
  end
39
79
  end
40
80
  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)
@@ -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
@@ -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
@@ -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.2
4
+ version: 0.8.5
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-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable