calificador 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +35 -16
- data/TODO.md +16 -0
- data/calificador.gemspec +54 -0
- data/lib/calificador.rb +8 -4
- data/lib/calificador/assert.rb +15 -0
- data/lib/calificador/assertor.rb +79 -35
- data/lib/calificador/build/attribute_evaluator.rb +34 -30
- data/lib/calificador/build/basic_factory.rb +195 -0
- data/lib/calificador/build/mock_factory.rb +151 -0
- data/lib/calificador/build/object_factory.rb +85 -0
- data/lib/calificador/build/trait.rb +0 -20
- data/lib/calificador/context/basic_context.rb +406 -0
- data/lib/calificador/context/class_method_context.rb +0 -0
- data/lib/calificador/{spec → context}/condition_context.rb +1 -3
- data/lib/calificador/{spec/type_context.rb → context/instance_context.rb} +5 -10
- data/lib/calificador/context/operation_context.rb +27 -0
- data/lib/calificador/context/override/argument_override.rb +73 -0
- data/lib/calificador/context/override/basic_override.rb +14 -0
- data/lib/calificador/context/override/factory_override.rb +31 -0
- data/lib/calificador/context/override/property_override.rb +61 -0
- data/lib/calificador/context/test_environment.rb +283 -0
- data/lib/calificador/{spec → context}/test_method.rb +2 -31
- data/lib/calificador/{spec → context}/test_root.rb +3 -15
- data/lib/calificador/{spec/examine_context.rb → context/type_context.rb} +7 -10
- data/lib/calificador/key.rb +27 -15
- data/lib/calificador/minitest/minitest_patches.rb +0 -2
- data/lib/calificador/test.rb +1 -3
- data/lib/calificador/test_mixin.rb +143 -139
- data/lib/calificador/util/call_formatter.rb +5 -5
- data/lib/calificador/util/core_extensions.rb +104 -79
- data/lib/calificador/util/proxy_object.rb +63 -0
- data/lib/calificador/version.rb +1 -1
- metadata +22 -42
- data/lib/calificador/build/attribute_container.rb +0 -103
- data/lib/calificador/build/factory.rb +0 -132
- data/lib/calificador/spec/basic_context.rb +0 -353
- data/lib/calificador/spec/class_method_context.rb +0 -42
- data/lib/calificador/spec/instance_method_context.rb +0 -38
- data/lib/calificador/spec/test_environment.rb +0 -141
- data/lib/calificador/spec/value_override.rb +0 -37
@@ -0,0 +1,195 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Calificador
|
4
|
+
module Build
|
5
|
+
# Factory calss
|
6
|
+
class BasicFactory
|
7
|
+
# Configuration derived factories
|
8
|
+
class Dsl < Util::ProxyObject
|
9
|
+
attr_reader :__factory
|
10
|
+
|
11
|
+
def initialize(factory:)
|
12
|
+
super()
|
13
|
+
|
14
|
+
@__factory = factory
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_attribute(name, **properties, &config)
|
18
|
+
type ||= __default_property_type(name: name)
|
19
|
+
attribute = Attribute.new(name: name, type: type, config: config)
|
20
|
+
@__factory.add_attribute(attribute)
|
21
|
+
end
|
22
|
+
|
23
|
+
def init_with(&block)
|
24
|
+
raise "Initializer requires a block to create the object" if block.nil?
|
25
|
+
|
26
|
+
@__factory.init_with = block
|
27
|
+
end
|
28
|
+
|
29
|
+
def before_create(&block)
|
30
|
+
raise "Before requires a block to call" if block.nil?
|
31
|
+
|
32
|
+
@__factory.before_create = block
|
33
|
+
end
|
34
|
+
|
35
|
+
def after_create(&block)
|
36
|
+
raise "After requires a block to call" if block.nil?
|
37
|
+
|
38
|
+
@__factory.after_create = block
|
39
|
+
end
|
40
|
+
|
41
|
+
def trait(trait, description = nil, &block)
|
42
|
+
factory = __create_factory(trait: trait, description: description, source_location: block.source_location)
|
43
|
+
factory.dsl.instance_exec(&block)
|
44
|
+
@__factory.context.add_factory(factory)
|
45
|
+
end
|
46
|
+
|
47
|
+
def singleton_method_added(name) # rubocop:disable Lint/MissingSuper
|
48
|
+
::Kernel.raise "Adding methods (#{name}) inside factory definitions is not supported"
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def __respond_to_missing?(name:, include_all:)
|
54
|
+
name.start_with?("__") ? super : true
|
55
|
+
end
|
56
|
+
|
57
|
+
def __method_missing(name:, arguments:, keywords:, block:)
|
58
|
+
if name.start_with?("__")
|
59
|
+
super
|
60
|
+
else
|
61
|
+
unless arguments.empty?
|
62
|
+
::Kernel.raise ::ArgumentError, <<~ERROR
|
63
|
+
Attribute '#{name}' cannot have arguments. Please use a block to configure the value
|
64
|
+
ERROR
|
65
|
+
end
|
66
|
+
|
67
|
+
::Kernel.raise ::ArgumentError, "Attribute '#{name}' must have a block to provide the value" if block.nil?
|
68
|
+
|
69
|
+
add_attribute(name, &block)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def __default_trait_description(trait:)
|
74
|
+
trait.to_s.gsub("_", " ").chomp
|
75
|
+
end
|
76
|
+
|
77
|
+
def __create_factory(trait:, description:, source_location:)
|
78
|
+
raise NotImplementedError, "Subclasses must implement"
|
79
|
+
end
|
80
|
+
|
81
|
+
def __default_property_type(name:)
|
82
|
+
raise NotImplementedError, "Subclasses must implement"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
attr_reader :parent, :description, :key, :context, :name, :source_location
|
87
|
+
attr_accessor :init_with, :before_create, :after_create
|
88
|
+
|
89
|
+
def initialize(context:, parent: nil, key:, name:, description: nil, source_location:)
|
90
|
+
raise "Parent factory must have same type" unless parent.nil? || parent.key.type == key.type
|
91
|
+
|
92
|
+
@context = context
|
93
|
+
@key = key
|
94
|
+
@name = name.to_sym
|
95
|
+
@source_location = source_location
|
96
|
+
@parent = parent
|
97
|
+
@description = description.dup.freeze
|
98
|
+
@attributes = {}
|
99
|
+
@init_with = nil
|
100
|
+
@before_create = nil
|
101
|
+
@after_create = nil
|
102
|
+
end
|
103
|
+
|
104
|
+
def create(environment:)
|
105
|
+
evaluator = AttributeEvaluator.new(key: @key, environment: environment)
|
106
|
+
|
107
|
+
collect_attributes(evaluator: evaluator)
|
108
|
+
|
109
|
+
exec_before_create(evaluator: evaluator)
|
110
|
+
|
111
|
+
object = create_object(evaluator: evaluator)
|
112
|
+
|
113
|
+
set_properties(object: object, evaluator: evaluator)
|
114
|
+
|
115
|
+
exec_after_create(evaluator: evaluator, object: object)
|
116
|
+
|
117
|
+
object
|
118
|
+
end
|
119
|
+
|
120
|
+
def attributes
|
121
|
+
@attributes.dup.freeze
|
122
|
+
end
|
123
|
+
|
124
|
+
def attribute(name:)
|
125
|
+
@attributes[name]
|
126
|
+
end
|
127
|
+
|
128
|
+
def add_attribute(attribute)
|
129
|
+
raise KeyError, "Duplicate attribute name #{attribute.name}" if @attributes.key?(attribute.name)
|
130
|
+
|
131
|
+
@attributes[attribute.name] = attribute
|
132
|
+
end
|
133
|
+
|
134
|
+
def add_overrides(overrides)
|
135
|
+
overrides.each do |name, value|
|
136
|
+
current_attribute = @parent&.lookup_attribute(name: name)
|
137
|
+
|
138
|
+
attribute = Attribute.new(name: name, type: current_attribute&.type || :property, config: value)
|
139
|
+
add_attribute(attribute)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def lookup_attribute(name:)
|
144
|
+
@attributes.fetch(name) do
|
145
|
+
@parent&.lookup_attribute(name: name)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def dsl
|
150
|
+
self.class.const_get(:Dsl).new(factory: self)
|
151
|
+
end
|
152
|
+
|
153
|
+
protected
|
154
|
+
|
155
|
+
def collect_attributes(evaluator:)
|
156
|
+
@parent&.collect_attributes(evaluator: evaluator)
|
157
|
+
evaluator.add_attributes(@attributes.values)
|
158
|
+
end
|
159
|
+
|
160
|
+
def exec_before_create(evaluator:)
|
161
|
+
@parent&.exec_before_create(evaluator: evaluator)
|
162
|
+
evaluator.evaluate(&@before_create) unless @before_create.nil?
|
163
|
+
end
|
164
|
+
|
165
|
+
def exec_after_create(evaluator:, object:)
|
166
|
+
@parent&.exec_after_create(evaluator: evaluator, object: object)
|
167
|
+
evaluator.evaluate(object, &@after_create) unless @after_create.nil?
|
168
|
+
end
|
169
|
+
|
170
|
+
def nearest_init_with
|
171
|
+
@init_with || @parent&.nearest_init_with
|
172
|
+
end
|
173
|
+
|
174
|
+
def create_object(evaluator:)
|
175
|
+
init_with = nearest_init_with
|
176
|
+
|
177
|
+
if init_with.nil?
|
178
|
+
instantiate_object(evaluator: evaluator)
|
179
|
+
else
|
180
|
+
evaluator.evaluate(&init_with)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def set_properties(object:, evaluator:)
|
185
|
+
evaluator.attributes.each_value do |attribute|
|
186
|
+
object.send(:"#{attribute.name}=", evaluator.value(name: attribute.name)) if attribute.type == :property
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def instantiate_object(evaluator:)
|
191
|
+
raise "Cannot instantiate #{@key} without init function"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Calificador
|
4
|
+
module Build
|
5
|
+
# Factory for mock objects
|
6
|
+
class MockFactory < BasicFactory
|
7
|
+
# Configuration derived factories
|
8
|
+
class Dsl < BasicFactory::Dsl
|
9
|
+
def expect(&block)
|
10
|
+
__factory.expect = block
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def __create_factory(trait:, description:, source_location:)
|
16
|
+
MockFactory.new(
|
17
|
+
context: __factory.context,
|
18
|
+
parent: __factory,
|
19
|
+
key: __factory.key.with(trait),
|
20
|
+
name: [__factory.name, trait].compact.join("_"),
|
21
|
+
source_location: source_location,
|
22
|
+
description: description || __default_trait_description(trait: trait)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def __default_property_type(name:)
|
27
|
+
:transient
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class ExpectProxy < Util::ProxyObject
|
32
|
+
def initialize(mock:, evaluator:)
|
33
|
+
super()
|
34
|
+
|
35
|
+
@mock = mock
|
36
|
+
@evaluator = evaluator
|
37
|
+
@environment = @evaluator.environment
|
38
|
+
@test_instance = @environment.test_instance
|
39
|
+
end
|
40
|
+
|
41
|
+
def mock
|
42
|
+
MockProxy.new(mock: @mock)
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def __respond_to_missing?(name:, include_all:)
|
48
|
+
@evaluator.attribute?(name: name) ||
|
49
|
+
!@environment.lookup_named_factory(name: name).nil? ||
|
50
|
+
@test_instance.respond_to?(name, false)
|
51
|
+
end
|
52
|
+
|
53
|
+
def __method_missing(name:, arguments:, keywords:, block:)
|
54
|
+
if @evaluator.attribute?(name: name)
|
55
|
+
@evaluator.value(name: name)
|
56
|
+
else
|
57
|
+
factory = @environment.lookup_named_factory(name: name)
|
58
|
+
|
59
|
+
if factory
|
60
|
+
@environment.create_object(key: factory.key)
|
61
|
+
else
|
62
|
+
@test_instance.send(name, *arguments, **keywords, &block)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class MockProxy < Util::ProxyObject
|
69
|
+
def initialize(mock:)
|
70
|
+
super()
|
71
|
+
|
72
|
+
@mock = mock
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
def __respond_to_missing?(name:, include_all:)
|
78
|
+
METHOD_PATTERN =~ name || super
|
79
|
+
end
|
80
|
+
|
81
|
+
def __method_missing(name:, arguments:, keywords:, block:)
|
82
|
+
if METHOD_PATTERN =~ name
|
83
|
+
MockCall.new(mock: @mock, name: name, arguments: arguments, keywords: keywords, block: block)
|
84
|
+
else
|
85
|
+
super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class MockCall
|
91
|
+
attr_reader :name, :arguments, :keywords, :block
|
92
|
+
|
93
|
+
def initialize(mock:, name:, arguments:, keywords:, block:)
|
94
|
+
@mock = mock
|
95
|
+
@name = name
|
96
|
+
@arguments = arguments
|
97
|
+
@keywords = keywords
|
98
|
+
@block = block
|
99
|
+
end
|
100
|
+
|
101
|
+
def >>(other)
|
102
|
+
@mock.expect(@name, other, combined_arguments)
|
103
|
+
end
|
104
|
+
|
105
|
+
protected
|
106
|
+
|
107
|
+
def combined_arguments
|
108
|
+
arguments = @arguments
|
109
|
+
arguments += [@keywords] unless keywords.empty?
|
110
|
+
arguments
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
attr_accessor :expect
|
115
|
+
|
116
|
+
def initialize(context:, parent: nil, key:, name:, description: nil, source_location:)
|
117
|
+
unless parent.nil?
|
118
|
+
raise "Parent factory must be a #{MockFactory}" unless parent.is_a?(MockFactory)
|
119
|
+
raise "Parent factory must have same type" unless parent.key.type == key.type
|
120
|
+
end
|
121
|
+
|
122
|
+
super(
|
123
|
+
context: context,
|
124
|
+
parent: parent,
|
125
|
+
key: key,
|
126
|
+
name: name,
|
127
|
+
description: description,
|
128
|
+
source_location: source_location
|
129
|
+
)
|
130
|
+
|
131
|
+
@expect = nil
|
132
|
+
end
|
133
|
+
|
134
|
+
def add_attribute(attribute)
|
135
|
+
raise ArgumentError, "Attribute must be transient" unless attribute.type == :transient
|
136
|
+
|
137
|
+
super
|
138
|
+
end
|
139
|
+
|
140
|
+
def set_properties(object:, evaluator:)
|
141
|
+
ExpectProxy.new(mock: object, evaluator: evaluator).instance_exec(&@expect) if @expect
|
142
|
+
end
|
143
|
+
|
144
|
+
protected
|
145
|
+
|
146
|
+
def instantiate_object(evaluator:)
|
147
|
+
::Minitest::Mock.new
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Calificador
|
4
|
+
module Build
|
5
|
+
# Factory for objects
|
6
|
+
class ObjectFactory < BasicFactory
|
7
|
+
# Configuration derived factories
|
8
|
+
class Dsl < BasicFactory::Dsl
|
9
|
+
def initialize(factory:)
|
10
|
+
super(factory: factory)
|
11
|
+
|
12
|
+
@property_type = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def transient(&block)
|
16
|
+
raise ArgumentError, "Transient requires a block" if block.nil?
|
17
|
+
|
18
|
+
old_property_type = @property_type
|
19
|
+
@property_type = :transient
|
20
|
+
|
21
|
+
begin
|
22
|
+
instance_exec(&block)
|
23
|
+
ensure
|
24
|
+
@property_type = old_property_type
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def __create_factory(trait:, description:, source_location:)
|
31
|
+
ObjectFactory.new(
|
32
|
+
context: __factory.context,
|
33
|
+
parent: __factory,
|
34
|
+
key: __factory.key.with(trait),
|
35
|
+
name: [__factory.name, trait].compact.join("_"),
|
36
|
+
source_location: source_location,
|
37
|
+
description: description || __default_trait_description(trait: trait)
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def __default_property_type(name:)
|
42
|
+
__factory.parent&.attribute(name: name)&.type || @property_type || :property
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize(context:, parent: nil, key:, name:, description: nil, source_location:)
|
47
|
+
unless parent.nil?
|
48
|
+
raise "Parent factory must be a #{ObjectFactory}" unless parent.is_a?(ObjectFactory)
|
49
|
+
raise "Parent factory must have same type" unless parent.key.type == key.type
|
50
|
+
end
|
51
|
+
|
52
|
+
super(
|
53
|
+
context: context,
|
54
|
+
parent: parent,
|
55
|
+
key: key,
|
56
|
+
name: name,
|
57
|
+
description: description,
|
58
|
+
source_location: source_location
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
|
64
|
+
def instantiate_object(evaluator:)
|
65
|
+
parameters = []
|
66
|
+
options = {}
|
67
|
+
|
68
|
+
@key.type.instance_method(:initialize).parameters.each do |type, name|
|
69
|
+
case type
|
70
|
+
when :req
|
71
|
+
parameters << evaluator.value(name: name)
|
72
|
+
when :opt
|
73
|
+
parameters << evaluator.value(name: name) if evaluator.attribute?(name: name)
|
74
|
+
when :keyreq
|
75
|
+
options[name] = evaluator.value(name: name)
|
76
|
+
when :key
|
77
|
+
options[name] = evaluator.value(name: name) if evaluator.attribute?(name: name)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
@key.type.new(*parameters, **options)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Calificador
|
4
|
-
module Build
|
5
|
-
# Trait description
|
6
|
-
class Trait < AttributeContainer
|
7
|
-
# Configuration proxy for traits
|
8
|
-
class Dsl < AttributeContainer::Dsl
|
9
|
-
end
|
10
|
-
|
11
|
-
attr_reader :name
|
12
|
-
|
13
|
-
def initialize(parent:, name:, description: nil)
|
14
|
-
super(parent: parent, description: description)
|
15
|
-
|
16
|
-
@name = name.to_sym
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|