yarrow 0.7.2 → 0.7.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: 395db81bd349ed8e1a6b5cdc67d0fd191f6ede369bd358483611e0b79fb27cec
4
- data.tar.gz: '028961b86756cfc6bc1453c6d3ad7789149e8921066fb5429358e1b3b08393eb'
3
+ metadata.gz: 0c49c3ae295fd127f7533a0af4c543d5315a2929a580a21b6b4c26379e519490
4
+ data.tar.gz: 6867e3004c89dc7362a83378f3b0d55a14803d513cc77445c7893a31ffb4e4bd
5
5
  SHA512:
6
- metadata.gz: f531ff89949b7d1b80e215529f2c6c29d812f958e9af4491d58b7a0f10f9419a9ee26385ac58891cc435053fff31123d2dcfed0e02c8dcd48927d0b47c312f9f
7
- data.tar.gz: 8a5bb918411a04ac7d5e8b3c649501346e94d3cc76c17097ec9eed436685b163a04294b40491b38f0ea8539a28722bc8e77043bdab81aec493e04d5ccc0b6e0e
6
+ metadata.gz: '0056694b64f9ac4d000e85341570b4a5b630f27b5de15d5dcf12eeae1dc3625368abfd1d65a6c0a26f9793cbd6993e076c7e46d76ef97007874e8dddfd75c7af'
7
+ data.tar.gz: fa5469997f6e674f3f2b1daf8bbc525637b2cbb2db7b9a8a1d494c85b1ce4d2725753ece2a2d114a905b994fe96f99e9db09178fa6f92483d4ee5d6208a500ad
data/.gitignore CHANGED
@@ -6,3 +6,4 @@ Gemfile.lock
6
6
  yarrow-*.gem
7
7
  coverage
8
8
  bin/scripts
9
+ .DS_Store
data/lib/yarrow/config.rb CHANGED
@@ -52,11 +52,11 @@ module Yarrow
52
52
  #
53
53
  # TODO: meta should be union of Type::Optional and Config::Meta
54
54
  Instance = Yarrow::Schema::Value.new(
55
- project_dir: Pathname,
56
- content_dir: Pathname,
57
- output_dir: Pathname,
58
- meta: Yarrow::Schema::Type::Any,
59
- server: Yarrow::Schema::Type::Any
55
+ project_dir: :path,
56
+ content_dir: :path,
57
+ output_dir: :path,
58
+ meta: :any,
59
+ server: :any
60
60
  )
61
61
  end
62
62
  end
@@ -1,49 +1,14 @@
1
1
  module Yarrow
2
2
  module Content
3
3
  class Expansion
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
4
+ def initialize(model)
5
+ @model = model
34
6
  end
35
7
 
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)]
8
+ def expand(graph)
9
+ @model.each_policy do |policy|
10
+ strategy = TreeExpansion.new(graph)
11
+ strategy.expand(policy)
47
12
  end
48
13
  end
49
14
  end
@@ -0,0 +1,51 @@
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
@@ -7,7 +7,7 @@ module Yarrow
7
7
  #
8
8
  # @return [Yarrow::Content::Graph]
9
9
  def self.from_source(config)
10
- new(SourceCollector.collect(config.content_dir), config)
10
+ new(Source.collect(config.content_dir), config)
11
11
  end
12
12
 
13
13
  attr_reader :graph, :config
@@ -18,7 +18,7 @@ module Yarrow
18
18
  end
19
19
 
20
20
  def expand_pages
21
- expander = Yarrow::Content::CollectionExpander.new
21
+ expander = Yarrow::Content::Expansion.new(Yarrow::Content::Model.new)
22
22
  expander.expand(graph)
23
23
  end
24
24
 
@@ -22,11 +22,11 @@ module Yarrow
22
22
  manifest
23
23
  end
24
24
 
25
- attr_reader :documents, :resources
25
+ attr_reader :documents, :assets
26
26
 
27
27
  def initialize
28
28
  @documents = []
29
- @resources = []
29
+ @assets = []
30
30
  end
31
31
 
32
32
  def self.collection_context(collection)
@@ -53,8 +53,8 @@ module Yarrow
53
53
  @documents << document
