morpher 0.2.6

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of morpher might be problematic. Click here for more details.

Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/.circle.yml +6 -0
  3. data/.gitignore +5 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +8 -0
  6. data/Changelog.md +60 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +20 -0
  9. data/README.md +56 -0
  10. data/Rakefile +95 -0
  11. data/circle.yml +7 -0
  12. data/config/devtools.yml +2 -0
  13. data/config/flay.yml +3 -0
  14. data/config/flog.yml +2 -0
  15. data/config/heckle.yml +3 -0
  16. data/config/mutant.yml +8 -0
  17. data/config/reek.yml +109 -0
  18. data/config/rubocop.yml +138 -0
  19. data/config/yardstick.yml +2 -0
  20. data/examples/README.md +13 -0
  21. data/examples/a.rb +25 -0
  22. data/examples/b.rb +35 -0
  23. data/lib/morpher.rb +111 -0
  24. data/lib/morpher/compiler.rb +17 -0
  25. data/lib/morpher/compiler/emitter.rb +82 -0
  26. data/lib/morpher/compiler/error.rb +84 -0
  27. data/lib/morpher/compiler/evaluator.rb +63 -0
  28. data/lib/morpher/compiler/evaluator/emitter.rb +224 -0
  29. data/lib/morpher/compiler/preprocessor.rb +29 -0
  30. data/lib/morpher/compiler/preprocessor/emitter.rb +54 -0
  31. data/lib/morpher/compiler/preprocessor/emitter/anima.rb +69 -0
  32. data/lib/morpher/compiler/preprocessor/emitter/boolean.rb +31 -0
  33. data/lib/morpher/compiler/preprocessor/emitter/key.rb +87 -0
  34. data/lib/morpher/compiler/preprocessor/emitter/noop.rb +45 -0
  35. data/lib/morpher/compiler/preprocessor/emitter/param.rb +50 -0
  36. data/lib/morpher/evaluation.rb +118 -0
  37. data/lib/morpher/evaluator.rb +40 -0
  38. data/lib/morpher/evaluator/binary.rb +46 -0
  39. data/lib/morpher/evaluator/nary.rb +97 -0
  40. data/lib/morpher/evaluator/nullary.rb +92 -0
  41. data/lib/morpher/evaluator/nullary/parameterized.rb +48 -0
  42. data/lib/morpher/evaluator/predicate.rb +22 -0
  43. data/lib/morpher/evaluator/predicate/boolean.rb +76 -0
  44. data/lib/morpher/evaluator/predicate/contradiction.rb +36 -0
  45. data/lib/morpher/evaluator/predicate/eql.rb +50 -0
  46. data/lib/morpher/evaluator/predicate/negation.rb +52 -0
  47. data/lib/morpher/evaluator/predicate/primitive.rb +49 -0
  48. data/lib/morpher/evaluator/predicate/tautology.rb +36 -0
  49. data/lib/morpher/evaluator/transformer.rb +75 -0
  50. data/lib/morpher/evaluator/transformer/attribute.rb +25 -0
  51. data/lib/morpher/evaluator/transformer/block.rb +81 -0
  52. data/lib/morpher/evaluator/transformer/coerce.rb +166 -0
  53. data/lib/morpher/evaluator/transformer/custom.rb +34 -0
  54. data/lib/morpher/evaluator/transformer/domain.rb +86 -0
  55. data/lib/morpher/evaluator/transformer/domain/attribute_accessors.rb +60 -0
  56. data/lib/morpher/evaluator/transformer/domain/attribute_hash.rb +52 -0
  57. data/lib/morpher/evaluator/transformer/domain/instance_variables.rb +60 -0
  58. data/lib/morpher/evaluator/transformer/domain/param.rb +54 -0
  59. data/lib/morpher/evaluator/transformer/guard.rb +62 -0
  60. data/lib/morpher/evaluator/transformer/hash_transform.rb +149 -0
  61. data/lib/morpher/evaluator/transformer/input.rb +37 -0
  62. data/lib/morpher/evaluator/transformer/key.rb +86 -0
  63. data/lib/morpher/evaluator/transformer/map.rb +100 -0
  64. data/lib/morpher/evaluator/transformer/merge.rb +25 -0
  65. data/lib/morpher/evaluator/transformer/static.rb +27 -0
  66. data/lib/morpher/evaluator/unary.rb +79 -0
  67. data/lib/morpher/node_helpers.rb +19 -0
  68. data/lib/morpher/printer.rb +233 -0
  69. data/lib/morpher/printer/mixin.rb +58 -0
  70. data/lib/morpher/registry.rb +51 -0
  71. data/lib/morpher/type_lookup.rb +51 -0
  72. data/morpher.gemspec +29 -0
  73. data/spec/integration_spec.rb +184 -0
  74. data/spec/rcov.opts +7 -0
  75. data/spec/shared/evaluator_behavior.rb +155 -0
  76. data/spec/spec_helper.rb +36 -0
  77. data/spec/support/ice_nine_config.rb +8 -0
  78. data/spec/support/let_mock_helper.rb +8 -0
  79. data/spec/support/strip_helper.rb +12 -0
  80. data/spec/unit/morpher/compiler/preprocessor_spec.rb +46 -0
  81. data/spec/unit/morpher/evaluator/nullary/parameterized_spec.rb +25 -0
  82. data/spec/unit/morpher/evaluator/predicate/boolean/and_spec.rb +11 -0
  83. data/spec/unit/morpher/evaluator/predicate/boolean/or_spec.rb +26 -0
  84. data/spec/unit/morpher/evaluator/predicate/boolean/xor_spec.rb +26 -0
  85. data/spec/unit/morpher/evaluator/predicate/contrandiction_spec.rb +7 -0
  86. data/spec/unit/morpher/evaluator/predicate/eql_spec.rb +11 -0
  87. data/spec/unit/morpher/evaluator/predicate/negation_spec.rb +10 -0
  88. data/spec/unit/morpher/evaluator/predicate/primitive_spec.rb +17 -0
  89. data/spec/unit/morpher/evaluator/predicate/tautology_spec.rb +7 -0
  90. data/spec/unit/morpher/evaluator/transformer/attribute_spec.rb +9 -0
  91. data/spec/unit/morpher/evaluator/transformer/block_spec.rb +92 -0
  92. data/spec/unit/morpher/evaluator/transformer/coerce/parse_int_spec.rb +23 -0
  93. data/spec/unit/morpher/evaluator/transformer/custom_spec.rb +13 -0
  94. data/spec/unit/morpher/evaluator/transformer/domain/attribute_accessors_spec.rb +48 -0
  95. data/spec/unit/morpher/evaluator/transformer/domain/attribute_hash_spec.rb +40 -0
  96. data/spec/unit/morpher/evaluator/transformer/domain/instance_variables_spec.rb +47 -0
  97. data/spec/unit/morpher/evaluator/transformer/guard_spec.rb +12 -0
  98. data/spec/unit/morpher/evaluator/transformer/hash_transform_spec.rb +47 -0
  99. data/spec/unit/morpher/evaluator/transformer/input_spec.rb +11 -0
  100. data/spec/unit/morpher/evaluator/transformer/map_spec.rb +25 -0
  101. data/spec/unit/morpher/evaluator/transformer/static_spec.rb +10 -0
  102. data/spec/unit/morpher/evaluator_spec.rb +15 -0
  103. data/spec/unit/morpher/printer_spec.rb +21 -0
  104. data/spec/unit/morpher/registry_spec.rb +11 -0
  105. data/spec/unit/morpher_spec.rb +53 -0
  106. metadata +302 -0
