yarrow 0.7.2 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/lib/yarrow/config.rb +5 -5
- data/lib/yarrow/content/expansion.rb +6 -41
- data/lib/yarrow/content/expansion_strategy.rb +51 -0
- data/lib/yarrow/content/graph.rb +2 -2
- data/lib/yarrow/content/manifest.rb +4 -4
- data/lib/yarrow/content/model.rb +46 -0
- data/lib/yarrow/content/policy.rb +48 -0
- data/lib/yarrow/content/resource.rb +29 -0
- data/lib/yarrow/content/source.rb +70 -3
- data/lib/yarrow/content/tree_expansion.rb +15 -6
- data/lib/yarrow/generator.rb +1 -1
- data/lib/yarrow/schema/definitions.rb +30 -0
- data/lib/yarrow/schema/dictionary.rb +49 -0
- data/lib/yarrow/schema/entity.rb +51 -0
- data/lib/yarrow/schema/types.rb +73 -0
- data/lib/yarrow/schema/value.rb +46 -0
- data/lib/yarrow/schema.rb +28 -119
- data/lib/yarrow/symbols.rb +15 -0
- data/lib/yarrow/version.rb +1 -1
- data/lib/yarrow.rb +10 -3
- data/yarrow.gemspec +1 -1
- metadata +18 -12
- data/lib/yarrow/content/collection_expander.rb +0 -241
- data/lib/yarrow/content/object_type.rb +0 -42
- data/lib/yarrow/content/source_collector.rb +0 -78
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c49c3ae295fd127f7533a0af4c543d5315a2929a580a21b6b4c26379e519490
|
4
|
+
data.tar.gz: 6867e3004c89dc7362a83378f3b0d55a14803d513cc77445c7893a31ffb4e4bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0056694b64f9ac4d000e85341570b4a5b630f27b5de15d5dcf12eeae1dc3625368abfd1d65a6c0a26f9793cbd6993e076c7e46d76ef97007874e8dddfd75c7af'
|
7
|
+
data.tar.gz: fa5469997f6e674f3f2b1daf8bbc525637b2cbb2db7b9a8a1d494c85b1ce4d2725753ece2a2d114a905b994fe96f99e9db09178fa6f92483d4ee5d6208a500ad
|
data/.gitignore
CHANGED
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:
|
56
|
-
content_dir:
|
57
|
-
output_dir:
|
58
|
-
meta:
|
59
|
-
server:
|
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
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
data/lib/yarrow/content/graph.rb
CHANGED
@@ -7,7 +7,7 @@ module Yarrow
|
|
7
7
|
#
|
8
8
|
# @return [Yarrow::Content::Graph]
|
9
9
|
def self.from_source(config)
|
10
|
-
new(
|
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::
|
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, :
|
25
|
+
attr_reader :documents, :assets
|
26
26
|
|
27
27
|
def initialize
|
28
28
|
@documents = []
|
29
|
-
@
|
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
|
57
|
-
@
|
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
|
-
|
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
|
-
|
7
|
-
|
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 <
|
4
|
-
def expand(
|
5
|
-
|
6
|
-
|
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
|
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] =
|
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
|
data/lib/yarrow/generator.rb
CHANGED
@@ -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
|