54
54
  end
55
55
 
56
- def add_resource(resource)
57
- @resources << resource
56
+ def add_asset(asset)
57
+ @assets << asset
58
58
  end
59
59
  end
60
60
  end
@@ -0,0 +1,46 @@
1
+ module Yarrow
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
+ 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
34
+ end
35
+ end
36
+
37
+ def each_policy(&block)
38
+ @policies.each_value(&block)
39
+ end
40
+
41
+ def policy_for(policy_key)
42
+ @policies[policy_key]
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,48 @@
1
+ module Yarrow
2
+ module Content
3
+ class Policy
4
+ Options = Yarrow::Schema::Value.new(
5
+ :container,
6
+ :entity,
7
+ :extensions,
8
+ :match_path
9
+ )
10
+
11
+ DEFAULT_EXTENSIONS = [".md", ".yml", ".htm"]
12
+
13
+ DEFAULT_MATCH_PATH = "."
14
+
15
+ def self.from_name(name)
16
+ new(Options.new(container: name.to_sym))
17
+ end
18
+
19
+ def initialize(properties)
20
+ unless properties.respond_to?(:container) || properties.respond_to?(:entity)
21
+ raise "Must provide a container name or entity name"
22
+ end
23
+
24
+ @properties = properties
25
+ end
26
+
27
+ def container
28
+ return @properties.container if @properties.container
29
+ Yarrow::Symbols.to_plural(@properties.entity)
30
+ end
31
+
32
+ def entity
33
+ return @properties.entity if @properties.entity
34
+ Yarrow::Symbols.to_singular(@properties.container)
35
+ end
36
+
37
+ def extensions
38
+ return @properties.extensions if @properties.extensions
39
+ DEFAULT_EXTENSIONS
40
+ end
41
+
42
+ def match_path
43
+ return @properties.match_path if @properties.match_path
44
+ DEFAULT_MATCH_PATH
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,29 @@
1
+ module Yarrow
2
+ module Content
3
+ class Resource < Schema::Entity
4
+ attribute :id, :string
5
+ attribute :name, :string
6
+ attribute :title, :string
7
+ attribute :url, :string
8
+ attribute :content, :string
9
+
10
+ def self.from_frontmatter_url(data, url_field)
11
+ new({
12
+ id: data[:id],
13
+ name: data[:name],
14
+ title: data[:title],
15
+ content: data[:content],
16
+ url: data[url_field]
17
+ })
18
+ end
19
+
20
+ def self.from_template_url(data, template, &block)
21
+
22
+ end
23
+
24
+ def self.from_source_path(data, &block)
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,10 +1,77 @@
1
1
  module Yarrow
2
2
  module Content
3
+ # Collects a digraph of all directories and files underneath the given input
4
+ # directory.
3
5
  class Source
4
- attr_reader :input_dir
6
+ def self.collect(input_dir)
7
+ Mementus::Graph.new(is_mutable: true) do
8
+ root = create_node do |root|
9
+ root.label = :root
10
+ end
5
11
 
6
- def initialize(config)
7
- @input_dir = config[:input_dir]
12
+ root_dir_entry = Pathname.new(input_dir)
13
+
14
+ root_dir = create_node do |dir|
15
+ dir.label = :directory
16
+ dir.props = {
17
+ name: root_dir_entry.basename.to_s,
18
+ path: root_dir_entry.to_s,
19
+ entry: root_dir_entry
20
+ }
21
+ end
22
+
23
+ create_edge do |child|
24
+ child.label = :child
25
+ child.from = root.id
26
+ child.to = root_dir.id
27
+ end
28
+
29
+ directories = {
30
+ root_dir_entry.to_s => root_dir.id
31
+ }
32
+
33
+ Pathname.glob("#{input_dir}/**/**").each do |entry|
34
+ if entry.directory?
35
+ content_node = create_node do |dir|
36
+ dir.label = :directory
37
+ # dir.props[:name] = entry.basename.to_s
38
+ # dir.props[:slug] = entry.basename.to_s
39
+ # dir.props[:path] = entry.to_s
40
+ # dir.props[:entry] = entry
41
+ dir.props = {
42
+ name: entry.basename.to_s,
43
+ path: entry.to_s,
44
+ entry: entry
45
+ }
46
+ end
47
+
48
+ directories[entry.to_s] = content_node.id
49
+ else
50
+ content_node = create_node do |file|
51
+ file.label = :file
52
+ # file.props[:name] = entry.basename.to_s
53
+ # file.props[:slug] = entry.basename.sub_ext('').to_s
54
+ # file.props[:path] = entry.to_s
55
+ # file.props[:entry] = entry
56
+
57
+ file.props = {
58
+ name: entry.basename.to_s,
59
+ ext: entry.extname.to_s,
60
+ path: entry.to_s,
61
+ entry: entry
62
+ }
63
+ end
64
+ end
65
+
66
+ if directories.key?(entry.dirname.to_s)
67
+ create_edge do |edge|
68
+ edge.label = :child
69
+ edge.from = directories[entry.dirname.to_s]
70
+ edge.to = content_node
71
+ end
72
+ end
73
+ end
74
+ end
8
75
  end
