bcdd-result 0.7.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|