trailblazer 2.0.7 → 2.1.0.beta1

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +35 -1
  3. data/Gemfile +6 -12
  4. data/README.md +3 -1
  5. data/Rakefile +6 -17
  6. data/lib/trailblazer.rb +7 -4
  7. data/lib/trailblazer/deprecation/call.rb +46 -0
  8. data/lib/trailblazer/deprecation/context.rb +43 -0
  9. data/lib/trailblazer/operation/contract.rb +40 -9
  10. data/lib/trailblazer/operation/deprecations.rb +21 -0
  11. data/lib/trailblazer/operation/guard.rb +5 -5
  12. data/lib/trailblazer/operation/model.rb +15 -10
  13. data/lib/trailblazer/operation/nested.rb +56 -85
  14. data/lib/trailblazer/operation/persist.rb +4 -2
  15. data/lib/trailblazer/operation/policy.rb +16 -7
  16. data/lib/trailblazer/operation/pundit.rb +3 -3
  17. data/lib/trailblazer/operation/representer.rb +5 -0
  18. data/lib/trailblazer/operation/rescue.rb +12 -9
  19. data/lib/trailblazer/operation/validate.rb +36 -29
  20. data/lib/trailblazer/operation/wrap.rb +49 -11
  21. data/lib/trailblazer/task.rb +20 -0
  22. data/lib/trailblazer/version.rb +1 -1
  23. data/test/benchmark.rb +63 -0
  24. data/test/deprecation/call_test.rb +42 -0
  25. data/test/deprecation/context_test.rb +19 -0
  26. data/test/docs/contract_test.rb +73 -53
  27. data/test/docs/dry_test.rb +2 -2
  28. data/test/docs/fast_test.rb +133 -13
  29. data/test/docs/guard_test.rb +28 -35
  30. data/test/docs/macro_test.rb +1 -1
  31. data/test/docs/model_test.rb +13 -13
  32. data/test/docs/nested_test.rb +54 -122
  33. data/test/docs/operation_test.rb +42 -43
  34. data/test/docs/pundit_test.rb +16 -16
  35. data/test/docs/representer_test.rb +18 -18
  36. data/test/docs/rescue_test.rb +29 -29
  37. data/test/docs/trace_test.rb +82 -0
  38. data/test/docs/wrap_test.rb +59 -26
  39. data/test/module_test.rb +75 -75
  40. data/test/nested_test.rb +293 -0
  41. data/test/operation/contract_test.rb +23 -153
  42. data/test/operation/dsl/contract_test.rb +9 -9
  43. data/test/operation/dsl/representer_test.rb +169 -169
  44. data/test/operation/model_test.rb +15 -21
  45. data/test/operation/persist_test.rb +18 -11
  46. data/test/operation/pundit_test.rb +25 -23
  47. data/test/operation/representer_test.rb +254 -254
  48. data/test/test_helper.rb +5 -2
  49. data/test/variables_test.rb +158 -0
  50. data/trailblazer.gemspec +1 -1
  51. data/untitled +33 -0
  52. metadata +25 -27
  53. data/lib/trailblazer/operation/callback.rb +0 -35
  54. data/lib/trailblazer/operation/procedural/contract.rb +0 -15
  55. data/lib/trailblazer/operation/procedural/validate.rb +0 -22
  56. data/test/operation/callback_test.rb +0 -70
  57. data/test/operation/dsl/callback_test.rb +0 -106
  58. data/test/operation/params_test.rb +0 -36
  59. data/test/operation/pipedream_test.rb +0 -59
  60. data/test/operation/pipetree_test.rb +0 -104
  61. data/test/operation/present_test.rb +0 -24
  62. data/test/operation/resolver_test.rb +0 -47
  63. data/test/operation_test.rb +0 -143