9
76
  end
10
77
  end
@@ -1,14 +1,23 @@
1
1
  module Yarrow
2
2
  module Content
3
- class TreeExpansion < Expansion
4
- def expand(content_type)
5
- type = content_type.collection
6
- exts = content_type.extensions
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
7
15
 
8
16
  # If match path represents entire content dir, then include the entire
9
17
  # content dir instead of scanning from a subfolder matching the name of
10
18
  # the collection.
11
- start_node = if content_type.match_path == "."
19
+ #start_node = if policy.match_path == "."
20
+ start_node = if true
12
21
  graph.n(:root)
13
22
  else
14
23
  graph.n(:root).out(name: type.to_s)
@@ -52,7 +61,7 @@ module Yarrow
52
61
  # Create an item node representing a file mapped to a unique content object
53
62
  item = graph.create_node do |item_node|
54
63
  item_node.label = :item
55
- item_node.props[:type] = content_type.entity
64
+ item_node.props[:type] = policy.record
56
65
  item_node.props[:name] = node.props[:entry].basename(node.props[:entry].extname).to_s
57
66
  item_node.props[:body] = body if body
58
67
  item_node.props[:title] = meta[:title] if meta
@@ -13,7 +13,7 @@ module Yarrow
13
13
  provides Content::Graph
14
14
 
15
15
  def step(content)
16
- expander = Content::CollectionExpander.new
16
+ expander = Content::Expansion.new(Yarrow::Content::Model.new)
17
17
  expander.expand(content.graph)
18
18
  content
19
19
  end