@@ -0,0 +1,34 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer < self
4
+ # Custom transformer with external injected behavior
5
+ class Custom < self
6
+ include Nullary::Parameterized, Transitive
7
+ register :custom
8
+
9
+ # Call transformer with input
10
+ #
11
+ # @param [Object]
12
+ #
13
+ # @return [undefinedo]
14
+ #
15
+ # @api private
16
+ #
17
+ def call(input)
18
+ param.first.call(input)
19
+ end
20
+
21
+ # Return inverse transformer
22
+ #
23
+ # @return [Evaluator::Transformer]
24
+ #
25
+ # @api private
26
+ #
27
+ def inverse
28
+ self.class.new(param.reverse)
29
+ end
30
+
31
+ end # Custom
32
+ end # Transformer
33
+ end # Evaluator
34
+ end # Morpher
@@ -0,0 +1,86 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+ # Abstract namespace class for transformers from/to domain
5
+ class Domain < self
6
+ include AbstractType, Nullary::Parameterized, Transitive
7
+
8
+ private
9
+
10
+ # Call block with param attributes
11
+ #
12
+ # @param [Object] aggregate
13
+ #
14
+ # @return [Object]
15
+ #
16
+ # @api private
17
+ #
18
+ def transform(aggregate, &block)
19
+ param.attributes.each_with_object(aggregate, &block)
20
+ end
21
+
22
+ # Mixin for domain dumpers
23
+ module Dump
24
+
25
+ # Return inverse evaluator
26
+ #
27
+ # @return [Evaluator]
28
+ #
29
+ # @api private
30
+ #
31
+ def inverse
32
+ self.class::Load.new(param)
33
+ end
34
+
35
+ private
36
+
37
+ # Dump object
38
+ #
39
+ # @param [Symbol] left
40
+ # @param [Symbol] right
41
+ #
42
+ # @return [Object]
43
+ #
44
+ # @api private
45
+ #
46
+ def dump(&block)
47
+ transform({}, &block)
48
+ end
49
+
50
+ end # Dump
51
+
52
+ # Mixin for domain loaders
53
+ module Load
54
+ include AbstractType
55
+
56
+ # Return inverse evaluator
57
+ #
58
+ # @return [Evaluator]
59
+ #
60
+ # @api private
61
+ #
62
+ def inverse
63
+ self.class::Dump.new(param)
64
+ end
65
+
66
+ private
67
+
68
+ # Load object
69
+ #
70
+ # @param [Symbol] left
71
+ # @param [Symbol] right
72
+ #
73
+ # @return [Object]
74
+ #
75
+ # @api private
76
+ #
77
+ def load(&block)
78
+ transform(param.model.allocate, &block)
79
+ end
80
+
81
+ end # Load
82
+
83
+ end # Domain
84
+ end # Transformer
85
+ end # Evaluator
86
+ end # Morpher
@@ -0,0 +1,60 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+ class Domain
5
+ # Abstract namespace class for domain objects via attribute accessors
6
+ class AttributeAccessors < self
7
+ include AbstractType
8
+
9
+ # Evaluator for dumping domain objects via instance variables
10
+ class Dump < self
11
+ include Domain::Dump
12
+
13
+ register :dump_attribute_accessors
14
+
15
+ # Call evaluator
16
+ #
17
+ # @param [Object] input
18
+ #
19
+ # @return [Hash<Symbol, Object>]
20
+ #
21
+ # @api private
22
+ #
23
+ def call(input)
24
+ dump do |attribute, attributes|
25
+ name = attribute.name
26
+ attributes[name] = input.public_send(name)
27
+ end
28
+ end
29
+
30
+ end # Dump
31
+
32
+ # Evaluator for loading domain objects via attributes hash
33
+ class Load < self
34
+ include Domain::Load
35
+
36
+ register :load_attribute_accessors
37
+
38
+ # Call evaluator
39
+ #
40
+ # @param [Object] input
41
+ #
42
+ # @return [Object]
43
+ #
44
+ # @api private
45
+ #
46
+ def call(input)
47
+ load do |attribute, object|
48
+ object.public_send(
49
+ attribute.writer,
50
+ input.fetch(attribute.name)
51
+ )
52
+ end
53
+ end
54
+
55
+ end # Load
56
+ end # Domain
57
+ end # Anima
58
+ end # Transformer
59
+ end # Evaluator
60
+ end # Morpher
@@ -0,0 +1,52 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+ class Domain < self
5
+ # Abstract namespace class for domain objects on attributes hash
6
+ class AttributeHash < self
7
+ include AbstractType
8
+
9
+ # Evaluator for dumping domain objects via attributes hash
10
+ class Dump < self
11
+ include Domain::Dump
12
+
13
+ register :dump_attribute_hash
14
+
15
+ # Call evaluator
16
+ #
17
+ # @param [Object] input
18
+ #
19
+ # @return [Hash<Symbol, Object>]
20
+ #
21
+ # @api private
22
+ #
23
+ def call(input)
24
+ input.to_h
25
+ end
26
+
27
+ end # Dump
28
+
29
+ # Evaluator for loading domain objects via attributes hash
30
+ class Load < self
31
+ include Domain::Load
32
+
33
+ register :load_attribute_hash
34
+
35
+ # Call evaluator
36
+ #
37
+ # @param [Object] input
38
+ #
39
+ # @return [Object]
40
+ #
41
+ # @api private
42
+ #
43
+ def call(input)
44
+ param.model.new(input)
45
+ end
46
+
47
+ end # Load
48
+ end # Domain
49
+ end # Anima
50
+ end # Transformer
51
+ end # Evaluator
52
+ end # Morpher
@@ -0,0 +1,60 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+ class Domain < self
5
+ # Abstract namespace class for domain objects via instance variables
6
+ class InstanceVariables < self
7
+ include AbstractType
8
+
9
+ # Evaluator for dumping domain objects via instance variables
10
+ class Dump < self
11
+ include Domain::Dump
12
+
13
+ register :dump_instance_variables
14
+
15
+ # Call evaluator
16
+ #
17
+ # @param [Object] input
18
+ #
19
+ # @return [Hash<Symbol, Object>]
20
+ #
21
+ # @api private
22
+ #
23
+ def call(input)
24
+ dump do |attribute, attributes|
25
+ attributes[attribute.name] =
26
+ input.instance_variable_get(attribute.ivar_name)
27
+ end
28
+ end
29
+
30
+ end # Dump
31
+
32
+ # Evaluator for loading domain objects via instance variables
33
+ class Load < self
34
+ include Domain::Load
35
+
36
+ register :load_instance_variables
37
+
38
+ # Call evaluator
39
+ #
40
+ # @param [Object] input
41
+ #
42
+ # @return [Object]
43
+ #
44
+ # @api private
45
+ #
46
+ def call(input)
47
+ load do |attribute, object|
48
+ object.instance_variable_set(
49
+ attribute.ivar_name,
50
+ input.fetch(attribute.name)
51
+ )
52
+ end
53
+ end
54
+
55
+ end # Load
56
+ end # Domain
57
+ end # Anima
58
+ end # Transformer
59
+ end # Evaluator
60
+ end # Morpher
@@ -0,0 +1,54 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+ class Domain < self
5
+
6
+ # Domain specific transformer parameter
7
+ class Param
8
+ include Adamantium, Concord::Public.new(:model, :attribute_names)
9
+
10
+ # Return attributes
11
+ #
12
+ # @return [Enumerable<Attribute>]
13
+ #
14
+ # @api private
15
+ #
16
+ def attributes
17
+ attribute_names.map(&Attribute.method(:new))
18
+ end
19
+ memoize :attributes
20
+
21
+ # Attribute on a domain transformer param
22
+ class Attribute
23
+ include Adamantium, Concord::Public.new(:name)
24
+
25
+ # Return instance variable name
26
+ #
27
+ # @return [Symbol]
28
+ #
29
+ # @api private
30
+ #
31
+ def ivar_name
32
+ :"@#{name}"
33
+ end
34
+ memoize :ivar_name
35
+
36
+ # Return writer name
37
+ #
38
+ # @return [Symbol]
39
+ #
40
+ # @api private
41
+ #
42
+ def writer
43
+ :"#{name}="
44
+ end
45
+ memoize :writer
46
+
47
+ end # Attribute
48
+
49
+ end # Param
50
+
51
+ end # Domain
52
+ end # Transformer
53
+ end # Evaluator
54
+ end # Morpher
@@ -0,0 +1,62 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+
5
+ # Transformer that allows to guard transformation process
6
+ # with a predicate on input
7
+ class Guard < self
8
+ include Unary, Transitive
9
+
10
+ register :guard
11
+
12
+ # Call evaluator
13
+ #
14
+ # @param [Object] input
15
+ #
16
+ # @return [Object]
17
+ # if input evaluates true under predicate
18
+ #
19
+ # @raise [TransformError]
20
+ # otherwise
21
+ #
22
+ # @api private
23
+ #
24
+ def call(input)
25
+ if operand.call(input)
26
+ input
27
+ else
28
+ fail TransformError.new(self, input)
29
+ end
30
+ end
31
+
32
+ # Return evaluation
33
+ #
34
+ # @param [Object] input
35
+ #
36
+ # @return [Evaluation::Guard]
37
+ #
38
+ # @api private
39
+ #
40
+ def evaluation(input)
41
+ operand_evaluation = operand.evaluation(input)
42
+ if operand_evaluation.output
43
+ evaluation_success(input, operand_evaluation, input)
44
+ else
45
+ evaluation_error(input, operand_evaluation)
46
+ end
47
+ end
48
+
49
+ # Return inverse evaluator
50
+ #
51
+ # @return [self]
52
+ #
53
+ # @api private
54
+ #
55
+ def inverse
56
+ self
57
+ end
58
+
59
+ end # Guard
60
+ end # Transformer
61
+ end # Evaluator
62
+ end # Morpher
@@ -0,0 +1,149 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+
5
+ # Too complex hash transformation evaluator
6
+ #
7
+ # FIXME: Should be broken up in better
8
+ # primitives a decompose, compose pair
9
+ #
10
+ # @api private
11
+ #
12
+ class HashTransform < self
13
+ include Nary
14
+
15
+ register :hash_transform
16
+
17
+ # Test if evaluator is transitive
18
+ #
19
+ # FIXME: Needs to be calculated dynamically
20
+ #
21
+ # @return [true]
22
+ # if evaluator is transitive
23
+ #
24
+ # @return [false]
25
+ # otherwise
26
+ #
27
+ # @api private
28
+ #
29
+ def transitive?
30
+ body.all?(&self.class.method(:transitive_keypair?))
31
+ end
32
+
33
+ # Test if evaluator is a keypair
34
+ #
35
+ # FIXME: Refactor the need for this away.
36
+ #
37
+ # This is a side effect from this class is
38
+ # generally to big in sense of SRP.
39
+ # Must be refactorable away. But dunno now.
40
+ # Still exploring.
41
+ #
42
+ # @param [Evaluator]
43
+ #
44
+ # @api private
45
+ #
46
+ def self.transitive_keypair?(evaluator)
47
+ return false unless evaluator.kind_of?(Block)
48
+
49
+ body = evaluator.body
50
+
51
+ left, operator, right = body if body.length.equal?(3)
52
+
53
+ left.kind_of?(Key::Fetch) &&
54
+ right.kind_of?(Key::Dump) &&
55
+ operator.transitive?
56
+ end
57
+
58
+ # Call evaluator
59
+ #
60
+ # @param [Object] input
61
+ #
62
+ # @return [Object]
63
+ #
64
+ # @api private
65
+ #
66
+ def call(input)
67
+ content = body.map do |node|
68
+ node.call(input)
69
+ end
70
+ Hash[content]
71
+ end
72
+
73
+ # Return inverse evaluator
74
+ #
75
+ # @return [HashTransform]
76
+ #
77
+ # @api private
78
+ #
79
+ def inverse
80
+ self.class.new(body.map(&:inverse))
81
+ end
82
+
83
+ # Return evaluation
84
+ #
85
+ # @param [Input]
86
+ #
87
+ # @return [Evaluation::Nary]
88
+ #
89
+ # @api private
90
+ #
91
+ # rubocop:disable MethodLength
92
+ #
93
+ def evaluation(input)
94
+ evaluations = body.each_with_object([]) do |evaluator, aggregate|
95
+ evaluation = evaluator.evaluation(input)
96
+ aggregate << evaluation
97
+ unless evaluation.success?
98
+ return evaluation_error(input, aggregate)
99
+ end
100
+ end
101
+
102
+ output = Hash[evaluations.map(&:output)]
103
+
104
+ Evaluation::Nary.success(
105
+ evaluator: self,
106
+ input: input,
107
+ evaluations: evaluations,
108
+ output: output
109
+ )
110
+ end
111
+
112
+ private
113
+
114
+ # Return evaluation error
115
+ #
116
+ # @return [Object] input
117
+ #
118
+ # @return [Array<Evaluations>] evaluations
119
+ #
120
+ # @api private
121
+ #
122
+ def evaluation_error(input, evaluations)
123
+ Evaluation::Nary.error(
124
+ evaluator: self,
125
+ input: input,
126
+ evaluations: evaluations
127
+ )
128
+ end
129
+
130
+ # Build evaluator from node
131
+ #
132
+ # @param [Compiler] compiler
133
+ # @param [Node] node
134
+ #
135
+ # @return [Evaluator]
136
+ #
137
+ # @api private
138
+ #
139
+ def self.build(compiler, node)
140
+ body = node.children.map do |child|
141
+ compiler.call(child)
142
+ end
143
+ new(body)
144
+ end
145
+
146
+ end # HashTransform
147
+ end # Transformer
148
+ end # Evaluator
149
+ end # Morpher