@@ -0,0 +1,82 @@
1
+ require "test_helper"
2
+
3
+ class DocsTraceTest < Minitest::Spec
4
+ Song = Struct.new(:id, :title)
5
+
6
+ class MyContract < Reform::Form
7
+ property :title
8
+ end
9
+
10
+ class Create < Trailblazer::Operation
11
+ class Present < Trailblazer::Operation
12
+ step Model( Song, :new )
13
+ step Contract::Build( constant: MyContract )
14
+ end
15
+
16
+ step Nested( Present )
17
+ step Contract::Validate( key: :song )
18
+ step Contract::Persist()
19
+ step :notify_band
20
+
21
+ def notify_band(options, **)
22
+ true
23
+ end
24
+ end
25
+
26
+ let(:params) { {} }
27
+ let(:current_user) { Module }
28
+
29
+ it do
30
+ #:trace
31
+ result = Create::Present.trace( params: params, current_user: current_user )
32
+ puts result.wtf?
33
+
34
+ # =>
35
+ # |-- Start.default
36
+ # |-- model.build
37
+ # |-- contract.build
38
+ # `-- End.success
39
+ #:trace end
40
+
41
+ result.wtf.gsub(/0x\w+/, "").must_equal %{|-- #<Trailblazer::Activity::Start:>
42
+ |-- model.build
43
+ |-- contract.build
44
+ `-- #<Trailblazer::Operation::Railway::End::Success:>}
45
+ end
46
+
47
+ it do
48
+ #:trace-cpx
49
+ result = Create.trace( params: params, current_user: current_user )
50
+ puts result.wtf?
51
+
52
+ # =>
53
+ # |-- #<Trailblazer::Activity::Nested:0x000000031960d8>
54
+ # | |-- Start.default
55
+ # | |-- model.build
56
+ # | |-- contract.build
57
+ # | |-- End.success
58
+ # | `-- #<Trailblazer::Activity::Nested:0x000000031960d8>
59
+ # |-- #<Trailblazer::Activity::Nested:0x0000000311f3e8>
60
+ # | |-- Start.default
61
+ # | |-- contract.default.params
62
+ # | |-- End.failure
63
+ # | `-- #<Trailblazer::Activity::Nested:0x0000000311f3e8>
64
+ # `-- #<Trailblazer::Operation::Railway::End::Failure:0x00000003201fb8>
65
+ #:trace-cpx end
66
+
67
+ # result.wtf?.must_equal %{|-- Start.default
68
+ # |-- model.build
69
+ # |-- contract.build
70
+ # `-- End.success}
71
+ end
72
+
73
+ # operation = ->(*args) { Create.__call__(*args) }
74
+
75
+ # stack, _ = Trailblazer::Circuit::Trace.(
76
+ # operation,
77
+ # nil,
78
+ # options={ a_return: true },
79
+ # )
80
+
81
+ # puts output = Circuit::Trace::Present.tree(stack)
82
+ end
@@ -1,5 +1,7 @@
1
1
  require "test_helper"
2
2
 
3
+ # TODO: consume End signal from wrapped
4
+
3
5
  class WrapTest < Minitest::Spec
4
6
  Song = Struct.new(:id, :title) do
5
7
  def self.find(id)
@@ -7,6 +9,34 @@ class WrapTest < Minitest::Spec
7
9
  end
8
10
  end
9
11
 
12
+ class DirectWiringTest < Minitest::Spec
13
+ class Create < Trailblazer::Operation
14
+ class MyContract < Reform::Form
15
+ property :title
16
+ end
17
+
18
+ step( Wrap( ->(options, *args, &block) {
19
+ begin
20
+ block.call
21
+ rescue => exception
22
+ options["result.model.find"] = "argh! because #{exception.class}"
23
+ [ Railway.fail_fast!, options, *args ]
24
+ end }) {
25
+ step ->(options, **) { options["x"] = true }
26
+ step Model( Song, :find )
27
+ step Contract::Build( constant: MyContract )
28
+ }.merge(fast_track: true))
29
+ step Contract::Validate()
30
+ step Contract::Persist( method: :sync )
31
+ end
32
+
33
+ it { Create.( params: {id: 1, title: "Prodigal Son"} ).inspect("x", :model).must_equal %{<Result:true [true, #<struct WrapTest::Song id=1, title=\"Prodigal Son\">] >} }
34
+
35
+ it "goes directly from Wrap to End.fail_fast" do
36
+ Create.(params: {}).inspect("x", :model, "result.model.find").must_equal %{<Result:false [true, nil, "argh! because RuntimeError"] >}
37
+ end
38
+ end
39
+
10
40
  class Create < Trailblazer::Operation
11
41
  class MyContract < Reform::Form
12
42
  property :title
@@ -26,26 +56,26 @@ class WrapTest < Minitest::Spec
26
56
  step Contract::Persist( method: :sync )
27
57
  end
28
58
 
29
- it { Create.( id: 1, title: "Prodigal Son" )["contract.default"].model.inspect.must_equal %{#<struct WrapTest::Song id=1, title="Prodigal Son">} }
30
- it { Create.( id: nil ).inspect("result.model.find").must_equal %{<Result:false [\"argh! because RuntimeError\"] >} }
59
+ it { Create.( params: {id: 1, title: "Prodigal Son"} )["contract.default"].model.inspect.must_equal %{#<struct WrapTest::Song id=1, title="Prodigal Son">} }
60
+ it { Create.( params: {id: nil }).inspect("result.model.find").must_equal %{<Result:false [\"argh! because RuntimeError\"] >} }
31
61
 
32
62
  #-
33
63
  # Wrap return
34
64
  class WrapReturnTest < Minitest::Spec
35
65
  class Create < Trailblazer::Operation
36
66
  step Wrap ->(options, *, &block) { options["yield?"] ? block.call : false } {
37
- step ->(options) { options["x"] = true }
67
+ step ->(options, **) { options["x"] = true }
38
68
  success :noop!
39
69
  # ...
40
70
  }
41
71
 
42
- def noop!(options)
72
+ def noop!(options, **)
43
73
  end
44
74
  end
45
75
 
46
- it { Create.().inspect("x").must_equal %{<Result:false [nil] >} }
76
+ it { Create.(params: {}).inspect("x").must_equal %{<Result:false [nil] >} }
47
77
  # returns falsey means deviate to left.
48
- it { Create.({}, "yield?" => true).inspect("x").must_equal %{<Result:true [true] >} }
78
+ it { Create.("yield?" => true).inspect("x").must_equal %{<Result:true [true] >} }
49
79
  end
50
80
 
51
81
  class WrapWithCallableTest < Minitest::Spec
@@ -59,27 +89,16 @@ class WrapTest < Minitest::Spec
59
89
 
60
90
  class Create < Trailblazer::Operation
61
91
  step Wrap( MyWrapper ) {
62
- step ->(options) { options["x"] = true }
92
+ step ->(options, **) { options["x"] = true }
63
93
  # ...
64
94
  }
65
95
  end
66
96
 
67
- it { Create.().inspect("x").must_equal %{<Result:false [nil] >} }
97
+ it { Create.(params: {}).inspect("x").must_equal %{<Result:false [nil] >} }
68
98
  # returns falsey means deviate to left.
69
- it { Create.({}, "yield?" => true).inspect("x").must_equal %{<Result:true [true] >} }
99
+ it { Create.("yield?" => true).inspect("x").must_equal %{<Result:true [true] >} }
70
100
  end
71
101
 
72
- #-
73
- # arguments for Wrap
74
- class Update < Trailblazer::Operation
75
- step Wrap ->(options, operation, pipe, &block) { operation["yield?"] ? block.call : false } {
76
- step ->(options) { options["x"] = true }
77
- }
78
- end
79
-
80
- it { Update.().inspect("x").must_equal %{<Result:false [nil] >} }
81
- it { Update.({}, "yield?" => true).inspect("x").must_equal %{<Result:true [true] >} }
82
-
83
102
  class WrapExampleProcTest < Minitest::Spec
84
103
  module Sequel
85
104
  def self.transaction
@@ -115,14 +134,14 @@ class WrapTest < Minitest::Spec
115
134
  # handle errors after the wrap
116
135
  end
117
136
 
118
- def notify!(options)
137
+ def notify!(options, **)
119
138
  MyNotifier.mail
120
139
  end
121
140
  #~wrap-only end
122
141
  end
123
142
  #:sequel-transaction end
124
143
 
125
- it { Create.( title: "Pie" ).inspect("model", "x", "err").must_equal %{<Result:true [#<struct WrapTest::Song id=nil, title=\"Pie\">, nil, nil] >} }
144
+ it { Create.( params: {title: "Pie"} ).inspect(:model, "x", "err").must_equal %{<Result:true [#<struct WrapTest::Song id=nil, title=\"Pie\">, nil, nil] >} }
126
145
  end
127
146
 
128
147
  class WrapExampleCallableTest < Minitest::Spec
@@ -138,8 +157,6 @@ class WrapTest < Minitest::Spec
138
157
 
139
158
  #:callable-t
140
159
  class MyTransaction
141
- extend Uber::Callable
142
-
143
160
  def self.call(options, *)
144
161
  Sequel.transaction { yield } # yield runs the nested pipe.
145
162
  # return value decides about left or right track!
@@ -170,14 +187,30 @@ class WrapTest < Minitest::Spec
170
187
  # handle errors after the wrap
171
188
  end
172
189
 
173
- def notify!(options)
190
+ def notify!(options, **)
174
191
  MyNotifier.mail # send emails, because success...
175
192
  end
176
193
  #~wrap-onlyy end
177
194
  end
178
195
  #:sequel-transaction-callable end
179
196
 
180
- it { Create.( title: "Pie" ).inspect("model", "x", "err").must_equal %{<Result:true [#<struct WrapTest::Song id=nil, title=\"Pie\">, nil, nil] >} }
197
+ it { Create.( params: {title: "Pie"} ).inspect(:model, "x", "err").must_equal %{<Result:true [#<struct WrapTest::Song id=nil, title=\"Pie\">, nil, nil] >} }
198
+ end
199
+
200
+ class WrapWithMethodTest < Minitest::Spec
201
+ class Create < Trailblazer::Operation
202
+ step Model( Song, :new )
203
+ step Wrap ->(options, *, &block) { block.call } {
204
+ step :check_model!
205
+
206
+ }
207
+
208
+ def check_model!(options, model:, **)
209
+ options["x"] = model
210
+ end
211
+ end
212
+
213
+ it { Create.(params: {}) }
181
214
  end
182
215
  end
183
216
 
@@ -1,100 +1,100 @@
1
- require 'test_helper'
2
- require "trailblazer/operation/module"
3
- require "trailblazer/operation/callback"
4
- require "trailblazer/operation/contract"
1
+ # require 'test_helper'
2
+ # require "trailblazer/operation/module"
3
+ # require "trailblazer/operation/callback"
4
+ # require "trailblazer/operation/contract"
5
5
 
6
- class OperationModuleTest < MiniTest::Spec
7
- Song = Struct.new(:name, :artist)
8
- Artist = Struct.new(:id, :full_name)
6
+ # class OperationModuleTest < MiniTest::Spec
7
+ # Song = Struct.new(:name, :artist)
8
+ # Artist = Struct.new(:id, :full_name)
9
9
 
10
- class Create < Trailblazer::Operation
11
- include Trailblazer::Operation::Callback
12
- include Contract::Explicit
10
+ # class Create < Trailblazer::Operation
11
+ # include Trailblazer::Operation::Callback
12
+ # include Contract::Explicit
13
13
 
14
- contract do
15
- property :name
16
- property :artist, populate_if_empty: Artist do
17
- property :id
18
- end
19
- end
14
+ # contract do
15
+ # property :name
16
+ # property :artist, populate_if_empty: Artist do
17
+ # property :id
18
+ # end
19
+ # end
20
20
 
21
- callback do
22
- on_change :notify_me!
23
- end
21
+ # callback do
22
+ # on_change :notify_me!
23
+ # end
24
24
 
25
- attr_reader :model
26
- def call(params)
27
- self["model"] = Song.new
25
+ # attr_reader :model
26
+ # def call(params)
27
+ # self["model"] = Song.new
28
28
 
29
- validate(params, model: self["model"]) do
30
- contract.sync
29
+ # validate(params, model: self["model"]) do
30
+ # contract.sync
31
31
 
32
- dispatch!
33
- end
32
+ # dispatch!
33
+ # end
34
34
 
35
- self
36
- end
35
+ # self
36
+ # end
37
37
 
38
- def dispatched
39
- self["dispatched"] ||= []
40
- end
38
+ # def dispatched
39
+ # self["dispatched"] ||= []
40
+ # end
41
41
 
42
- private
43
- def notify_me!(*)
44
- dispatched << :notify_me!
45
- end
46
- end
42
+ # private
43
+ # def notify_me!(*)
44
+ # dispatched << :notify_me!
45
+ # end
46
+ # end
47
47
 
48
48
 
49
- module SignedIn
50
- include Trailblazer::Operation::Module
49
+ # module SignedIn
50
+ # include Trailblazer::Operation::Module
51
51
 
52
- contract do
53
- property :artist, inherit: true do
54
- property :full_name
52
+ # contract do
53
+ # property :artist, inherit: true do
54
+ # property :full_name
55
55
 
56
- puts definitions.inspect
57
- end
58
- end
56
+ # puts definitions.inspect
57
+ # end
58
+ # end
59
59
 
60
- callback do
61
- on_change :notify_you!
62
- end
60
+ # callback do
61
+ # on_change :notify_you!
62
+ # end
63
63
 
64
- def notify_you!(*)
65
- dispatched << :notify_you!
66
- end
67
- end
64
+ # def notify_you!(*)
65
+ # dispatched << :notify_you!
66
+ # end
67
+ # end
68
68
 
69
69
 
70
- class Update < Create
71
- callback do
72
- on_change :notify_them!
73
- end
70
+ # class Update < Create
71
+ # callback do
72
+ # on_change :notify_them!
73
+ # end
74
74
 
75
- include SignedIn
75
+ # include SignedIn
76
76
 
77
- def notify_them!(*)
78
- dispatched << :notify_them!
79
- end
80
- end
77
+ # def notify_them!(*)
78
+ # dispatched << :notify_them!
79
+ # end
80
+ # end
81
81
 
82
82
 
83
- it do
84
- op = Create.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
83
+ # it do
84
+ # op = Create.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
85
85
 
86
- op["dispatched"].must_equal [:notify_me!]
87
- op["model"].name.must_equal "Feelings"
88
- op["model"].artist.id.must_equal 1
89
- op["model"].artist.full_name.must_equal nil # property not declared.
90
- end
86
+ # op["dispatched"].must_equal [:notify_me!]
87
+ # op["model"].name.must_equal "Feelings"
88
+ # op["model"].artist.id.must_equal 1
89
+ # op["model"].artist.full_name.must_equal nil # property not declared.
90
+ # end
91
91
 
92
- it do
93
- op = Update.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
92
+ # it do
93
+ # op = Update.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
94
94
 
95
- op["dispatched"].must_equal [:notify_me!, :notify_them!, :notify_you!]
96
- op["model"].name.must_equal "Feelings"
97
- op["model"].artist.id.must_equal 1
98
- op["model"].artist.full_name.must_equal "The Offspring" # property declared via Module.
99
- end
100
- end
95
+ # op["dispatched"].must_equal [:notify_me!, :notify_them!, :notify_you!]
96
+ # op["model"].name.must_equal "Feelings"
97
+ # op["model"].artist.id.must_equal 1
98
+ # op["model"].artist.full_name.must_equal "The Offspring" # property declared via Module.
99
+ # end
100
+ # end
@@ -0,0 +1,293 @@
1
+ require "test_helper"
2
+
3
+ # exec_context nach Nested
4
+
5
+ class NestedTest < Minitest::Spec
6
+ #---
7
+ #- shared data
8
+ class B < Trailblazer::Operation
9
+ pass ->(options, **) { options["can.B.see.A.mutable.data?"] = options["mutable.data.from.A"] }
10
+ pass ->(options, **) { options["can.B.see.current_user?"] = options["current_user"] }
11
+ pass ->(options, **) { options["can.B.see.params?"] = options["params"] }
12
+ pass ->(options, **) { options["can.B.see.A.class.data?"] = options["A.class.data"] }
13
+ pass ->(options, **) { options["can.B.see.container.data?"] = options["some.container.data"] }
14
+ pass ->(options, **) { options["mutable.data.from.B"] = "from B!" }
15
+ end
16
+
17
+ class A < Trailblazer::Operation
18
+ extend ClassDependencies
19
+ self["A.class.data"] = "yes" # class data on A
20
+
21
+ pass ->(options, **) { options["mutable.data.from.A"] = "from A!" } # mutable data on A
22
+ step Nested( B )
23
+ pass ->(options, **) { options["can.A.see.B.mutable.data?"] = options["mutable.data.from.B"] }
24
+ end
25
+
26
+ #---
27
+ #- default behavior: share everything.
28
+ # no containers
29
+ # no runtime data
30
+ # no params
31
+ it do
32
+ result = A.("params" => {})
33
+ # everything from A visible
34
+ result["A.class.data"]. must_equal "yes"
35
+ result["mutable.data.from.A"].must_equal "from A!"
36
+
37
+ # B can see everything
38
+ result["can.B.see.A.mutable.data?"].must_equal "from A!"
39
+ result["can.B.see.current_user?"].must_be_nil
40
+ result["can.B.see.params?"].must_equal({})
41
+ result["can.B.see.A.class.data?"].must_equal "yes"
42
+ result["can.B.see.container.data?"].must_be_nil
43
+
44
+ result["can.A.see.B.mutable.data?"].must_equal "from B!"
45
+ end
46
+
47
+ #---
48
+ #- Nested::NonActivity
49
+ class AlmostB < Trailblazer::Operation
50
+ step ->(options, is_successful:raise, **) { is_successful } # {AlmostB} fails if {is_successful} isn't true.
51
+ step ->(options, **) { options["can.B.see.A.mutable.data?"] = options["mutable.data.from.A"] }
52
+ pass ->(options, **) { options["mutable.data.from.B"] = "from AlmostB!" }
53
+ end
54
+
55
+ #- Nested( ->{} )
56
+ class SomeNestedWithProc < Trailblazer::Operation
57
+ extend ClassDependencies
58
+ self["A.class.data"] = "yes" # class data on A
59
+
60
+ Decider = ->(options, use_class:raise, **) { use_class }
61
+
62
+ pass ->(options, **) { options["mutable.data.from.A"] = "from A!" } # mutable data on A
63
+ step Nested( Decider )
64
+ pass ->(options, **) { options["can.A.see.B.mutable.data?"] = options["mutable.data.from.B"] }
65
+ end
66
+
67
+ #- Nested( Callable )
68
+ class SomeNestedWithCallable < Trailblazer::Operation
69
+ extend ClassDependencies
70
+ self["A.class.data"] = "yes" # class data on A
71
+
72
+ class Decider
73
+ def self.call(options, use_class:raise, **)
74
+ use_class
75
+ end
76
+ end
77
+
78
+ pass ->(options, **) { options["mutable.data.from.A"] = "from A!" } # mutable data on A
79
+ step Nested( Decider )
80
+ pass ->(options, **) { options["can.A.see.B.mutable.data?"] = options["mutable.data.from.B"] }
81
+ end
82
+
83
+ #- Nested( :method )
84
+ class SomeNestedWithMethod < Trailblazer::Operation
85
+ extend ClassDependencies
86
+ self["A.class.data"] = "yes" # class data on A
87
+
88
+ def decider(options, use_class:raise, **)
89
+ use_class
90
+ end
91
+
92
+ pass ->(options, **) { options["mutable.data.from.A"] = "from A!" } # mutable data on A
93
+ step Nested( :decider )
94
+ pass ->(options, **) { options["can.A.see.B.mutable.data?"] = options["mutable.data.from.B"] }
95
+ end
96
+
97
+
98
+ #- test callable
99
+ # B with Callable, successful
100
+ it do
101
+ result = SomeNestedWithCallable.("params" => {}, use_class: B)
102
+ assert_b(result, is_successful: "whatever")
103
+ end
104
+
105
+ # AlmostB with Callable, successful
106
+ it do
107
+ result = SomeNestedWithCallable.("params" => {}, use_class: AlmostB, is_successful: true)
108
+ assert_almost_b(result, is_successful: true)
109
+ end
110
+
111
+ # AlmostB with Callable, failure
112
+ it do
113
+ result = SomeNestedWithCallable.("params" => {}, use_class: AlmostB, is_successful: false)
114
+ assert_almost_b(result, is_successful: false)
115
+ end
116
+
117
+ #- test proc
118
+ # B with proc, successful
119
+ it do
120
+ result = SomeNestedWithProc.("params" => {}, use_class: B)
121
+ assert_b(result, is_successful: "whatever")
122
+ end
123
+
124
+ # AlmostB with proc, successful
125
+ it do
126
+ result = SomeNestedWithProc.("params" => {}, use_class: AlmostB, is_successful: true)
127
+
128
+ assert_almost_b(result, is_successful: true)
129
+ end
130
+
131
+ # AlmostB with proc, failure.
132
+ it do
133
+ result = SomeNestedWithProc.("params" => {}, use_class: AlmostB, is_successful: false)
134
+
135
+ assert_almost_b(result, is_successful: false)
136
+ end
137
+
138
+ #- test :method
139
+ # B with method, successful
140
+ it do
141
+ result = SomeNestedWithMethod.("params" => {}, use_class: B)
142
+ assert_b(result, is_successful: "whatever")
143
+ end
144
+
145
+ # AlmostB with method, successful
146
+ it do
147
+ result = SomeNestedWithMethod.("params" => {}, use_class: AlmostB, is_successful: true)
148
+
149
+ assert_almost_b(result, is_successful: true)
150
+ end
151
+
152
+ # AlmostB with method, failure.
153
+ it do
154
+ result = SomeNestedWithMethod.("params" => {}, use_class: AlmostB, is_successful: false)
155
+
156
+ assert_almost_b(result, is_successful: false)
157
+ end
158
+
159
+
160
+ def assert_almost_b(result, is_successful:raise)
161
+ result.success?.must_equal is_successful # AlmostB was successful, so A is successful.
162
+
163
+ # everything from A visible
164
+ result["A.class.data"]. must_equal "yes"
165
+ result["mutable.data.from.A"].must_equal "from A!"
166
+
167
+ # AlmostB doesn't look for everything
168
+ result["can.B.see.current_user?"].must_be_nil
169
+ result["can.B.see.params?"].must_be_nil
170
+ if is_successful
171
+ result["can.B.see.A.mutable.data?"].must_equal "from A!"
172
+ result["can.B.see.A.class.data?"].must_equal nil # we don't look for it.
173
+ result["can.A.see.B.mutable.data?"].must_equal "from AlmostB!"
174
+ else
175
+ result["can.B.see.A.mutable.data?"].must_be_nil
176
+ result["can.B.see.A.class.data?"].must_be_nil
177
+ result["can.A.see.B.mutable.data?"].must_be_nil
178
+ end
179
+ result["can.B.see.container.data?"].must_be_nil
180
+
181
+
182
+ # result[:is_successful].must_equal is_successful # FIXME: this is wrong!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! key is symbol
183
+ end
184
+
185
+ def assert_b(result, is_successful:raise)
186
+ # everything from A visible
187
+ result["A.class.data"]. must_equal "yes"
188
+ result["mutable.data.from.A"].must_equal "from A!"
189
+
190
+ # B can see everything
191
+ result["can.B.see.A.mutable.data?"].must_equal "from A!"
192
+ result["can.B.see.current_user?"].must_be_nil
193
+ result["can.B.see.params?"].must_equal({})
194
+ result["can.B.see.A.class.data?"].must_equal "yes"
195
+ result["can.B.see.container.data?"].must_be_nil
196
+
197
+ result["can.A.see.B.mutable.data?"].must_equal "from B!"
198
+
199
+ result[:is_successful].must_be_nil
200
+ result.success?.must_equal true # B was successful, so A is successful.
201
+ end
202
+
203
+
204
+ #---
205
+ #- :exec_context
206
+ class Create < Trailblazer::Operation
207
+ class Edit < Trailblazer::Operation
208
+ step :c!
209
+
210
+ def c!(options, **); options[:c] = 1 end
211
+ end
212
+
213
+ step :a!
214
+ step Nested( Edit )
215
+ step :b!
216
+
217
+ def a!(options, **); options[:a] = 2 end
218
+ def b!(options, **); options[:b] = 3 end
219
+ end
220
+
221
+ it { Create.().inspect(:a, :b, :c).must_equal %{<Result:true [2, 3, 1] >} }
222
+ end
223
+
224
+ class NestedWithFastTrackTest < Minitest::Spec
225
+ module Steps
226
+ def b(options, a:, **)
227
+ options["b"] = a+1
228
+ end
229
+
230
+ def f(options, **)
231
+ options["f"] = 3
232
+ end
233
+ end
234
+
235
+ class Edit < Trailblazer::Operation
236
+ pass :a, pass_fast: true
237
+
238
+ def a(options, **)
239
+ options["a"] = 1
240
+ end
241
+ end
242
+
243
+ class Update < Trailblazer::Operation
244
+ step Nested( Edit )
245
+ step :b
246
+ fail :f
247
+
248
+ include Steps
249
+ end
250
+
251
+ # from Nested straight to End.pass_fast.
252
+ it { Update.({}).inspect("a", "b", "f").must_equal %{<Result:true [1, nil, nil] >} }
253
+
254
+ #- Nested, pass_fast: true
255
+ class Upsert < Trailblazer::Operation
256
+ step Nested( Edit ), pass_fast: true # this option is unnecessary.
257
+ step :b
258
+ fail :f
259
+
260
+ include Steps
261
+ end
262
+
263
+ # from Nested straight to End.pass_fast.
264
+ it { Upsert.({}).inspect("a", "b", "f").must_equal %{<Result:true [1, nil, nil] >} }
265
+
266
+ #- mapping
267
+ #- Nested, :pass_fast => :failure
268
+ it "attaches :pass_fast => :failure" do
269
+ op = Class.new(Trailblazer::Operation) do
270
+ step Nested( Edit ), Output(:pass_fast) => :failure
271
+ step :b
272
+ fail :f
273
+
274
+ include Steps
275
+ end
276
+
277
+ # from Nested to :failure track.
278
+ op.({}).inspect("a", "b", "c", "f").must_equal %{<Result:false [1, nil, nil, 3] >}
279
+ end
280
+
281
+ it "goes straigt to End.failure" do
282
+ op = Class.new(Trailblazer::Operation) do
283
+ step Nested( Edit ), Output(:pass_fast) => "End.failure"
284
+ step :b
285
+ fail :f
286
+
287
+ include Steps
288
+ end
289
+
290
+ # from Nested straight to End.failure, no fail step will be visited.
291
+ op.({}).inspect("a", "b", "c", "f").must_equal %{<Result:false [1, nil, nil, nil] >}
292
+ end
293
+ end