trailblazer-operation 0.0.13 → 0.1.1

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -3
  3. data/CHANGES.md +44 -0
  4. data/Gemfile +13 -2
  5. data/Rakefile +8 -6
  6. data/lib/trailblazer/operation.rb +86 -12
  7. data/lib/trailblazer/operation/deprecated_macro.rb +19 -0
  8. data/lib/trailblazer/operation/inject.rb +34 -0
  9. data/lib/trailblazer/operation/inspect.rb +80 -0
  10. data/lib/trailblazer/operation/public_call.rb +62 -0
  11. data/lib/trailblazer/operation/railway.rb +32 -0
  12. data/lib/trailblazer/operation/railway/fast_track.rb +13 -0
  13. data/lib/trailblazer/operation/railway/normalizer.rb +73 -0
  14. data/lib/trailblazer/operation/railway/task_builder.rb +44 -0
  15. data/lib/trailblazer/operation/result.rb +6 -4
  16. data/lib/trailblazer/operation/skill.rb +8 -24
  17. data/lib/trailblazer/operation/task_wrap.rb +68 -0
  18. data/lib/trailblazer/operation/trace.rb +49 -0
  19. data/lib/trailblazer/operation/variable_mapping.rb +91 -0
  20. data/lib/trailblazer/operation/version.rb +1 -1
  21. data/test/call_test.rb +27 -8
  22. data/test/class_dependencies_test.rb +16 -0
  23. data/test/docs/doormat_test.rb +189 -0
  24. data/test/docs/wiring_test.rb +421 -0
  25. data/test/dry_container_test.rb +4 -0
  26. data/test/fast_track_test.rb +197 -0
  27. data/test/gemfiles/Gemfile.ruby-2.0 +1 -2
  28. data/test/gemfiles/Gemfile.ruby-2.0.lock +40 -0
  29. data/test/inheritance_test.rb +1 -1
  30. data/test/inspect_test.rb +43 -0
  31. data/test/introspect_test.rb +50 -0
  32. data/test/macro_test.rb +61 -0
  33. data/test/operation_test.rb +94 -0
  34. data/test/result_test.rb +14 -8
  35. data/test/ruby-2.0.0/operation_test.rb +73 -0
  36. data/test/ruby-2.0.0/step_test.rb +136 -0
  37. data/test/skill_test.rb +66 -48
  38. data/test/step_test.rb +228 -0
  39. data/test/task_wrap_test.rb +91 -0
  40. data/test/test_helper.rb +37 -0
  41. data/test/trace_test.rb +62 -0
  42. data/test/variable_mapping_test.rb +66 -0
  43. data/test/wire_test.rb +113 -0
  44. data/test/wiring/defaults_test.rb +197 -0
  45. data/test/wiring/subprocess_test.rb +70 -0
  46. data/trailblazer-operation.gemspec +3 -5
  47. metadata +62 -36
  48. data/lib/trailblazer/operation/1.9.3/option.rb +0 -36
  49. data/lib/trailblazer/operation/generic.rb +0 -12
  50. data/lib/trailblazer/operation/option.rb +0 -54
  51. data/lib/trailblazer/operation/pipetree.rb +0 -142
  52. data/lib/trailblazer/skill.rb +0 -70
  53. data/test/2.0.0-pipetree_test.rb +0 -100
  54. data/test/2.1.0-pipetree_test.rb +0 -100
  55. data/test/operation_skill_test.rb +0 -89
  56. data/test/pipetree_test.rb +0 -185
