trailblazer-operation 0.0.13 → 0.1.1

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