bcdd-result 0.9.1 → 0.11.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 +43 -16
- data/README.md +259 -40
- data/Rakefile +8 -2
- data/Steepfile +2 -2
- data/lib/bcdd/result/config/switchers/addons.rb +25 -0
- data/lib/bcdd/result/config/{constant_alias.rb → switchers/constant_aliases.rb} +4 -4
- data/lib/bcdd/result/config/switchers/features.rb +28 -0
- data/lib/bcdd/result/config/switchers/pattern_matching.rb +20 -0
- data/lib/bcdd/result/config.rb +8 -28
- data/lib/bcdd/result/context/expectations/mixin.rb +10 -2
- data/lib/bcdd/result/context/mixin.rb +13 -5
- data/lib/bcdd/result/context/success.rb +2 -2
- data/lib/bcdd/result/context.rb +10 -15
- data/lib/bcdd/result/expectations/mixin.rb +11 -5
- data/lib/bcdd/result/expectations.rb +6 -6
- data/lib/bcdd/result/mixin.rb +11 -5
- data/lib/bcdd/result/transitions/tracking/disabled.rb +17 -0
- data/lib/bcdd/result/transitions/tracking/enabled.rb +80 -0
- data/lib/bcdd/result/transitions/tracking.rb +20 -0
- data/lib/bcdd/result/transitions/tree.rb +95 -0
- data/lib/bcdd/result/transitions.rb +30 -0
- data/lib/bcdd/result/version.rb +1 -1
- data/lib/bcdd/result.rb +33 -22
- data/sig/bcdd/result/config.rbs +101 -0
- data/sig/bcdd/result/context.rbs +114 -0
- data/sig/bcdd/result/contract.rbs +119 -0
- data/sig/bcdd/result/data.rbs +16 -0
- data/sig/bcdd/result/error.rbs +31 -0
- data/sig/bcdd/result/expectations.rbs +71 -0
- data/sig/bcdd/result/handler.rbs +47 -0
- data/sig/bcdd/result/mixin.rbs +45 -0
- data/sig/bcdd/result/transitions.rbs +89 -0
- data/sig/bcdd/result/version.rbs +5 -0
- data/sig/bcdd/result.rbs +7 -519
- metadata +22 -4
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
class Config
|
5
|
+
module Features
|
6
|
+
OPTIONS = {
|
7
|
+
expectations: {
|
8
|
+
default: true,
|
9
|
+
affects: %w[BCDD::Result::Expectations BCDD::Result::Context::Expectations]
|
10
|
+
},
|
11
|
+
transitions: {
|
12
|
+
default: true,
|
13
|
+
affects: %w[BCDD::Result BCDD::Result::Context]
|
14
|
+
}
|
15
|
+
}.transform_values!(&:freeze).freeze
|
16
|
+
|
17
|
+
Listener = ->(option_name, _bool) do
|
18
|
+
Thread.current[Transitions::THREAD_VAR_NAME] = nil if option_name == :transitions
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.switcher
|
22
|
+
Switcher.new(options: OPTIONS, listener: Listener)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private_constant :Features
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
class Config
|
5
|
+
module PatternMatching
|
6
|
+
OPTIONS = {
|
7
|
+
nil_as_valid_value_checking: {
|
8
|
+
default: false,
|
9
|
+
affects: %w[BCDD::Result::Expectations BCDD::Result::Context::Expectations]
|
10
|
+
}
|
11
|
+
}.transform_values!(&:freeze).freeze
|
12
|
+
|
13
|
+
def self.switcher
|
14
|
+
Switcher.new(options: OPTIONS)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private_constant :PatternMatching
|
19
|
+
end
|
20
|
+
end
|
data/lib/bcdd/result/config.rb
CHANGED
@@ -4,40 +4,22 @@ require 'singleton'
|
|
4
4
|
|
5
5
|
require_relative 'config/options'
|
6
6
|
require_relative 'config/switcher'
|
7
|
-
require_relative 'config/
|
7
|
+
require_relative 'config/switchers/addons'
|
8
|
+
require_relative 'config/switchers/constant_aliases'
|
9
|
+
require_relative 'config/switchers/features'
|
10
|
+
require_relative 'config/switchers/pattern_matching'
|
8
11
|
|
9
12
|
class BCDD::Result
|
10
13
|
class Config
|
11
14
|
include Singleton
|
12
15
|
|
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
16
|
attr_reader :addon, :feature, :constant_alias, :pattern_matching
|
35
17
|
|
36
18
|
def initialize
|
37
|
-
@addon =
|
38
|
-
@feature =
|
39
|
-
@constant_alias =
|
40
|
-
@pattern_matching =
|
19
|
+
@addon = Addons.switcher
|
20
|
+
@feature = Features.switcher
|
21
|
+
@constant_alias = ConstantAliases.switcher
|
22
|
+
@pattern_matching = PatternMatching.switcher
|
41
23
|
end
|
42
24
|
|
43
25
|
def freeze
|
@@ -65,7 +47,5 @@ class BCDD::Result
|
|
65
47
|
def inspect
|
66
48
|
"#<#{self.class.name} options=#{options.keys.sort.inspect}>"
|
67
49
|
end
|
68
|
-
|
69
|
-
private_constant :ADDON, :FEATURE, :PATTERN_MATCHING
|
70
50
|
end
|
71
51
|
end
|
@@ -7,13 +7,21 @@ class BCDD::Result::Context
|
|
7
7
|
Methods = BCDD::Result::Expectations::Mixin::Methods
|
8
8
|
|
9
9
|
module Addons
|
10
|
-
module
|
10
|
+
module Continue
|
11
11
|
private def Continue(**value)
|
12
12
|
Success.new(type: :continued, value: value, subject: self)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
module Given
|
17
|
+
private def Given(*values)
|
18
|
+
value = values.map(&:to_h).reduce({}) { |acc, val| acc.merge(val) }
|
19
|
+
|
20
|
+
Success.new(type: :given, value: value, subject: self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
OPTIONS = { continue: Continue, given: Given }.freeze
|
17
25
|
|
18
26
|
def self.options(config_flags)
|
19
27
|
::BCDD::Result::Config::Options.addon(map: config_flags, from: OPTIONS)
|
@@ -13,15 +13,15 @@ class BCDD::Result::Context
|
|
13
13
|
_ResultAs(Failure, type, value)
|
14
14
|
end
|
15
15
|
|
16
|
-
private def _ResultAs(kind_class, type, value,
|
17
|
-
kind_class.new(type: type, value: value, subject: self,
|
16
|
+
private def _ResultAs(kind_class, type, value, terminal: nil)
|
17
|
+
kind_class.new(type: type, value: value, subject: self, terminal: terminal)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
module Addons
|
22
|
-
module
|
22
|
+
module Continue
|
23
23
|
def Success(type, **value)
|
24
|
-
_ResultAs(Success, type, value,
|
24
|
+
_ResultAs(Success, type, value, terminal: true)
|
25
25
|
end
|
26
26
|
|
27
27
|
private def Continue(**value)
|
@@ -29,7 +29,15 @@ class BCDD::Result::Context
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
|
32
|
+
module Given
|
33
|
+
private def Given(*values)
|
34
|
+
value = values.map(&:to_h).reduce({}) { |acc, val| acc.merge(val) }
|
35
|
+
|
36
|
+
_ResultAs(Success, :given, value)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
OPTIONS = { continue: Continue, given: Given }.freeze
|
33
41
|
|
34
42
|
def self.options(config_flags)
|
35
43
|
::BCDD::Result::Config::Options.addon(map: config_flags, from: OPTIONS)
|
@@ -3,13 +3,13 @@
|
|
3
3
|
class BCDD::Result::Context::Success < BCDD::Result::Context
|
4
4
|
include ::BCDD::Result::Success::Methods
|
5
5
|
|
6
|
-
def and_expose(type, keys,
|
6
|
+
def and_expose(type, keys, terminal: true)
|
7
7
|
unless keys.is_a?(::Array) && !keys.empty? && keys.all?(::Symbol)
|
8
8
|
raise ::ArgumentError, 'keys must be an Array of Symbols'
|
9
9
|
end
|
10
10
|
|
11
11
|
exposed_value = acc.merge(value).slice(*keys)
|
12
12
|
|
13
|
-
self.class.new(type: type, value: exposed_value, subject: subject,
|
13
|
+
self.class.new(type: type, value: exposed_value, subject: subject, terminal: terminal)
|
14
14
|
end
|
15
15
|
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, terminal: nil)
|
19
19
|
value.is_a?(::Hash) or raise ::ArgumentError, 'value must be a Hash'
|
20
20
|
|
21
21
|
@acc = {}
|
@@ -40,25 +40,20 @@ class BCDD::Result
|
|
40
40
|
-1
|
41
41
|
end
|
42
42
|
|
43
|
-
def call_and_then_subject_method(
|
44
|
-
|
43
|
+
def call_and_then_subject_method!(method, context_data)
|
44
|
+
acc.merge!(value.merge(context_data))
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
when 1 then subject.send(method_name, **acc)
|
52
|
-
else raise Error::InvalidSubjectMethodArity.build(subject: subject, method: method, max_arity: 1)
|
53
|
-
end
|
54
|
-
|
55
|
-
ensure_result_object(result, origin: :method)
|
46
|
+
case SubjectMethodArity[method]
|
47
|
+
when 0 then subject.send(method.name)
|
48
|
+
when 1 then subject.send(method.name, **acc)
|
49
|
+
else raise Error::InvalidSubjectMethodArity.build(subject: subject, method: method, max_arity: 1)
|
50
|
+
end
|
56
51
|
end
|
57
52
|
|
58
|
-
def call_and_then_block(block)
|
53
|
+
def call_and_then_block!(block)
|
59
54
|
acc.merge!(value)
|
60
55
|
|
61
|
-
|
56
|
+
block.call(acc)
|
62
57
|
end
|
63
58
|
|
64
59
|
def ensure_result_object(result, origin:)
|
@@ -24,25 +24,31 @@ class BCDD::Result
|
|
24
24
|
|
25
25
|
FACTORY = <<~RUBY
|
26
26
|
private def _Result
|
27
|
-
@_Result ||= Result.with(subject: self,
|
27
|
+
@_Result ||= Result.with(subject: self, terminal: %<terminal>s)
|
28
28
|
end
|
29
29
|
RUBY
|
30
30
|
|
31
31
|
def self.to_eval(addons)
|
32
|
-
|
32
|
+
terminal = addons.key?(:continue) ? 'true' : 'nil'
|
33
33
|
|
34
|
-
"#{BASE}\n#{format(FACTORY,
|
34
|
+
"#{BASE}\n#{format(FACTORY, terminal: terminal)}"
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
module Addons
|
39
|
-
module
|
39
|
+
module Continue
|
40
40
|
private def Continue(value)
|
41
41
|
Success.new(type: :continued, value: value, subject: self)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
|
45
|
+
module Given
|
46
|
+
private def Given(value)
|
47
|
+
Success.new(type: :given, value: value, subject: self)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
OPTIONS = { continue: Continue, given: Given }.freeze
|
46
52
|
|
47
53
|
def self.options(config_flags)
|
48
54
|
Config::Options.addon(map: config_flags, from: OPTIONS)
|
@@ -38,8 +38,8 @@ class BCDD::Result
|
|
38
38
|
|
39
39
|
private_class_method :mixin!, :mixin_module, :result_factory_without_expectations
|
40
40
|
|
41
|
-
def initialize(subject: nil, contract: nil,
|
42
|
-
@
|
41
|
+
def initialize(subject: nil, contract: nil, terminal: nil, **options)
|
42
|
+
@terminal = terminal
|
43
43
|
|
44
44
|
@subject = subject
|
45
45
|
|
@@ -60,16 +60,16 @@ class BCDD::Result
|
|
60
60
|
_ResultAs(Failure, type, value)
|
61
61
|
end
|
62
62
|
|
63
|
-
def with(subject:,
|
64
|
-
self.class.new(subject: subject,
|
63
|
+
def with(subject:, terminal: nil)
|
64
|
+
self.class.new(subject: subject, terminal: terminal, contract: contract)
|
65
65
|
end
|
66
66
|
|
67
67
|
private
|
68
68
|
|
69
69
|
def _ResultAs(kind_class, type, value)
|
70
|
-
kind_class.new(type: type, value: value, subject: subject, expectations: contract,
|
70
|
+
kind_class.new(type: type, value: value, subject: subject, expectations: contract, terminal: terminal)
|
71
71
|
end
|
72
72
|
|
73
|
-
attr_reader :subject, :
|
73
|
+
attr_reader :subject, :terminal, :contract
|
74
74
|
end
|
75
75
|
end
|
data/lib/bcdd/result/mixin.rb
CHANGED
@@ -20,15 +20,15 @@ class BCDD::Result
|
|
20
20
|
_ResultAs(Failure, type, value)
|
21
21
|
end
|
22
22
|
|
23
|
-
private def _ResultAs(kind_class, type, value,
|
24
|
-
kind_class.new(type: type, value: value, subject: self,
|
23
|
+
private def _ResultAs(kind_class, type, value, terminal: nil)
|
24
|
+
kind_class.new(type: type, value: value, subject: self, terminal: terminal)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
module Addons
|
29
|
-
module
|
29
|
+
module Continue
|
30
30
|
def Success(type, value = nil)
|
31
|
-
_ResultAs(Success, type, value,
|
31
|
+
_ResultAs(Success, type, value, terminal: true)
|
32
32
|
end
|
33
33
|
|
34
34
|
private def Continue(value)
|
@@ -36,7 +36,13 @@ class BCDD::Result
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
|
39
|
+
module Given
|
40
|
+
private def Given(value)
|
41
|
+
_ResultAs(Success, :given, value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
OPTIONS = { continue: Continue, given: Given }.freeze
|
40
46
|
|
41
47
|
def self.options(config_flags)
|
42
48
|
Config::Options.addon(map: config_flags, from: OPTIONS)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BCDD::Result::Transitions
|
4
|
+
module Tracking::Disabled
|
5
|
+
def self.start(name:, desc:); end
|
6
|
+
|
7
|
+
def self.finish(result:); end
|
8
|
+
|
9
|
+
def self.reset!; end
|
10
|
+
|
11
|
+
def self.record(result); end
|
12
|
+
|
13
|
+
def self.record_and_then(_type, _data, _subject)
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BCDD::Result::Transitions
|
4
|
+
class Tracking::Enabled
|
5
|
+
attr_accessor :tree, :records, :root_started_at
|
6
|
+
|
7
|
+
private :tree, :tree=, :records, :records=, :root_started_at, :root_started_at=
|
8
|
+
|
9
|
+
def start(name:, desc:)
|
10
|
+
name_and_desc = [name, desc]
|
11
|
+
|
12
|
+
tree.frozen? ? root_start(name_and_desc) : tree.insert!(name_and_desc)
|
13
|
+
end
|
14
|
+
|
15
|
+
def finish(result:)
|
16
|
+
node = tree.current
|
17
|
+
|
18
|
+
tree.move_up!
|
19
|
+
|
20
|
+
return unless node.root?
|
21
|
+
|
22
|
+
duration = (now_in_milliseconds - root_started_at)
|
23
|
+
|
24
|
+
metadata = { duration: duration, tree_map: tree.nested_ids }
|
25
|
+
|
26
|
+
result.send(:transitions=, version: Tracking::VERSION, records: records, metadata: metadata)
|
27
|
+
|
28
|
+
reset!
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset!
|
32
|
+
self.tree = Tracking::EMPTY_TREE
|
33
|
+
end
|
34
|
+
|
35
|
+
def record(result)
|
36
|
+
return if tree.frozen?
|
37
|
+
|
38
|
+
track(result, time: ::Time.now.getutc)
|
39
|
+
end
|
40
|
+
|
41
|
+
def record_and_then(type_arg, arg, subject)
|
42
|
+
type = type_arg.instance_of?(::Method) ? :method : type_arg
|
43
|
+
|
44
|
+
unless tree.frozen?
|
45
|
+
current_and_then = { type: type, arg: arg, subject: subject }
|
46
|
+
current_and_then[:method_name] = type_arg.name if type == :method
|
47
|
+
|
48
|
+
tree.current.value[1] = current_and_then
|
49
|
+
end
|
50
|
+
|
51
|
+
yield
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
TreeNodeValueNormalizer = ->(id, (nam, des)) { [{ id: id, name: nam, desc: des }, Tracking::EMPTY_HASH] }
|
57
|
+
|
58
|
+
def root_start(name_and_desc)
|
59
|
+
self.root_started_at = now_in_milliseconds
|
60
|
+
|
61
|
+
self.records = []
|
62
|
+
|
63
|
+
self.tree = Tree.new(name_and_desc, normalizer: TreeNodeValueNormalizer)
|
64
|
+
end
|
65
|
+
|
66
|
+
def track(result, time:)
|
67
|
+
result = result.data.to_h
|
68
|
+
|
69
|
+
root, = tree.root_value
|
70
|
+
parent, = tree.parent_value
|
71
|
+
current, and_then = tree.current_value
|
72
|
+
|
73
|
+
records << { root: root, parent: parent, current: current, result: result, and_then: and_then, time: time }
|
74
|
+
end
|
75
|
+
|
76
|
+
def now_in_milliseconds
|
77
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
module Transitions
|
5
|
+
module Tracking
|
6
|
+
require_relative 'tracking/enabled'
|
7
|
+
require_relative 'tracking/disabled'
|
8
|
+
|
9
|
+
EMPTY_ARRAY = [].freeze
|
10
|
+
EMPTY_HASH = {}.freeze
|
11
|
+
EMPTY_TREE = Tree.new(nil).freeze
|
12
|
+
VERSION = 1
|
13
|
+
EMPTY = { version: VERSION, records: EMPTY_ARRAY, metadata: { duration: 0, tree_map: EMPTY_ARRAY } }.freeze
|
14
|
+
|
15
|
+
def self.instance
|
16
|
+
Config.instance.feature.enabled?(:transitions) ? Tracking::Enabled.new : Tracking::Disabled
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
module Transitions
|
5
|
+
class Tree
|
6
|
+
class Node
|
7
|
+
attr_reader :id, :value, :parent, :normalizer, :children
|
8
|
+
|
9
|
+
def initialize(value, parent:, id:, normalizer:)
|
10
|
+
@normalizer = normalizer
|
11
|
+
|
12
|
+
@id = id
|
13
|
+
@value = normalizer.call(id, value)
|
14
|
+
@parent = parent
|
15
|
+
|
16
|
+
@children = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def insert(value, id:)
|
20
|
+
node = self.class.new(value, parent: self, id: id, normalizer: normalizer)
|
21
|
+
|
22
|
+
@children << node
|
23
|
+
|
24
|
+
node
|
25
|
+
end
|
26
|
+
|
27
|
+
def root?
|
28
|
+
parent.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
def leaf?
|
32
|
+
children.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
def node?
|
36
|
+
!leaf?
|
37
|
+
end
|
38
|
+
|
39
|
+
def inspect
|
40
|
+
"#<#{self.class.name} id=#{id} children.size=#{children.size}>"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :size, :root, :current
|
45
|
+
|
46
|
+
def initialize(value, normalizer: ->(_id, val) { val })
|
47
|
+
@size = 0
|
48
|
+
|
49
|
+
@root = Node.new(value, parent: nil, id: @size, normalizer: normalizer)
|
50
|
+
|
51
|
+
@current = @root
|
52
|
+
end
|
53
|
+
|
54
|
+
def root_value
|
55
|
+
root.value
|
56
|
+
end
|
57
|
+
|
58
|
+
def parent_value
|
59
|
+
current.parent&.value || root_value
|
60
|
+
end
|
61
|
+
|
62
|
+
def current_value
|
63
|
+
current.value
|
64
|
+
end
|
65
|
+
|
66
|
+
def insert(value)
|
67
|
+
@size += 1
|
68
|
+
|
69
|
+
current.insert(value, id: size)
|
70
|
+
end
|
71
|
+
|
72
|
+
def insert!(value)
|
73
|
+
@current = insert(value)
|
74
|
+
end
|
75
|
+
|
76
|
+
def move_up!(level = 1)
|
77
|
+
tap { level.times { @current = current.parent || root } }
|
78
|
+
end
|
79
|
+
|
80
|
+
def move_down!(level = 1, index: -1)
|
81
|
+
tap { level.times { current.children[index].then { |child| @current = child if child } } }
|
82
|
+
end
|
83
|
+
|
84
|
+
def move_to_root!
|
85
|
+
tap { @current = root }
|
86
|
+
end
|
87
|
+
|
88
|
+
NestedIds = ->(node) { [node.id, node.children.map(&NestedIds)] }
|
89
|
+
|
90
|
+
def nested_ids
|
91
|
+
NestedIds[root]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BCDD::Result
|
4
|
+
module Transitions
|
5
|
+
require_relative 'transitions/tree'
|
6
|
+
require_relative 'transitions/tracking'
|
7
|
+
|
8
|
+
THREAD_VAR_NAME = :bcdd_result_transitions_tracking
|
9
|
+
|
10
|
+
def self.tracking
|
11
|
+
Thread.current[THREAD_VAR_NAME] ||= Tracking.instance
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.transitions(name: nil, desc: nil)
|
16
|
+
Transitions.tracking.start(name: name, desc: desc)
|
17
|
+
|
18
|
+
result = yield
|
19
|
+
|
20
|
+
result.is_a?(::BCDD::Result) or raise Error::UnexpectedOutcome.build(outcome: result, origin: :transitions)
|
21
|
+
|
22
|
+
Transitions.tracking.finish(result: result)
|
23
|
+
|
24
|
+
result
|
25
|
+
rescue ::Exception => e
|
26
|
+
Transitions.tracking.reset!
|
27
|
+
|
28
|
+
raise e
|
29
|
+
end
|
30
|
+
end
|