@@ -0,0 +1,91 @@
1
+ require "test_helper"
2
+
3
+ require "trailblazer/operation/inject" # an optional feature.com.
4
+
5
+ class TaskWrapTest < Minitest::Spec
6
+ MyMacro = ->( (options, *args), *) do
7
+ options["MyMacro.contract"] = options[:contract]
8
+ [ Trailblazer::Activity::Right, [options, *args] ]
9
+ end
10
+
11
+ class Create < Trailblazer::Operation
12
+ step :model!
13
+ # step [ MyMacro, { name: "MyMacro" }, { dependencies: { "contract" => :external_maybe } }]
14
+ step(
15
+ task: MyMacro,
16
+ id: "MyMacro",
17
+
18
+ runner_options: {
19
+ merge: Trailblazer::Activity::Magnetic::Builder::Path.plan do
20
+ task Trailblazer::Operation::Wrap::Inject::ReverseMergeDefaults.new( contract: "MyDefaultContract" ),
21
+ id: "inject.my_default",
22
+ before: "task_wrap.call_task"
23
+ end
24
+ }
25
+
26
+ )
27
+
28
+ def model!(options, **)
29
+ options["options.contract"] = options[:contract]
30
+ true
31
+ end
32
+ end
33
+
34
+ # it { Create.__call__("adsf", options={}, {}).inspect("MyMacro.contract", "options.contract").must_equal %{} }
35
+
36
+ def inspect_hash(hash, *keys)
37
+ Hash[ keys.collect { |key| [key, hash[key]] } ].inspect
38
+ end
39
+
40
+ #-
41
+ # default gets set by Injection.
42
+ it do
43
+ direction, (options, _) = Create.__call__( [{}, {}] )
44
+
45
+
46
+ inspect_hash(options, "options.contract", :contract, "MyMacro.contract").
47
+ must_equal %{{"options.contract"=>nil, :contract=>"MyDefaultContract", "MyMacro.contract"=>"MyDefaultContract"}}
48
+ end
49
+
50
+ # injected from outside, Injection skips.
51
+ it do
52
+ direction, (options, _) = Create.__call__( [ { :contract=>"MyExternalContract" }, {} ] )
53
+
54
+ inspect_hash(options, "options.contract", :contract, "MyMacro.contract").
55
+ must_equal %{{"options.contract"=>"MyExternalContract", :contract=>"MyExternalContract", "MyMacro.contract"=>"MyExternalContract"}}
56
+ end
57
+
58
+ #- Nested task_wraps should not override the outer.
59
+ AnotherMacro = ->( (options, *args), *) do
60
+ options["AnotherMacro.another_contract"] = options[:another_contract]
61
+ [ Trailblazer::Activity::Right, [options, *args] ]
62
+ end
63
+
64
+ class Update < Trailblazer::Operation
65
+ step(
66
+ task: ->( (options, *args), * ) {
67
+ _d, *o = Create.__call__( [ options, *args ] )
68
+
69
+ [ Trailblazer::Activity::Right, *o ]
70
+ },
71
+ id: "Create"
72
+ )
73
+ step(
74
+ task: AnotherMacro,
75
+ id: "AnotherMacro",
76
+ runner_options: {
77
+ merge: Trailblazer::Activity::Magnetic::Builder::Path.plan do
78
+ task Trailblazer::Operation::Wrap::Inject::ReverseMergeDefaults.new( another_contract: "AnotherDefaultContract" ), id: "inject.my_default",
79
+ before: "task_wrap.call_task"
80
+ end
81
+ }
82
+ )
83
+ end
84
+
85
+ it do
86
+ direction, (options, _) = Update.__call__( [ {}, {} ] )
87
+
88
+ inspect_hash(options, "options.contract", :contract, "MyMacro.contract", "AnotherMacro.another_contract").
89
+ must_equal %{{"options.contract"=>nil, :contract=>"MyDefaultContract", "MyMacro.contract"=>"MyDefaultContract", "AnotherMacro.another_contract"=>"AnotherDefaultContract"}}
90
+ end
91
+ end
data/test/test_helper.rb CHANGED
@@ -1,2 +1,39 @@
1
+ require "pp"
2
+
1
3
  require "minitest/autorun"
2
4
  require "trailblazer/operation"
