bcdd-result 0.7.0 → 0.9.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/.rubocop.yml +8 -0
- data/CHANGELOG.md +142 -0
- data/README.md +352 -144
- data/Rakefile +9 -3
- data/Steepfile +1 -1
- data/lib/bcdd/result/config/constant_alias.rb +35 -0
- data/lib/bcdd/result/config/options.rb +27 -0
- data/lib/bcdd/result/config/switcher.rb +82 -0
- data/lib/bcdd/result/config.rb +71 -0
- data/lib/bcdd/result/context/expectations/mixin.rb +5 -17
- data/lib/bcdd/result/context/expectations.rb +9 -25
- data/lib/bcdd/result/context/mixin.rb +23 -12
- data/lib/bcdd/result/context.rb +1 -1
- data/lib/bcdd/result/contract/evaluator.rb +1 -1
- data/lib/bcdd/result/contract/for_types_and_values.rb +7 -2
- data/lib/bcdd/result/contract.rb +4 -14
- data/lib/bcdd/result/data.rb +7 -7
- data/lib/bcdd/result/expectations/mixin.rb +30 -20
- data/lib/bcdd/result/expectations.rb +47 -13
- data/lib/bcdd/result/failure/methods.rb +1 -1
- data/lib/bcdd/result/mixin.rb +39 -18
- data/lib/bcdd/result/success/methods.rb +1 -1
- data/lib/bcdd/result/version.rb +1 -1
- data/lib/bcdd/result.rb +22 -6
- data/lib/bcdd-result.rb +3 -0
- data/sig/bcdd/result.rbs +174 -48
- metadata +11 -8
- data/lib/result.rb +0 -5
data/Rakefile
CHANGED
@@ -3,10 +3,16 @@
|
|
3
3
|
require 'bundler/gem_tasks'
|
4
4
|
require 'rake/testtask'
|
5
5
|
|
6
|
+
Rake::TestTask.new(:test_configuration) do |t|
|
7
|
+
t.libs += %w[lib test]
|
8
|
+
|
9
|
+
t.test_files = FileList.new('test/**/configuration_test.rb')
|
10
|
+
end
|
11
|
+
|
6
12
|
Rake::TestTask.new(:test) do |t|
|
7
|
-
t.libs
|
8
|
-
|
9
|
-
t.test_files = FileList
|
13
|
+
t.libs += %w[lib test]
|
14
|
+
|
15
|
+
t.test_files = FileList.new('test/**/*_test.rb')
|
10
16
|
end
|
11
17
|
|
12
18
|
require 'rubocop/rake_task'
|
data/Steepfile
CHANGED
@@ -10,7 +10,7 @@ target :lib do
|
|
10
10
|
# check 'app/models/**/*.rb' # Glob
|
11
11
|
# ignore 'lib/templates/*.rb'
|
12
12
|
|
13
|
-
|
13
|
+
library 'singleton' # Standard libraries
|
14
14
|
# library 'strong_json' # Gems
|
15
15
|
|
16
16
|
# configure_code_diagnostics(D::Ruby.default) # `default` diagnostics setting (applies by default)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
class Config
|
5
|
+
module ConstantAlias
|
6
|
+
MAPPING = {
|
7
|
+
'Result' => { target: ::Object, name: :Result, value: ::BCDD::Result },
|
8
|
+
'Context' => { target: ::Object, name: :Context, value: ::BCDD::Result::Context },
|
9
|
+
'BCDD::Context' => { target: ::BCDD, name: :Context, value: ::BCDD::Result::Context }
|
10
|
+
}.transform_values!(&:freeze).freeze
|
11
|
+
|
12
|
+
OPTIONS = MAPPING.to_h do |option_name, mapping|
|
13
|
+
affects = mapping.fetch(:target).name.freeze
|
14
|
+
|
15
|
+
[option_name, { default: false, affects: [affects].freeze }]
|
16
|
+
end.freeze
|
17
|
+
|
18
|
+
Listener = ->(option_name, boolean) do
|
19
|
+
mapping = MAPPING.fetch(option_name)
|
20
|
+
|
21
|
+
target, name, value = mapping.fetch_values(:target, :name, :value)
|
22
|
+
|
23
|
+
defined = target.const_defined?(name, false)
|
24
|
+
|
25
|
+
boolean ? defined || target.const_set(name, value) : defined && target.send(:remove_const, name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.switcher
|
29
|
+
Switcher.new(options: OPTIONS, listener: Listener)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private_constant :ConstantAlias
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
class Config
|
5
|
+
module Options
|
6
|
+
def self.with_defaults(all_flags, config)
|
7
|
+
all_flags ||= {}
|
8
|
+
|
9
|
+
default_flags = Config.instance.to_h.fetch(config)
|
10
|
+
|
11
|
+
config_flags = all_flags.fetch(config, {})
|
12
|
+
|
13
|
+
default_flags.merge(config_flags).slice(*default_flags.keys)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.select(all_flags, config:, from:)
|
17
|
+
with_defaults(all_flags, config)
|
18
|
+
.filter_map { |name, truthy| [name, from[name]] if truthy }
|
19
|
+
.to_h
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.addon(map:, from:)
|
23
|
+
select(map, config: :addon, from: from)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
class Config
|
5
|
+
class Switcher
|
6
|
+
attr_reader :_options, :_affects, :listener
|
7
|
+
|
8
|
+
private :_options, :_affects, :listener
|
9
|
+
|
10
|
+
def initialize(options:, listener: nil)
|
11
|
+
@_options = options.transform_values { _1.fetch(:default) }
|
12
|
+
@_affects = options.transform_values { _1.fetch(:affects) }
|
13
|
+
@listener = listener
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
"#<#{self.class.name} options=#{_options.inspect}>"
|
18
|
+
end
|
19
|
+
|
20
|
+
def freeze
|
21
|
+
_options.freeze
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_h
|
26
|
+
_options.dup
|
27
|
+
end
|
28
|
+
|
29
|
+
def options
|
30
|
+
_affects.to_h { |name, affects| [name, { enabled: _options[name], affects: affects }] }
|
31
|
+
end
|
32
|
+
|
33
|
+
def enabled?(name)
|
34
|
+
_options[name] || false
|
35
|
+
end
|
36
|
+
|
37
|
+
def enable!(*names)
|
38
|
+
set_many(names, to: true)
|
39
|
+
end
|
40
|
+
|
41
|
+
def disable!(*names)
|
42
|
+
set_many(names, to: false)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def set_many(names, to:)
|
48
|
+
require_option!(names)
|
49
|
+
|
50
|
+
names.each do |name|
|
51
|
+
set_one(name, to)
|
52
|
+
|
53
|
+
listener&.call(name, to)
|
54
|
+
end
|
55
|
+
|
56
|
+
options.slice(*names)
|
57
|
+
end
|
58
|
+
|
59
|
+
def set_one(name, boolean)
|
60
|
+
validate_option!(name)
|
61
|
+
|
62
|
+
_options[name] = boolean
|
63
|
+
end
|
64
|
+
|
65
|
+
def require_option!(names)
|
66
|
+
raise ::ArgumentError, "One or more options required. #{available_options_message}" if names.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate_option!(name)
|
70
|
+
return if _options.key?(name)
|
71
|
+
|
72
|
+
raise ::ArgumentError, "Invalid option: #{name.inspect}. #{available_options_message}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def available_options_message
|
76
|
+
"Available options: #{_options.keys.map(&:inspect).join(', ')}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private_constant :Switcher
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
require_relative 'config/options'
|
6
|
+
require_relative 'config/switcher'
|
7
|
+
require_relative 'config/constant_alias'
|
8
|
+
|
9
|
+
class BCDD::Result
|
10
|
+
class Config
|
11
|
+
include Singleton
|
12
|
+
|
13
|
+
ADDON = {
|
14
|
+
continue: {
|
15
|
+
default: false,
|
16
|
+
affects: %w[BCDD::Result BCDD::Result::Context BCDD::Result::Expectations BCDD::Result::Context::Expectations]
|
17
|
+
}
|
18
|
+
}.transform_values!(&:freeze).freeze
|
19
|
+
|
20
|
+
FEATURE = {
|
21
|
+
expectations: {
|
22
|
+
default: true,
|
23
|
+
affects: %w[BCDD::Result::Expectations BCDD::Result::Context::Expectations]
|
24
|
+
}
|
25
|
+
}.transform_values!(&:freeze).freeze
|
26
|
+
|
27
|
+
PATTERN_MATCHING = {
|
28
|
+
nil_as_valid_value_checking: {
|
29
|
+
default: false,
|
30
|
+
affects: %w[BCDD::Result::Expectations BCDD::Result::Context::Expectations]
|
31
|
+
}
|
32
|
+
}.transform_values!(&:freeze).freeze
|
33
|
+
|
34
|
+
attr_reader :addon, :feature, :constant_alias, :pattern_matching
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
@addon = Switcher.new(options: ADDON)
|
38
|
+
@feature = Switcher.new(options: FEATURE)
|
39
|
+
@constant_alias = ConstantAlias.switcher
|
40
|
+
@pattern_matching = Switcher.new(options: PATTERN_MATCHING)
|
41
|
+
end
|
42
|
+
|
43
|
+
def freeze
|
44
|
+
addon.freeze
|
45
|
+
feature.freeze
|
46
|
+
constant_alias.freeze
|
47
|
+
pattern_matching.freeze
|
48
|
+
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
def options
|
53
|
+
{
|
54
|
+
addon: addon,
|
55
|
+
feature: feature,
|
56
|
+
constant_alias: constant_alias,
|
57
|
+
pattern_matching: pattern_matching
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_h
|
62
|
+
options.transform_values(&:to_h)
|
63
|
+
end
|
64
|
+
|
65
|
+
def inspect
|
66
|
+
"#<#{self.class.name} options=#{options.keys.sort.inspect}>"
|
67
|
+
end
|
68
|
+
|
69
|
+
private_constant :ADDON, :FEATURE, :PATTERN_MATCHING
|
70
|
+
end
|
71
|
+
end
|
@@ -2,21 +2,9 @@
|
|
2
2
|
|
3
3
|
class BCDD::Result::Context
|
4
4
|
module Expectations::Mixin
|
5
|
-
|
6
|
-
def Success(...)
|
7
|
-
_Result::Success(...)
|
8
|
-
end
|
9
|
-
|
10
|
-
def Failure(...)
|
11
|
-
_Result::Failure(...)
|
12
|
-
end
|
5
|
+
Factory = BCDD::Result::Expectations::Mixin::Factory
|
13
6
|
|
14
|
-
|
15
|
-
|
16
|
-
def _Result
|
17
|
-
@_Result ||= Result.with(subject: self)
|
18
|
-
end
|
19
|
-
RUBY
|
7
|
+
Methods = BCDD::Result::Expectations::Mixin::Methods
|
20
8
|
|
21
9
|
module Addons
|
22
10
|
module Continuable
|
@@ -25,10 +13,10 @@ class BCDD::Result::Context
|
|
25
13
|
end
|
26
14
|
end
|
27
15
|
|
28
|
-
OPTIONS = {
|
16
|
+
OPTIONS = { continue: Continuable }.freeze
|
29
17
|
|
30
|
-
def self.options(
|
31
|
-
|
18
|
+
def self.options(config_flags)
|
19
|
+
::BCDD::Result::Config::Options.addon(map: config_flags, from: OPTIONS)
|
32
20
|
end
|
33
21
|
end
|
34
22
|
end
|
@@ -1,41 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class BCDD::Result::Context
|
4
|
-
class Expectations
|
4
|
+
class Expectations < BCDD::Result::Expectations
|
5
5
|
require_relative 'expectations/mixin'
|
6
6
|
|
7
|
-
def self.
|
8
|
-
|
9
|
-
|
10
|
-
mod = ::BCDD::Result::Expectations::Mixin.module!
|
11
|
-
mod.const_set(:Result, new(success: success, failure: failure).freeze)
|
12
|
-
mod.module_eval(Mixin::METHODS)
|
13
|
-
mod.send(:include, *addons) unless addons.empty?
|
14
|
-
mod
|
7
|
+
def self.mixin_module
|
8
|
+
Mixin
|
15
9
|
end
|
16
10
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
20
|
-
@contract = contract if contract.is_a?(::BCDD::Result::Contract::Evaluator)
|
21
|
-
|
22
|
-
@contract ||= ::BCDD::Result::Contract.new(success: success, failure: failure).freeze
|
11
|
+
def self.result_factory_without_expectations
|
12
|
+
::BCDD::Result::Context
|
23
13
|
end
|
24
14
|
|
15
|
+
private_class_method :mixin!, :mixin_module, :result_factory_without_expectations
|
16
|
+
|
25
17
|
def Success(type, **value)
|
26
|
-
Success
|
18
|
+
_ResultAs(Success, type, value)
|
27
19
|
end
|
28
20
|
|
29
21
|
def Failure(type, **value)
|
30
|
-
Failure
|
22
|
+
_ResultAs(Failure, type, value)
|
31
23
|
end
|
32
|
-
|
33
|
-
def with(subject:)
|
34
|
-
self.class.new(subject: subject, contract: contract)
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
attr_reader :subject, :contract
|
40
24
|
end
|
41
25
|
end
|
@@ -2,37 +2,48 @@
|
|
2
2
|
|
3
3
|
class BCDD::Result::Context
|
4
4
|
module Mixin
|
5
|
+
Factory = BCDD::Result::Mixin::Factory
|
6
|
+
|
5
7
|
module Methods
|
6
8
|
def Success(type, **value)
|
7
|
-
Success
|
9
|
+
_ResultAs(Success, type, value)
|
8
10
|
end
|
9
11
|
|
10
12
|
def Failure(type, **value)
|
11
|
-
Failure
|
13
|
+
_ResultAs(Failure, type, value)
|
14
|
+
end
|
15
|
+
|
16
|
+
private def _ResultAs(kind_class, type, value, halted: nil)
|
17
|
+
kind_class.new(type: type, value: value, subject: self, halted: halted)
|
12
18
|
end
|
13
19
|
end
|
14
20
|
|
15
21
|
module Addons
|
16
22
|
module Continuable
|
23
|
+
def Success(type, **value)
|
24
|
+
_ResultAs(Success, type, value, halted: true)
|
25
|
+
end
|
26
|
+
|
17
27
|
private def Continue(**value)
|
18
|
-
Success
|
28
|
+
_ResultAs(Success, :continued, value)
|
19
29
|
end
|
20
30
|
end
|
21
31
|
|
22
|
-
OPTIONS = {
|
32
|
+
OPTIONS = { continue: Continuable }.freeze
|
23
33
|
|
24
|
-
def self.options(
|
25
|
-
|
34
|
+
def self.options(config_flags)
|
35
|
+
::BCDD::Result::Config::Options.addon(map: config_flags, from: OPTIONS)
|
26
36
|
end
|
27
37
|
end
|
28
38
|
end
|
29
39
|
|
30
|
-
def self.
|
31
|
-
|
40
|
+
def self.mixin_module
|
41
|
+
Mixin
|
42
|
+
end
|
32
43
|
|
33
|
-
|
34
|
-
|
35
|
-
mod.send(:include, *addons) unless addons.empty?
|
36
|
-
mod
|
44
|
+
def self.result_factory
|
45
|
+
::BCDD::Result::Context
|
37
46
|
end
|
47
|
+
|
48
|
+
private_class_method :mixin_module, :result_factory
|
38
49
|
end
|
data/lib/bcdd/result/context.rb
CHANGED
@@ -15,7 +15,7 @@ class BCDD::Result
|
|
15
15
|
Failure.new(type: type, value: value)
|
16
16
|
end
|
17
17
|
|
18
|
-
def initialize(type:, value:, subject: nil, expectations: nil)
|
18
|
+
def initialize(type:, value:, subject: nil, expectations: nil, halted: nil)
|
19
19
|
value.is_a?(::Hash) or raise ::ArgumentError, 'value must be a Hash'
|
20
20
|
|
21
21
|
@acc = {}
|
@@ -4,7 +4,12 @@ class BCDD::Result
|
|
4
4
|
class Contract::ForTypesAndValues
|
5
5
|
include Contract::Interface
|
6
6
|
|
7
|
-
def initialize(types_and_values)
|
7
|
+
def initialize(types_and_values, config)
|
8
|
+
@nil_as_valid_value_checking =
|
9
|
+
Config::Options
|
10
|
+
.with_defaults(config, :pattern_matching)
|
11
|
+
.fetch(:nil_as_valid_value_checking)
|
12
|
+
|
8
13
|
@types_and_values = types_and_values.transform_keys(&:to_sym)
|
9
14
|
|
10
15
|
@types_contract = Contract::ForTypes.new(@types_and_values.keys)
|
@@ -29,7 +34,7 @@ class BCDD::Result
|
|
29
34
|
|
30
35
|
checking_result = value_checking === value
|
31
36
|
|
32
|
-
return value if checking_result || (
|
37
|
+
return value if checking_result || (checking_result.nil? && @nil_as_valid_value_checking)
|
33
38
|
|
34
39
|
raise Contract::Error::UnexpectedValue.build(type: type, value: value)
|
35
40
|
rescue ::NoMatchingPatternError => e
|
data/lib/bcdd/result/contract.rb
CHANGED
@@ -19,24 +19,14 @@ module BCDD::Result::Contract
|
|
19
19
|
TypeChecker.new(data.type, expectations: contract)
|
20
20
|
end
|
21
21
|
|
22
|
-
ToEnsure = ->(spec) do
|
22
|
+
ToEnsure = ->(spec, config) do
|
23
23
|
return Disabled if spec.nil?
|
24
24
|
|
25
|
-
spec.is_a?(::Hash) ? ForTypesAndValues.new(spec) : ForTypes.new(Array(spec))
|
25
|
+
spec.is_a?(::Hash) ? ForTypesAndValues.new(spec, config) : ForTypes.new(Array(spec))
|
26
26
|
end
|
27
27
|
|
28
|
-
def self.new(success:, failure:)
|
29
|
-
Evaluator.new(ToEnsure[success], ToEnsure[failure])
|
30
|
-
end
|
31
|
-
|
32
|
-
@nil_as_valid_value_checking = false
|
33
|
-
|
34
|
-
def self.nil_as_valid_value_checking!(enabled: true)
|
35
|
-
@nil_as_valid_value_checking = enabled
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.nil_as_valid_value_checking?
|
39
|
-
@nil_as_valid_value_checking
|
28
|
+
def self.new(success:, failure:, config:)
|
29
|
+
Evaluator.new(ToEnsure[success, config], ToEnsure[failure, config])
|
40
30
|
end
|
41
31
|
|
42
32
|
private_constant :ToEnsure
|
data/lib/bcdd/result/data.rb
CHANGED
@@ -2,26 +2,26 @@
|
|
2
2
|
|
3
3
|
class BCDD::Result
|
4
4
|
class Data
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :kind, :type, :value
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
7
|
+
def initialize(kind, type, value)
|
8
|
+
@kind = kind
|
9
9
|
@type = type.to_sym
|
10
10
|
@value = value
|
11
11
|
end
|
12
12
|
|
13
13
|
def to_h
|
14
|
-
{
|
14
|
+
{ kind: kind, type: type, value: value }
|
15
15
|
end
|
16
16
|
|
17
17
|
def to_a
|
18
|
-
[
|
18
|
+
[kind, type, value]
|
19
19
|
end
|
20
20
|
|
21
21
|
def inspect
|
22
22
|
format(
|
23
|
-
'#<%<class_name>s
|
24
|
-
class_name: self.class.name,
|
23
|
+
'#<%<class_name>s kind=%<kind>p type=%<type>p value=%<value>p>',
|
24
|
+
class_name: self.class.name, kind: kind, type: type, value: value
|
25
25
|
)
|
26
26
|
end
|
27
27
|
|
@@ -2,21 +2,38 @@
|
|
2
2
|
|
3
3
|
class BCDD::Result
|
4
4
|
module Expectations::Mixin
|
5
|
-
|
6
|
-
def
|
7
|
-
|
5
|
+
module Factory
|
6
|
+
def self.module!
|
7
|
+
::Module.new do
|
8
|
+
def self.included(base); base.const_set(:ResultExpectationsMixin, self); end
|
9
|
+
def self.extended(base); base.const_set(:ResultExpectationsMixin, self); end
|
10
|
+
end
|
8
11
|
end
|
12
|
+
end
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
|
14
|
+
module Methods
|
15
|
+
BASE = <<~RUBY
|
16
|
+
def Success(...)
|
17
|
+
_Result.Success(...)
|
18
|
+
end
|
19
|
+
|
20
|
+
def Failure(...)
|
21
|
+
_Result.Failure(...)
|
22
|
+
end
|
23
|
+
RUBY
|
13
24
|
|
14
|
-
|
25
|
+
FACTORY = <<~RUBY
|
26
|
+
private def _Result
|
27
|
+
@_Result ||= Result.with(subject: self, halted: %<halted>s)
|
28
|
+
end
|
29
|
+
RUBY
|
30
|
+
|
31
|
+
def self.to_eval(addons)
|
32
|
+
halted = addons.key?(:continue) ? 'true' : 'nil'
|
15
33
|
|
16
|
-
|
17
|
-
@_Result ||= Result.with(subject: self)
|
34
|
+
"#{BASE}\n#{format(FACTORY, halted: halted)}"
|
18
35
|
end
|
19
|
-
|
36
|
+
end
|
20
37
|
|
21
38
|
module Addons
|
22
39
|
module Continuable
|
@@ -25,17 +42,10 @@ class BCDD::Result
|
|
25
42
|
end
|
26
43
|
end
|
27
44
|
|
28
|
-
OPTIONS = {
|
29
|
-
|
30
|
-
def self.options(names)
|
31
|
-
Array(names).filter_map { |name| OPTIONS[name] }
|
32
|
-
end
|
33
|
-
end
|
45
|
+
OPTIONS = { continue: Continuable }.freeze
|
34
46
|
|
35
|
-
|
36
|
-
|
37
|
-
def self.included(base); base.const_set(:ResultExpectationsMixin, self); end
|
38
|
-
def self.extended(base); base.const_set(:ResultExpectationsMixin, self); end
|
47
|
+
def self.options(config_flags)
|
48
|
+
Config::Options.addon(map: config_flags, from: OPTIONS)
|
39
49
|
end
|
40
50
|
end
|
41
51
|
end
|
@@ -4,38 +4,72 @@ class BCDD::Result
|
|
4
4
|
class Expectations
|
5
5
|
require_relative 'expectations/mixin'
|
6
6
|
|
7
|
-
def self.mixin(
|
8
|
-
|
7
|
+
def self.mixin(**options)
|
8
|
+
return mixin!(**options) if Config.instance.feature.enabled?(:expectations)
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
result_factory_without_expectations.mixin(**options.slice(:config))
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.mixin!(success: nil, failure: nil, config: nil)
|
14
|
+
addons = mixin_module::Addons.options(config)
|
15
|
+
|
16
|
+
mod = mixin_module::Factory.module!
|
17
|
+
mod.const_set(:Result, new(success: success, failure: failure, config: config).freeze)
|
18
|
+
mod.module_eval(mixin_module::Methods.to_eval(addons), __FILE__, __LINE__ + 1)
|
19
|
+
mod.send(:include, *addons.values) unless addons.empty?
|
14
20
|
mod
|
15
21
|
end
|
16
22
|
|
17
|
-
def
|
23
|
+
def self.mixin_module
|
24
|
+
Mixin
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.result_factory_without_expectations
|
28
|
+
::BCDD::Result
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.new(...)
|
32
|
+
return result_factory_without_expectations unless Config.instance.feature.enabled?(:expectations)
|
33
|
+
|
34
|
+
instance = allocate
|
35
|
+
instance.send(:initialize, ...)
|
36
|
+
instance
|
37
|
+
end
|
38
|
+
|
39
|
+
private_class_method :mixin!, :mixin_module, :result_factory_without_expectations
|
40
|
+
|
41
|
+
def initialize(subject: nil, contract: nil, halted: nil, **options)
|
42
|
+
@halted = halted
|
43
|
+
|
18
44
|
@subject = subject
|
19
45
|
|
20
46
|
@contract = contract if contract.is_a?(Contract::Evaluator)
|
21
47
|
|
22
|
-
@contract ||= Contract.new(
|
48
|
+
@contract ||= Contract.new(
|
49
|
+
success: options[:success],
|
50
|
+
failure: options[:failure],
|
51
|
+
config: options[:config]
|
52
|
+
).freeze
|
23
53
|
end
|
24
54
|
|
25
55
|
def Success(type, value = nil)
|
26
|
-
Success
|
56
|
+
_ResultAs(Success, type, value)
|
27
57
|
end
|
28
58
|
|
29
59
|
def Failure(type, value = nil)
|
30
|
-
Failure
|
60
|
+
_ResultAs(Failure, type, value)
|
31
61
|
end
|
32
62
|
|
33
|
-
def with(subject:)
|
34
|
-
self.class.new(subject: subject, contract: contract)
|
63
|
+
def with(subject:, halted: nil)
|
64
|
+
self.class.new(subject: subject, halted: halted, contract: contract)
|
35
65
|
end
|
36
66
|
|
37
67
|
private
|
38
68
|
|
39
|
-
|
69
|
+
def _ResultAs(kind_class, type, value)
|
70
|
+
kind_class.new(type: type, value: value, subject: subject, expectations: contract, halted: halted)
|
71
|
+
end
|
72
|
+
|
73
|
+
attr_reader :subject, :halted, :contract
|
40
74
|
end
|
41
75
|
end
|