ducktrap 0.0.1
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 +7 -0
- data/.circle.yml +6 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/.travis.yml +16 -0
- data/Gemfile +6 -0
- data/Gemfile.devtools +59 -0
- data/Guardfile +18 -0
- data/LICENSE +20 -0
- data/README.md +47 -0
- data/Rakefile +2 -0
- data/TODO +4 -0
- data/config/devtools.yml +2 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/heckle.yml +3 -0
- data/config/mutant.yml +2 -0
- data/config/reek.yml +98 -0
- data/config/yardstick.yml +2 -0
- data/ducktrap.gemspec +23 -0
- data/lib/ducktrap.rb +73 -0
- data/lib/ducktrap/builder.rb +36 -0
- data/lib/ducktrap/error.rb +48 -0
- data/lib/ducktrap/evaluator.rb +127 -0
- data/lib/ducktrap/evaluator/invalid.rb +30 -0
- data/lib/ducktrap/evaluator/static.rb +25 -0
- data/lib/ducktrap/formatter.rb +138 -0
- data/lib/ducktrap/mapper.rb +59 -0
- data/lib/ducktrap/nary.rb +148 -0
- data/lib/ducktrap/node.rb +94 -0
- data/lib/ducktrap/node/anima.rb +40 -0
- data/lib/ducktrap/node/anima/dump.rb +39 -0
- data/lib/ducktrap/node/anima/load.rb +39 -0
- data/lib/ducktrap/node/block.rb +44 -0
- data/lib/ducktrap/node/coercion.rb +7 -0
- data/lib/ducktrap/node/custom.rb +121 -0
- data/lib/ducktrap/node/disjunction.rb +8 -0
- data/lib/ducktrap/node/forward.rb +55 -0
- data/lib/ducktrap/node/guard_nil.rb +42 -0
- data/lib/ducktrap/node/hash.rb +7 -0
- data/lib/ducktrap/node/hash/transform.rb +53 -0
- data/lib/ducktrap/node/invalid.rb +29 -0
- data/lib/ducktrap/node/inverse.rb +46 -0
- data/lib/ducktrap/node/key.rb +61 -0
- data/lib/ducktrap/node/key/add.rb +42 -0
- data/lib/ducktrap/node/key/delete.rb +44 -0
- data/lib/ducktrap/node/key/dump.rb +38 -0
- data/lib/ducktrap/node/key/fetch.rb +49 -0
- data/lib/ducktrap/node/map.rb +42 -0
- data/lib/ducktrap/node/noop.rb +30 -0
- data/lib/ducktrap/node/primitive.rb +61 -0
- data/lib/ducktrap/node/static.rb +33 -0
- data/lib/ducktrap/nullary.rb +41 -0
- data/lib/ducktrap/pretty_dump.rb +31 -0
- data/lib/ducktrap/registry.rb +60 -0
- data/lib/ducktrap/singleton.rb +55 -0
- data/lib/ducktrap/unary.rb +153 -0
- data/spec/rcov.opts +7 -0
- data/spec/shared/inverse_behavior.rb +7 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/ice_nine_config.rb +8 -0
- data/spec/support/let_mock_helper.rb +13 -0
- data/spec/unit/ducktrap/builder/class_methods/new_spec.rb +41 -0
- data/spec/unit/ducktrap/builder/new_spec.rb +41 -0
- data/spec/unit/ducktrap/class_methods/build_spec.rb +11 -0
- data/spec/unit/ducktrap/error/exception/pretty_inspect_spec.rb +22 -0
- data/spec/unit/ducktrap/error/pretty_inspect_spec.rb +19 -0
- data/spec/unit/ducktrap/evaluator/assert_successful_spec.rb +38 -0
- data/spec/unit/ducktrap/evaluator/invalid/output_spec.rb +14 -0
- data/spec/unit/ducktrap/evaluator/noop/output_spec.rb +14 -0
- data/spec/unit/ducktrap/evaluator/output_spec.rb +52 -0
- data/spec/unit/ducktrap/evaluator/pretty_inspect_spec.rb +63 -0
- data/spec/unit/ducktrap/evaluator/successful_predicate_spec.rb +36 -0
- data/spec/unit/ducktrap/failed_transformation_error/message_spec.rb +10 -0
- data/spec/unit/ducktrap/formatter/attribute_spec.rb +24 -0
- data/spec/unit/ducktrap/formatter/body_spec.rb +19 -0
- data/spec/unit/ducktrap/formatter/class_methods/new_spec.rb +14 -0
- data/spec/unit/ducktrap/formatter/initialize_spec.rb +14 -0
- data/spec/unit/ducktrap/formatter/name_spec.rb +25 -0
- data/spec/unit/ducktrap/formatter/nest_spec.rb +48 -0
- data/spec/unit/ducktrap/formatter/puts_spec.rb +36 -0
- data/spec/unit/ducktrap/mapper/builder/dumper_spec.rb +20 -0
- data/spec/unit/ducktrap/mapper/builder/loader_spec.rb +20 -0
- data/spec/unit/ducktrap/mapper/builder/object_spec.rb +90 -0
- data/spec/unit/ducktrap/mapper/class_methods/build_spec.rb +20 -0
- data/spec/unit/ducktrap/nary/builder/add_spec.rb +16 -0
- data/spec/unit/ducktrap/nary/builder/method_missing_spec.rb +44 -0
- data/spec/unit/ducktrap/nary/builder/output_spec.rb +16 -0
- data/spec/unit/ducktrap/nary/class_methods/build_spec.rb +22 -0
- data/spec/unit/ducktrap/nary/class_methods/included_spec.rb +13 -0
- data/spec/unit/ducktrap/nary/evaluator/output_spec.rb +79 -0
- data/spec/unit/ducktrap/nary/instance_methods/inverse_spec.rb +24 -0
- data/spec/unit/ducktrap/nary/instance_methods/pretty_inspect_spec.rb +31 -0
- data/spec/unit/ducktrap/node/anima/dump/evaluator/output_spec.rb +18 -0
- data/spec/unit/ducktrap/node/anima/dump/inverse_spec.rb +10 -0
- data/spec/unit/ducktrap/node/anima/dump/pretty_inspect_spec.rb +15 -0
- data/spec/unit/ducktrap/node/anima/evaluator/output_spec.rb +18 -0
- data/spec/unit/ducktrap/node/anima/load/evaluator/output_spec.rb +35 -0
- data/spec/unit/ducktrap/node/anima/load/inverse_spec.rb +10 -0
- data/spec/unit/ducktrap/node/anima/load/pretty_inspect_spec.rb +15 -0
- data/spec/unit/ducktrap/node/anima/pretty_inspect_spec.rb +15 -0
- data/spec/unit/ducktrap/node/block/evaluator/output_spec.rb +79 -0
- data/spec/unit/ducktrap/node/block/inverse_spec.rb +24 -0
- data/spec/unit/ducktrap/node/call_spec.rb +21 -0
- data/spec/unit/ducktrap/node/class_methods/build_spec.rb +11 -0
- data/spec/unit/ducktrap/node/class_methods/register_spec.rb +16 -0
- data/spec/unit/ducktrap/node/custom/builder/forward_spec.rb +54 -0
- data/spec/unit/ducktrap/node/custom/builder/inverse_spec.rb +54 -0
- data/spec/unit/ducktrap/node/custom/builder/object_spec.rb +54 -0
- data/spec/unit/ducktrap/node/custom/call_spec.rb +21 -0
- data/spec/unit/ducktrap/node/custom/class_methods/build_spec.rb +20 -0
- data/spec/unit/ducktrap/node/custom/inverse_spec.rb +12 -0
- data/spec/unit/ducktrap/node/custom/pretty_inspect_spec.rb +15 -0
- data/spec/unit/ducktrap/node/forward/evaluator/output_spec.rb +13 -0
- data/spec/unit/ducktrap/node/forward/inverse_spec.rb +14 -0
- data/spec/unit/ducktrap/node/forward/pretty_inspect_spec.rb +19 -0
- data/spec/unit/ducktrap/node/guard_nil/call_spec.rb +21 -0
- data/spec/unit/ducktrap/node/guard_nil/evaluator/output_spec.rb +27 -0
- data/spec/unit/ducktrap/node/guard_nil/inverse_spec.rb +12 -0
- data/spec/unit/ducktrap/node/hash/transform/evaluator/output_spec.rb +51 -0
- data/spec/unit/ducktrap/node/hash/transform/inverse_spec.rb +27 -0
- data/spec/unit/ducktrap/node/invalid/call_spec.rb +13 -0
- data/spec/unit/ducktrap/node/invalid/inverse_spec.rb +11 -0
- data/spec/unit/ducktrap/node/inverse/call_spec.rb +11 -0
- data/spec/unit/ducktrap/node/inverse/inverse_spec.rb +14 -0
- data/spec/unit/ducktrap/node/inverse/pretty_inspect_spec.rb +19 -0
- data/spec/unit/ducktrap/node/key/add/call_spec.rb +15 -0
- data/spec/unit/ducktrap/node/key/add/evaluator/call_spec.rb +15 -0
- data/spec/unit/ducktrap/node/key/add/inverse_spec.rb +14 -0
- data/spec/unit/ducktrap/node/key/delete/call_spec.rb +13 -0
- data/spec/unit/ducktrap/node/key/delete/evaluator/call_spec.rb +13 -0
- data/spec/unit/ducktrap/node/key/delete/inverse_spec.rb +13 -0
- data/spec/unit/ducktrap/node/key/dump/evaluator/output_spec.rb +37 -0
- data/spec/unit/ducktrap/node/key/dump/inverse_spec.rb +14 -0
- data/spec/unit/ducktrap/node/key/evaluator/key_spec.rb +26 -0
- data/spec/unit/ducktrap/node/key/fetch/evaluator/output_spec.rb +48 -0
- data/spec/unit/ducktrap/node/key/fetch/inverse_spec.rb +14 -0
- data/spec/unit/ducktrap/node/key/pretty_inspect_spec.rb +20 -0
- data/spec/unit/ducktrap/node/map/evaluator/output_spec.rb +22 -0
- data/spec/unit/ducktrap/node/map/inverse_spec.rb +15 -0
- data/spec/unit/ducktrap/node/noop/call_spec.rb +11 -0
- data/spec/unit/ducktrap/node/noop/inverse_spec.rb +9 -0
- data/spec/unit/ducktrap/node/pretty_inspect_spec.rb +17 -0
- data/spec/unit/ducktrap/node/primitive/call_spec.rb +19 -0
- data/spec/unit/ducktrap/node/primitive/inverse_spec.rb +13 -0
- data/spec/unit/ducktrap/node/primitive/pretty_inspect_spec.rb +15 -0
- data/spec/unit/ducktrap/node/run_spec.rb +46 -0
- data/spec/unit/ducktrap/node/static/call_spec.rb +13 -0
- data/spec/unit/ducktrap/node/static/inverse_spec.rb +14 -0
- data/spec/unit/ducktrap/nullary/class_methods/build_spec.rb +37 -0
- data/spec/unit/ducktrap/nullary/class_methods/included_spec.rb +7 -0
- data/spec/unit/ducktrap/pretty_dump/pretty_dump_spec.rb +47 -0
- data/spec/unit/ducktrap/pretty_dump/pretty_inspect_spec.rb +22 -0
- data/spec/unit/ducktrap/registry/lookup_spec.rb +27 -0
- data/spec/unit/ducktrap/registry/register_spec.rb +21 -0
- data/spec/unit/ducktrap/singleton/class_methods/included_spec.rb +13 -0
- data/spec/unit/ducktrap/singleton/class_methods/instance_spec.rb +19 -0
- data/spec/unit/ducktrap/singleton/instance_methods/inspect_spec.rb +19 -0
- data/spec/unit/ducktrap/singleton/instance_methods/pretty_inspect_spec.rb +17 -0
- data/spec/unit/ducktrap/unary/class_methods/build_spec.rb +18 -0
- data/spec/unit/ducktrap/unary/class_methods/included_spec.rb +14 -0
- data/spec/unit/ducktrap/unary/evaluator/output2_spec.rb +48 -0
- data/spec/unit/ducktrap/unary/evaluator/output_spec.rb +37 -0
- data/spec/unit/ducktrap/unary/instance_methods/pretty_inspect_spec.rb +15 -0
- metadata +399 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
module Ducktrap
|
2
|
+
|
3
|
+
# Error returned for invalid inputs
|
4
|
+
class Error
|
5
|
+
include PrettyDump, Adamantium::Flat, Concord::Public.new(:context, :input)
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
# Dump state to output
|
10
|
+
#
|
11
|
+
# @param [Formatter] formatter
|
12
|
+
#
|
13
|
+
# @return [undefined]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
#
|
17
|
+
def dump(output)
|
18
|
+
output.name(self)
|
19
|
+
output.attribute(:input, input)
|
20
|
+
output.nest(:context, context)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Error caused by exception
|
24
|
+
class Exception < self
|
25
|
+
include Concord::Public.new(:context, :input, :exception)
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# Dump state to output
|
30
|
+
#
|
31
|
+
# @param [Formatter] formatter
|
32
|
+
#
|
33
|
+
# @return [undefined]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
def dump(output)
|
38
|
+
excpt = exception
|
39
|
+
output.name(self)
|
40
|
+
output.attribute(:input, input)
|
41
|
+
output.attribute(:exception, excpt.class)
|
42
|
+
output.attribute(:exception_message, excpt.message)
|
43
|
+
output.nest(:context, context)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Ducktrap
|
2
|
+
|
3
|
+
# Abstract base class for transform evaluator
|
4
|
+
class Evaluator
|
5
|
+
include AbstractType, Adamantium::Flat, PrettyDump, Concord::Public.new(:context, :input)
|
6
|
+
|
7
|
+
# Test if conversion was successful
|
8
|
+
#
|
9
|
+
# @return [true]
|
10
|
+
# if conversion was successful
|
11
|
+
#
|
12
|
+
# @return [false]
|
13
|
+
# otherwise
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
#
|
17
|
+
def successful?
|
18
|
+
!output.kind_of?(Error)
|
19
|
+
end
|
20
|
+
memoize :successful?
|
21
|
+
|
22
|
+
alias_method :success?, :successful?
|
23
|
+
|
24
|
+
# Assert evaluator is successful
|
25
|
+
#
|
26
|
+
# @return [self]
|
27
|
+
# if successful
|
28
|
+
#
|
29
|
+
# @raise [FailedTransformationError]
|
30
|
+
# otherwise
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
#
|
34
|
+
def assert_successful
|
35
|
+
unless successful?
|
36
|
+
raise FailedTransformationError.new(self)
|
37
|
+
end
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return output
|
42
|
+
#
|
43
|
+
# @return [Object]
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
#
|
47
|
+
attr_reader :output
|
48
|
+
|
49
|
+
# Return output
|
50
|
+
#
|
51
|
+
# @return [Object]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
#
|
55
|
+
def output
|
56
|
+
process
|
57
|
+
end
|
58
|
+
memoize :output
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# Calculate result
|
63
|
+
#
|
64
|
+
# @return [Object]
|
65
|
+
# if successful
|
66
|
+
#
|
67
|
+
# @return [Error]
|
68
|
+
# otherwise
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
#
|
72
|
+
abstract_method :process
|
73
|
+
private :process
|
74
|
+
|
75
|
+
# Return error
|
76
|
+
#
|
77
|
+
# @return [Error]
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
#
|
81
|
+
def error
|
82
|
+
Error.new(context, input)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return error with message
|
86
|
+
#
|
87
|
+
# @param [Error::Message]
|
88
|
+
#
|
89
|
+
# @return [Error]
|
90
|
+
#
|
91
|
+
# @api private
|
92
|
+
#
|
93
|
+
def exception(exception)
|
94
|
+
Error::Exception.new(context, input, exception)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Return nested error
|
98
|
+
#
|
99
|
+
# @return [Error::Nested]
|
100
|
+
#
|
101
|
+
# @api private
|
102
|
+
#
|
103
|
+
def nested_error(error)
|
104
|
+
Error.new(error, input)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Dump object
|
108
|
+
#
|
109
|
+
# @param [Formatter] formatter
|
110
|
+
#
|
111
|
+
# @return [undefined]
|
112
|
+
#
|
113
|
+
# @api private
|
114
|
+
#
|
115
|
+
def dump(formatter)
|
116
|
+
formatter.name(self)
|
117
|
+
formatter.attribute(:input, input)
|
118
|
+
if successful?
|
119
|
+
formatter.attribute(:output, output)
|
120
|
+
else
|
121
|
+
formatter.nest(:error, output)
|
122
|
+
end
|
123
|
+
formatter.nest(:context, context)
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Ducktrap
|
2
|
+
class Evaluator
|
3
|
+
# Evaluator that always returns errors
|
4
|
+
class Invalid < self
|
5
|
+
|
6
|
+
# Return output
|
7
|
+
#
|
8
|
+
# @return [Evaluator]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
#
|
12
|
+
attr_reader :output
|
13
|
+
|
14
|
+
# Initialize object
|
15
|
+
#
|
16
|
+
# @param [Ducktra] context
|
17
|
+
# @param [Object] input
|
18
|
+
#
|
19
|
+
# @return [undefined]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
#
|
23
|
+
def initialize(_context, _input)
|
24
|
+
super
|
25
|
+
@output = error
|
26
|
+
end
|
27
|
+
|
28
|
+
end # Invalid
|
29
|
+
end # Evaluator
|
30
|
+
end # Ducktrap
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Ducktrap
|
2
|
+
class Evaluator
|
3
|
+
|
4
|
+
# Static evaluator with input as output
|
5
|
+
class Noop < self
|
6
|
+
|
7
|
+
# Return output
|
8
|
+
#
|
9
|
+
# @return [Object]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
def output
|
14
|
+
input
|
15
|
+
end
|
16
|
+
|
17
|
+
end # Noop
|
18
|
+
|
19
|
+
# Static evaluator with static output
|
20
|
+
class Static < Noop
|
21
|
+
include Concord::Public.new(:context, :input, :output)
|
22
|
+
end # Static
|
23
|
+
|
24
|
+
end # Evaluator
|
25
|
+
end # Ducktrap
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Ducktrap
|
2
|
+
# Printer for ducktrap trees
|
3
|
+
class Formatter
|
4
|
+
include Adamantium::Flat, Equalizer.new(:output, :level)
|
5
|
+
|
6
|
+
# Return output
|
7
|
+
#
|
8
|
+
# @return [IO]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
#
|
12
|
+
attr_reader :output
|
13
|
+
protected :output
|
14
|
+
|
15
|
+
# Return indent
|
16
|
+
#
|
17
|
+
# @return [Fixnum]
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
#
|
21
|
+
attr_reader :level
|
22
|
+
protected :level
|
23
|
+
|
24
|
+
# Initialize formatter
|
25
|
+
#
|
26
|
+
# @param [IO] output
|
27
|
+
# @param [Fixnum] indent
|
28
|
+
#
|
29
|
+
# @return [undefined]
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
#
|
33
|
+
def initialize(output = $stderr, level = 0)
|
34
|
+
@output, @level = output, level
|
35
|
+
end
|
36
|
+
|
37
|
+
# Write name of class of object
|
38
|
+
#
|
39
|
+
# @param [Object] object
|
40
|
+
#
|
41
|
+
# @return [self]
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
#
|
45
|
+
def name(object)
|
46
|
+
puts(object.class.name)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Write nest with label
|
50
|
+
#
|
51
|
+
# @param [String] label
|
52
|
+
# @param [#pretty_dump] nested
|
53
|
+
#
|
54
|
+
# @return [self]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
#
|
58
|
+
def nest(label, nested)
|
59
|
+
indented = indent
|
60
|
+
indented.puts("#{label}:")
|
61
|
+
nested.pretty_dump(indented.indent)
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
# Write labeled value
|
66
|
+
#
|
67
|
+
# @param [#to_str] name
|
68
|
+
# @param [#inspect] value
|
69
|
+
#
|
70
|
+
# @return [self]
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
#
|
74
|
+
def attribute(name, value)
|
75
|
+
indent.puts("#{name}: #{value.inspect}")
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
# Write body
|
80
|
+
#
|
81
|
+
# @param [Enumerable<#pretty_dump>] body
|
82
|
+
#
|
83
|
+
# @return [self]
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
#
|
87
|
+
def body(body)
|
88
|
+
indented = indent
|
89
|
+
indented.puts('body:')
|
90
|
+
body.each do |member|
|
91
|
+
member.pretty_dump(indented.indent)
|
92
|
+
end
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
# Write string with indentation
|
97
|
+
#
|
98
|
+
# @param [String] string
|
99
|
+
#
|
100
|
+
# @return [self]
|
101
|
+
#
|
102
|
+
# @api private
|
103
|
+
#
|
104
|
+
def puts(string)
|
105
|
+
util = output
|
106
|
+
util.write(prefix)
|
107
|
+
util.puts(string)
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
# Return formatter with deeper indentation indent
|
112
|
+
#
|
113
|
+
# @return [Formatter]
|
114
|
+
#
|
115
|
+
# @api private
|
116
|
+
#
|
117
|
+
def indent
|
118
|
+
self.class.new(output, level+1)
|
119
|
+
end
|
120
|
+
memoize :indent
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
INDENT = ' '.freeze
|
125
|
+
|
126
|
+
# Return prefix at current indentation
|
127
|
+
#
|
128
|
+
# @return [String]
|
129
|
+
#
|
130
|
+
# @api private
|
131
|
+
#
|
132
|
+
def prefix
|
133
|
+
INDENT * level
|
134
|
+
end
|
135
|
+
memoize :prefix
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Ducktrap
|
2
|
+
# An object holding references to two inverse ducktraps
|
3
|
+
class Mapper
|
4
|
+
include Concord::Public.new(:loader, :dumper)
|
5
|
+
|
6
|
+
# Build mapper
|
7
|
+
#
|
8
|
+
# @return [Mapper]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
#
|
12
|
+
def self.build(&block)
|
13
|
+
Builder.new(self, &block).object
|
14
|
+
end
|
15
|
+
|
16
|
+
# Builder for mapper
|
17
|
+
class Builder < Ducktrap::Builder
|
18
|
+
|
19
|
+
# Capture loader block
|
20
|
+
#
|
21
|
+
# @return [self]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
def loader(&block)
|
26
|
+
@loader = Ducktrap.build(&block)
|
27
|
+
@dumper ||= @loader.inverse
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
# Capture dumper block
|
32
|
+
#
|
33
|
+
# @return [self]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
def dumper(&block)
|
38
|
+
@dumper = Ducktrap.build(&block)
|
39
|
+
@loader ||= @dumper.inverse
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return mapper
|
44
|
+
#
|
45
|
+
# @return [Mapper]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
#
|
49
|
+
def object
|
50
|
+
unless @loader or @dumper
|
51
|
+
raise 'Did not specify loader or dumper or both'
|
52
|
+
end
|
53
|
+
|
54
|
+
klass.new(@loader, @dumper)
|
55
|
+
end
|
56
|
+
|
57
|
+
end # Builder
|
58
|
+
end # Mapper
|
59
|
+
end # Ducktrap
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Ducktrap
|
2
|
+
# Mixin for Nary ducktraps
|
3
|
+
module Nary
|
4
|
+
|
5
|
+
# Builder for nary ducktraps
|
6
|
+
class Builder < Ducktrap::Builder
|
7
|
+
|
8
|
+
# Add ducktrap argument
|
9
|
+
#
|
10
|
+
# @param [Ducktrap] ducktrap
|
11
|
+
#
|
12
|
+
# @return [self]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
def add(ducktrap)
|
17
|
+
body << ducktrap
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
# Hook called when method is missing
|
22
|
+
#
|
23
|
+
# @return [Object]
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
#
|
27
|
+
def method_missing(name, *arguments, &block)
|
28
|
+
builder = DSL.lookup(name) { super }
|
29
|
+
add(builder.build(*arguments, &block))
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return build instance
|
33
|
+
#
|
34
|
+
# @return [Object]
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
#
|
38
|
+
def object
|
39
|
+
@klass.new(body)
|
40
|
+
end
|
41
|
+
memoize :object
|
42
|
+
|
43
|
+
# Return body
|
44
|
+
#
|
45
|
+
# @return [Enumerable<Ducktrap>]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
#
|
49
|
+
attr_reader :body
|
50
|
+
|
51
|
+
# Initialize object
|
52
|
+
#
|
53
|
+
# @param [Class] klass
|
54
|
+
# the klass to build
|
55
|
+
#
|
56
|
+
# @param [Enumerable<Ducktrap>] body
|
57
|
+
# the body of nary ducktrap
|
58
|
+
#
|
59
|
+
# @api private
|
60
|
+
#
|
61
|
+
def initialize(klass, body = [])
|
62
|
+
@body = body
|
63
|
+
super(klass)
|
64
|
+
end
|
65
|
+
|
66
|
+
end # Builder
|
67
|
+
|
68
|
+
module ClassMethods
|
69
|
+
|
70
|
+
# Build ducktrap
|
71
|
+
#
|
72
|
+
# @return [Ducktrap]
|
73
|
+
#
|
74
|
+
# @api private
|
75
|
+
#
|
76
|
+
def build(*arguments, &block)
|
77
|
+
Builder.new(self, *arguments, &block).object
|
78
|
+
end
|
79
|
+
|
80
|
+
end # ClassMethods
|
81
|
+
|
82
|
+
module InstanceMethods
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# Return inverse body
|
87
|
+
#
|
88
|
+
# @return [Enumerable<Ducktrap>]
|
89
|
+
#
|
90
|
+
# @api private
|
91
|
+
#
|
92
|
+
def inverse_body
|
93
|
+
body.map(&:inverse).reverse
|
94
|
+
end
|
95
|
+
|
96
|
+
# Dump instance
|
97
|
+
#
|
98
|
+
# @param [Formatter] output
|
99
|
+
#
|
100
|
+
# @return [undefined]
|
101
|
+
#
|
102
|
+
# @api private
|
103
|
+
#
|
104
|
+
def dump(output)
|
105
|
+
output.name(self)
|
106
|
+
output.body(body)
|
107
|
+
end
|
108
|
+
|
109
|
+
end # InstanceMethods
|
110
|
+
|
111
|
+
CONCORD = Concord::Public.new(:body)
|
112
|
+
|
113
|
+
# Hook called when module was included
|
114
|
+
#
|
115
|
+
# @param [Class,Module] scope
|
116
|
+
#
|
117
|
+
# @return [undefined]
|
118
|
+
#
|
119
|
+
# @api private
|
120
|
+
#
|
121
|
+
def self.included(scope)
|
122
|
+
scope.extend(ClassMethods)
|
123
|
+
scope.module_eval do
|
124
|
+
include InstanceMethods
|
125
|
+
include CONCORD
|
126
|
+
end
|
127
|
+
end
|
128
|
+
private_class_method :included
|
129
|
+
|
130
|
+
# Base class for nary evaluators
|
131
|
+
class Evaluator < Ducktrap::Evaluator
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
# Return body
|
136
|
+
#
|
137
|
+
# @return [Enumerable<Ducktrap>]
|
138
|
+
#
|
139
|
+
# @api private
|
140
|
+
#
|
141
|
+
def body
|
142
|
+
context.body
|
143
|
+
end
|
144
|
+
|
145
|
+
end # Evaluator
|
146
|
+
|
147
|
+
end # Nary
|
148
|
+
end # Ducktrap
|