5
+
6
+ Minitest::Spec::Activity = Trailblazer::Activity
7
+
8
+ module Test
9
+ # Create a step method in `klass` with the following body.
10
+ #
11
+ # def a(options, a_return:, data:, **)
12
+ # data << :a
13
+ #
14
+ # a_return
15
+ # end
16
+ def self.step(klass, *names)
17
+ names.each do |name|
18
+ method_def =
19
+ %{def #{name}(options, #{name}_return:, data:, **)
20
+ data << :#{name}
21
+
22
+ #{name}_return
23
+ end}
24
+
25
+ klass.class_eval(method_def)
26
+ end
27
+ end
28
+
29
+ # builder for PlusPoles
30
+ def self.plus_poles_for(mapping)
31
+ ary = mapping.collect { |evt, semantic| [Trailblazer:: Activity::Output(evt, semantic), semantic ] }
32
+
33
+ Trailblazer::Activity::Magnetic::DSL::PlusPoles.new.merge(::Hash[ary])
34
+ end
35
+ end
36
+
37
+ Minitest::Spec.class_eval do
38
+
39
+ end
@@ -0,0 +1,62 @@
1
+ require "test_helper"
2
+
3
+ class TraceTest < Minitest::Spec
4
+ class B < Trailblazer::Operation
5
+ step ->(options, **) { options[:b] = true }, id: "B.task.b"
6
+ step ->(options, **) { options[:e] = true }, id: "B.task.e"
7
+
8
+ def self.call( (options, flow_options), **circuit_options )
9
+ __call__( [Trailblazer::Context(options), flow_options], circuit_options )
10
+ end
11
+ end
12
+
13
+ class Create < Trailblazer::Operation
14
+ step ->(options, a_return:, **) { options[:a] = a_return }, id: "Create.task.a"
15
+ step( {task: B, id: "MyNested"}, B.outputs[:success] => :success )
16
+ step ->(options, **) { options[:c] = true }, id: "Create.task.c"
17
+ step ->(options, params:, **) { params.any? }, id: "Create.task.params"
18
+
19
+ def self.call( (options, flow_options), **circuit_options )
20
+ __call__( [Trailblazer::Context(options), flow_options], circuit_options ) # FIXME.
21
+ end
22
+ end
23
+ # raise Create["__task_wraps__"].inspect
24
+
25
+ it "allows using low-level Activity::Trace" do
26
+ operation = ->(*args) { puts "@@@@@ #{args.last.inspect}"; Create.__call__(*args) }
27
+
28
+ stack, _ = Trailblazer::Activity::Trace.(
29
+ Create,
30
+ [
31
+ { a_return: true, "params" => {} },
32
+ ]
33
+ )
34
+
35
+ puts output = Trailblazer::Activity::Trace::Present.tree(stack)
36
+
37
+ output.gsub(/0x\w+/, "").gsub(/@.+_test/, "").must_equal %{|-- #<Trailblazer::Activity::Start:>
38
+ |-- Create.task.a
39
+ |-- MyNested
40
+ | |-- #<Trailblazer::Activity::Start:>
41
+ | |-- B.task.b
42
+ | |-- B.task.e
43
+ | `-- #<Trailblazer::Operation::Railway::End::Success:>
44
+ |-- Create.task.c
45
+ |-- Create.task.params
46
+ `-- #<Trailblazer::Operation::Railway::End::Failure:>}
47
+ end
48
+
49
+ it "Operation::trace" do
50
+ result = Create.trace({ "params" => { x: 1 }, a_return: true })
51
+ result.wtf.gsub(/0x\w+/, "").gsub(/@.+_test/, "").must_equal %{|-- #<Trailblazer::Activity::Start:>
52
+ |-- Create.task.a
53
+ |-- MyNested
54
+ | |-- #<Trailblazer::Activity::Start:>
55
+ | |-- B.task.b
56
+ | |-- B.task.e
57
+ | `-- #<Trailblazer::Operation::Railway::End::Success:>
58
+ |-- Create.task.c
59
+ |-- Create.task.params
60
+ `-- #<Trailblazer::Operation::Railway::End::Success:>}
61
+ end
62
+ end
@@ -0,0 +1,66 @@
1
+ require "test_helper"
2
+
3
+ # Test input- and output filter for specific tasks.
4
+ # These are task wrap steps added before and after the task.
5
+ class VariableMappingTest < Minitest::Spec
6
+ # first task
7
+ Model = ->((options, flow), **o) do
8
+ options["a"] = options["a"] * 2 # rename to model.a
9
+ options["model.nonsense"] = true # filter me out
10
+
11
+ [Activity::Right, [options, flow]]
12
+ end
13
+
14
+ # second task
15
+ Uuid = ->((options, flow), **o) do
16
+ options["a"] = options["a"] + options["model.a"] # rename to uuid.a
17
+ options["uuid.nonsense"] = false # filter me out
18
+
19
+ [Activity::Right, [options, flow]]
20
+ end
21
+
22
+ let (:activity) do
23
+ Activity.build do
24
+ task Model
25
+ task Uuid
26
+ end
27
+ end
28
+
29
+ describe "input/output" do
30
+
31
+ it do
32
+ model_input = ->(options) { { "a" => options["a"]+1 } }
33
+ model_output = ->(options) { { "model.a" => options["a"] } }
34
+ uuid_input = ->(options) { { "a" => options["a"]*3, "model.a" => options["model.a"] } }
35
+ uuid_output = ->(options) { { "uuid.a" => options["a"] } }
36
+
37
+ runtime = Hash.new([])
38
+
39
+ # add filters around Model.
40
+ runtime[ Model ] = Activity::Magnetic::Builder::Path.plan do
41
+ task Activity::Wrap::Input.new( model_input ), id: "task_wrap.input", before: "task_wrap.call_task"
42
+ task Activity::Wrap::Output.new( model_output ), id: "task_wrap.output", before: "End.success", group: :end
43
+ end
44
+
45
+ # add filters around Uuid.
46
+ runtime[ Uuid ] = Activity::Magnetic::Builder::Path.plan do
47
+ task Activity::Wrap::Input.new( uuid_input ), id: "task_wrap.input", before: "task_wrap.call_task"
48
+ task Activity::Wrap::Output.new( uuid_output ), id: "task_wrap.output", before: "End.success", group: :end
49
+ end
50
+
51
+ signal, (options, flow_options) = activity.(
52
+ [
53
+ options = { "a" => 1 },
54
+ {},
55
+ ],
56
+
57
+ wrap_runtime: runtime, # dynamic additions from the outside (e.g. tracing), also per task.
58
+ runner: Activity::Wrap::Runner,
59
+ wrap_static: Hash.new( Activity::Wrap.initial_activity ), # per activity?
60
+ )
61
+
62
+ signal.must_equal activity.outputs[:success].signal
63
+ options.must_equal({"a"=>1, "model.a"=>4, "uuid.a" => 7 })
64
+ end
65
+ end
66
+ end
data/test/wire_test.rb ADDED
@@ -0,0 +1,113 @@
1
+ # require "test_helper"
2
+ # # require "trailblazer/developer"
3
+
4
+ # class WireTest < Minitest::Spec
5
+ # Circuit = Trailblazer::Circuit
6
+ # ExceptionFromD = Class.new # custom signal
7
+
8
+ # D = ->((options, *args), *) do
9
+ # options["D"] = [ options["a"], options["b"], options["c"] ]
10
+
11
+ # signal = options["D_return"]
12
+ # [ signal, [ options, *args ] ]
13
+ # end
14
+
15
+ # #---
16
+ # #- step providing all :outputs manually.
17
+ # class Create < Trailblazer::Operation
18
+ # step ->(options, **) { options["a"] = 1 }
19
+ # step ->(options, **) { options["b"] = 2 }, name: "b"
20
+
21
+ # step( { task: D,
22
+ # outputs: { Circuit::Right => :success, Circuit::Left => :failure, ExceptionFromD => :exception }, # any outputs and their polarization, generic.
23
+ # id: :d,
24
+ # },
25
+ # :exception => MyEnd = End("End.ExceptionFromD_happened")
26
+ # )
27
+
28
+ # fail ->(options, **) { options["f"] = 4 }, id: "f"
29
+ # step ->(options, **) { options["c"] = 3 }, id: "c"
30
+ # end
31
+
32
+ # # myend ==> d
33
+ # it { Trailblazer::Operation::Inspect.(Create).gsub(/0x.+?wire_test.rb/, "").must_equal %{[>#<Proc::18 (lambda)>,>b,>d,<<f,>c]} }
34
+
35
+ # # normal flow as D sits on the Right track.
36
+ # it do
37
+ # result = Create.({}, "D_return" => Circuit::Right)
38
+
39
+ # result.inspect("a", "b", "c", "D", "f").must_equal %{<Result:true [1, 2, 3, [1, 2, nil], nil] >}
40
+ # result.event.must_equal Create.outputs.keys[1]
41
+ # end
42
+
43
+ # # ends on MyEnd, without hitting fail.
44
+ # it do
45
+ # result = Create.({}, "D_return" => ExceptionFromD)
46
+
47
+ # result.event.must_equal Create::MyEnd
48
+ # result.inspect("a", "b", "c", "D", "f").must_equal %{<Result:false [1, 2, nil, [1, 2, nil], nil] >}
49
+ # end
50
+
51
+ # # normal flow to left track.
52
+ # it do
53
+ # result = Create.({}, "D_return" => Circuit::Left)
54
+
55
+ # result.inspect("a", "b", "c", "D", "f").must_equal %{<Result:false [1, 2, nil, [1, 2, nil], 4] >}
56
+ # result.event.must_equal Create.outputs.keys[0]
57
+ # end
58
+
59
+ # #---
60
+ # #- step with Merge
61
+ # class CreateWithDefaults < Trailblazer::Operation
62
+ # step ->(options, **) { options["a"] = 1 }
63
+ # step ->(options, **) { options["b"] = 2 }, name: "b"
64
+
65
+ # step( { task: D,
66
+ # outputs: Merge( ExceptionFromD => { role: :exception } ), # any outputs and their polarization, generic.
67
+ # id: :d,
68
+ # },
69
+ # :exception => MyEnd = End("End.ExceptionFromD_happened")
70
+ # )
71
+
72
+ # fail ->(options, **) { options["f"] = 4 }, id: "f"
73
+ # step ->(options, **) { options["c"] = 3 }, id: "c"
74
+ # end
75
+
76
+ # # normal flow as D sits on the Right track.
77
+ # it do
78
+ # result = CreateWithDefaults.({}, "D_return" => Circuit::Right)
79
+
80
+ # result.inspect("a", "b", "c", "D", "f").must_equal %{<Result:true [1, 2, 3, [1, 2, nil], nil] >}
81
+ # result.event.must_equal CreateWithDefaults.outputs.keys[1]
82
+ # end
83
+
84
+ # # ends on MyEnd, without hitting fail.
85
+ # it do
86
+ # result = CreateWithDefaults.({}, "D_return" => ExceptionFromD)
87
+
88
+ # result.event.must_equal CreateWithDefaults::MyEnd
89
+ # result.inspect("a", "b", "c", "D", "f").must_equal %{<Result:false [1, 2, nil, [1, 2, nil], nil] >}
90
+ # end
91
+
92
+ # # normal flow to left track.
93
+ # it do
94
+ # result = CreateWithDefaults.({}, "D_return" => Circuit::Left)
95
+
96
+ # result.inspect("a", "b", "c", "D", "f").must_equal %{<Result:false [1, 2, nil, [1, 2, nil], 4] >}
97
+ # result.event.must_equal CreateWithDefaults.outputs.keys[0]
98
+ # end
99
+
100
+ # end
101
+
102
+ # class WireExceptionTest < Minitest::Spec
103
+ # # role in :outputs can't be connected because not in :connect_to.
104
+ # it do
105
+ # exception = assert_raises do
106
+ # class Create < Trailblazer::Operation
107
+ # step :a, outputs: { "some" => { role: :success } }, connect_to: { :not_existent => "End.success" }
108
+ # end
109
+ # end
110
+
111
+ # exception.message.must_equal %{Couldn't map output role :success for {:not_existent=>"End.success"}}
112
+ # end
113
+ # end
@@ -0,0 +1,197 @@
1
+ require "test_helper"
2
+
3
+ # class WireDefaultsTest < Minitest::Spec
4
+ # class C < Trailblazer::Operation
5
+ # # here, D has a step interface!
6
+ # D = ->(options, a:raise, b:raise, **) {
7
+ # options["D"] = [ a, b, options["c"] ]
8
+
9
+ # options["D_return"]
10
+ # }
11
+
12
+ # ExceptionFromD = Class.new(Circuit::Signal) # for steps, return value has to be subclass of Signal to be passed through as a signal and not a boolean.
13
+ # MyEnd = Class.new(Circuit::End)
14
+
15
+ # step ->(options, **) { options["a"] = 1 }, id: "a"
16
+ # step ->(options, **) { options["b"] = 2 }, id: "b"
17
+
18
+ # attach MyEnd.new(:myend), id: "End.myend"
19
+
20
+ # # step provides defaults:
21
+ # step D,
22
+ # outputs: Merge( ExceptionFromD => { role: :exception } ),
23
+ # connect_to: Merge( exception: "End.myend" ),
24
+ # id: "d"
25
+
26
+ # fail ->(options, **) { options["f"] = 4 }, id: "f"
27
+ # step ->(options, **) { options["c"] = 3 }, id: "c"
28
+ # end
29
+
30
+ # # normal flow as D sits on the Right track.
31
+ # it { C.( "D_return" => Circuit::Right).inspect("a", "b", "c", "D", "f").must_equal %{<Result:true [1, 2, 3, [1, 2, nil], nil] >} }
32
+ # # ends on MyEnd, without hitting fail.
33
+ # it { C.( "D_return" => C::ExceptionFromD).inspect("a", "b", "c", "D", "f").must_equal %{<Result:false [1, 2, nil, [1, 2, nil], nil] >} } # todo: HOW TO CHECK End instance?
34
+ # it { C.( "D_return" => Circuit::Left).inspect("a", "b", "c", "D", "f").must_equal %{<Result:false [1, 2, nil, [1, 2, nil], 4] >} } # todo: HOW TO CHECK End instance?
35
+ # it do
36
+ # step1 = C["__sequence__"][0].instructions[0].last[:node][0]
37
+ # step2 = C["__sequence__"][1].instructions[0].last[:node][0]
38
+ # step3 = C["__sequence__"][3].instructions[0].last[:node][0] # D
39
+ # step4 = C["__sequence__"][4].instructions[0].last[:node][0]
40
+ # step5 = C["__sequence__"][5].instructions[0].last[:node][0]
41
+
42
+ # require "trailblazer/activity/schema"
43
+
44
+ # Output = Trailblazer::Activity::Schema::Output
45
+
46
+ # steps = [
47
+ # [ [:success], step1, [Output.new(Circuit::Right, :success), Output.new(Circuit::Left, :failure)] ],
48
+ # [ [:success], step2, [Output.new(Circuit::Right, :success), Output.new(Circuit::Left, :failure)] ],
49
+
50
+ # [ [:success], step3, [Output.new(Circuit::Right, :success), Output.new(Circuit::Left, :failure), Output.new(C::ExceptionFromD, :exception)] ],
51
+ # [ [:exception], C::MyEnd.new(:myend), [] ],
52
+
53
+ # [ [:failure], step4, [Output.new(Circuit::Right, :failure), Output.new(Circuit::Left, :failure)] ],
54
+ # [ [:success], step5, [Output.new(Circuit::Right, :success), Output.new(Circuit::Left, :failure)] ],
55
+ # ]
56
+
57
+ # ends = [
58
+ # [ [:success], Trailblazer::Operation::Railway::End::Success.new(:success), [] ],
59
+ # [ [:failure], Trailblazer::Operation::Railway::End::Failure.new(:failure), [] ],
60
+ # ]
61
+
62
+
63
+ # graph = Trailblazer::Activity::Schema.bla(steps + ends)
64
+ # circuit = Trailblazer::Activity.new(graph)
65
+ # # pp schema
66
+
67
+ # C["__activity__"] = circuit # this is so wrong
68
+ # C.( "D_return" => Circuit::Right).inspect("a", "b", "c", "D", "f").must_equal %{<Result:true [1, 2, 3, [1, 2, nil], nil] >}
69
+
70
+ # end
71
+ # end
72
+
73
+ # # step :a
74
+ # # fail :b, connect_to: { Circuit::Right => "End.success" }
75
+ # # fail :c, connect_to: { Circuit::Right => "End.success" }
76
+
77
+ # Connect failure steps to right track, allowing to append steps after.
78
+ # @see https://github.com/trailblazer/trailblazer/issues/190#issuecomment-326992255
79
+ class WireDefaultsEarlyExitSuccessTest < Minitest::Spec
80
+ class Create < Trailblazer::Operation
81
+ step :a
82
+ fail :b, Output(:success) => :success #{}"End.success"
83
+ fail :c, Output(:success) => :success
84
+
85
+ Test.step(self, :a, :b, :c)
86
+ end
87
+
88
+ # a => true
89
+ it { Create.( a_return: true, data: []).inspect(:data).must_equal %{<Result:true [[:a]] >} }
90
+ # b => true
91
+ it { Create.( a_return: false, b_return: true, data: []).inspect(:data).must_equal %{<Result:true [[:a, :b]] >} }
92
+ # c => true
93
+ it { Create.( a_return: false, b_return: false, c_return: true, data: []).inspect(:data).must_equal %{<Result:true [[:a, :b, :c]] >} }
94
+ # a => b => c => false
95
+ it { Create.( a_return: false, b_return: false, c_return: false, data: []).inspect(:data).must_equal %{<Result:false [[:a, :b, :c]] >} }
96
+
97
+ # # require "trailblazer/developer"
98
+ # # it { Trailblazer::Developer::Client.push( operation: Create, name: "ushi" ) }
99
+
100
+
101
+ # #---
102
+ # # with => :success, steps can still be added before End.success and they will be executed.
103
+ class Update < Create
104
+ pass :d
105
+
106
+ def d(options, data:, **)
107
+ data << :d
108
+ end
109
+ end
110
+
111
+ # a => true
112
+ it { Update.( a_return: true, data: []).inspect(:data).must_equal %{<Result:true [[:a, :d]] >} }
113
+ # b => true
114
+ it { Update.( a_return: false, b_return: true, data: []).inspect(:data).must_equal %{<Result:true [[:a, :b, :d]] >} }
115
+ # c => true
116
+ it { Update.( a_return: false, b_return: false, c_return: true, data: []).inspect(:data).must_equal %{<Result:true [[:a, :b, :c, :d]] >} }
117
+ # a => b => c => false
118
+ it { Update.( a_return: false, b_return: false, c_return: false, data: []).inspect(:data).must_equal %{<Result:false [[:a, :b, :c]] >} }
119
+
120
+ #---
121
+ # failure steps reference End.success and not just the polarization. This won't call #d in failure=>success case.
122
+ class Delete < Trailblazer::Operation
123
+ step :a
124
+ fail :b, Output(:success) => "End.success"
125
+ fail :c, Output(:success) => "End.success"
126
+ pass :d
127
+
128
+ Test.step(self, :a, :b, :c)
129
+
130
+ def d(options, data:, **)
131
+ data << :d
132
+ end
133
+ end
134
+
135
+ # a => true
136
+ it { Delete.( a_return: true, data: []).inspect(:data).must_equal %{<Result:true [[:a, :d]] >} }
137
+ # b => true
138
+ it { Delete.( a_return: false, b_return: true, data: []).inspect(:data).must_equal %{<Result:true [[:a, :b]] >} }
139
+ # c => true
140
+ it { Delete.( a_return: false, b_return: false, c_return: true, data: []).inspect(:data).must_equal %{<Result:true [[:a, :b, :c]] >} }
141
+ # a => b => c => false
142
+ it { Delete.( a_return: false, b_return: false, c_return: false, data: []).inspect(:data).must_equal %{<Result:false [[:a, :b, :c]] >} }
143
+
144
+ #---
145
+ # |----|
146
+ # a --> b c--d --> E.s
147
+ # |_____|_|_______ E.f
148
+ class Connect < Trailblazer::Operation
149
+ step :a
150
+ step :b, Output(:success) => "d"
151
+ step :c, magnetic_to: [] # otherwise :success will be an open input!
152
+ pass :d, id: "d"
153
+
154
+ Test.step(self, :a, :b, :c)
155
+
156
+ def d(options, data:, **)
157
+ data << :d
158
+ end
159
+ end
160
+
161
+ it { puts Trailblazer::Activity::Magnetic::Introspect.seq( Connect ) }
162
+
163
+ # a => true
164
+ it { Connect.( a_return: true, b_return: true,data: []).inspect(:data).must_equal %{<Result:true [[:a, :b, :d]] >} }
165
+ # a => false
166
+ it { Connect.( a_return: false, data: []).inspect(:data).must_equal %{<Result:false [[:a]] >} }
167
+ # b => false
168
+ it { Connect.( a_return: true, b_return: false, data: []).inspect(:data).must_equal %{<Result:false [[:a, :b]] >} }
169
+
170
+ #---
171
+ # |---------|
172
+ # | V
173
+ # a c----d --
174
+ # |\ ^\ \
175
+ # | \ / V
176
+ # |__f____g----E.f
177
+ class Post < Trailblazer::Operation
178
+ step :a, Output(:success) => "d", id: "a"
179
+ fail :f, Output(:success) => "c"
180
+ step :c, magnetic_to: [], id: "c" # otherwise :success will be an open input!
181
+ fail :g
182
+ step :d, id: "d"
183
+
184
+ Test.step(self, :a, :f, :c, :g, :d)
185
+ end
186
+
187
+ pp Post["__sequence__"]
188
+
189
+ # a => true
190
+ it { Post.( a_return: true, d_return: true, data: []).inspect(:data).must_equal %{<Result:true [[:a, :d]] >} }
191
+ # a => false
192
+ it { Post.( a_return: false, f_return: false, g_return: nil, data: []).inspect(:data).must_equal %{<Result:false [[:a, :f, :g]] >} }
193
+ # a => false, f => true
194
+ it { Post.( a_return: false, f_return: true, c_return: true, d_return: true, data: []).inspect(:data).must_equal %{<Result:true [[:a, :f, :c, :d]] >} }
195
+ # a => false, f => true, c => false
196
+ it { Post.( a_return: false, f_return: true, c_return: false, g_return: true, data: []).inspect(:data).must_equal %{<Result:false [[:a, :f, :c, :g]] >} }
197
+ end