bcdd-result 0.9.1 → 0.11.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 +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
|