calificador 0.1.0 → 0.2.0
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 +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
|