@@ -0,0 +1,30 @@
1
+ module Yarrow
2
+ module Schema
3
+ module Definitions
4
+ DEFINED_TYPES = {
5
+ string: Type::Raw[String],
6
+ integer: Type::Raw[Integer],
7
+ path: Type::Raw[Pathname],
8
+ any: Type::Any
9
+ }
10
+
11
+ def self.register(identifier, type_class)
12
+ if DEFINED_TYPES.key?(identifier)
13
+ raise "#{identifier} is already defined"
14
+ end
15
+
16
+ DEFINED_TYPES[identifier] = type_class
17
+ end
18
+
19
+ def resolve_type(identifier)
20
+ #return identifier unless identifier.is_a?(Symbol)
21
+
22
+ unless DEFINED_TYPES.key?(identifier)
23
+ raise "#{identifier} is not defined"
24
+ end
25
+
26
+ return DEFINED_TYPES[identifier]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,49 @@
1
+ module Yarrow
2
+ module Schema
3
+ # Specifies types plugged into each attribute slot and runs any required
4
+ # validations and coercions.
5
+ #
6
+ # Current design throws on error rather than returns a boolean result.
7
+ class Dictionary
8
+ include Definitions
9
+
10
+ # @param attrs_spec [Hash] defines the slots in the schema to validate against
11
+ def initialize(attrs_spec={})
12
+ @attrs_spec = attrs_spec.reduce({}) do |spec, (name, type_identifier)|
13
+ spec[name] = resolve_type(type_identifier)
14
+ spec
15
+ end
16
+ end
17
+
18
+ def define_attribute(name, type_identifier)
19
+ @attrs_spec[name] = resolve_type(type_identifier)
20
+ end
21
+
22
+ def attr_names
23
+ @attrs_spec.keys
24
+ end
25
+
26
+ def check(input)
27
+ missing_attrs = @attrs_spec.keys.difference(input.keys)
28
+
29
+ if missing_attrs.any?
30
+ missing_attrs.each do |name|
31
+ raise "wrong number of attributes" unless @attrs_spec[name].eql?(Type::Any)
32
+ end
33
+ end
34
+
35
+ mismatching_attrs = input.keys.difference(@attrs_spec.keys)
36
+
37
+ raise "attribute does not exist" if mismatching_attrs.any?
38
+
39
+ input.each do |(name, value)|
40
+ unless value.is_a?(@attrs_spec[name]) || @attrs_spec[name].eql?(Type::Any)
41
+ raise "wrong data type"
42
+ end
43
+ end
44
+
45
+ true
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,51 @@
1
+ module Yarrow
2
+ module Schema
3
+ # Entity with comparison by reference equality. Generates attribute helpers
4
+ # for a declared set of props. Used to replace Hashie::Mash without dragging
5
+ # in a whole new library.
6
+ class Entity
7
+ class << self
8
+ def attribute(name, value_type)
9
+ dictionary.define_attribute(name, value_type)
10
+ attr_reader(name)
11
+ end
12
+
13
+ def dictionary
14
+ @dictionary ||= Dictionary.new({})
15
+ end
16
+ end
17
+
18
+ def initialize(config)
19
+ dictionary.check(config)
20
+ # dictionary.each_key do |name|
21
+ # raise "missing declared attribute #{name}" unless config.key?(name)
22
+ # end
23
+ #
24
+ config.each_pair do |key, value|
25
+ # raise "#{key} not a declared attribute" unless dictionary.key?(key)
26
+ #
27
+ # defined_type = dictionary[key]
28
+ #
29
+ # unless value.is_a?(defined_type)
30
+ # raise "#{key} accepts #{defined_type} but #{value.class} given"
31
+ # end
32
+
33
+ instance_variable_set("@#{key}", value)
34
+ end
35
+ end
36
+
37
+ def to_h
38
+ dictionary.attr_names.reduce({}) do |attr_dict, name|
39
+ attr_dict[name] = instance_variable_get("@#{name}")
40
+ attr_dict
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def dictionary
47
+ self.class.dictionary
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,73 @@
1
+ module Yarrow
2
+ module Schema
3
+ module Types
4
+ class CastError < TypeError
5
+ def self.instance_of(t, u)
6
+ new("#{t} is not an instance of #{u}")
7
+ end
8
+
9
+ def self.kind_of(t, u)
10
+ new("#{t} is not a subclass of #{u}")
11
+ end
12
+ end
13
+
14
+ class TypeClass
15
+ attr_reader :unit
16
+
17
+ def initialize(unit=nil)
18
+ @unit = unit
19
+ end
20
+
21
+ def check_instance_of!(input)
22
+ unless input.instance_of?(unit)
23
+ raise CastError.instance_of(input.class, unit)
24
+ end
25
+ end
26
+
27
+ def check_kind_of!(input)
28
+ unless input.kind_of?(unit)
29
+ raise CastError.kind_of(input.class, unit)
30
+ end
31
+ end
32
+
33
+ def check_respond_to!(input)
34
+
35
+ end
36
+
37
+ def cast(input); end
38
+ end
39
+
40
+ class Any < TypeClass
41
+ def cast(input)
42
+ input
43
+ end
44
+ end
45
+
46
+ class Instance < TypeClass
47
+ def cast(input)
48
+ check_instance_of!(input)
49
+ input
50
+ end
51
+ end
52
+
53
+ class Kind
54
+ def cast(input)
55
+ check_kind_of!(input)
56
+ input
57
+ end
58
+ end
59
+
60
+ class Interface
61
+
62
+ end
63
+
64
+ class Optional
65
+
66
+ end
67
+
68
+ class Constrained
69
+
70
+ end
71
+ end
72
+ end
73
+ end