trailblazer 2.0.7 → 2.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +35 -1
- data/Gemfile +6 -12
- data/README.md +3 -1
- data/Rakefile +6 -17
- data/lib/trailblazer.rb +7 -4
- data/lib/trailblazer/deprecation/call.rb +46 -0
- data/lib/trailblazer/deprecation/context.rb +43 -0
- data/lib/trailblazer/operation/contract.rb +40 -9
- data/lib/trailblazer/operation/deprecations.rb +21 -0
- data/lib/trailblazer/operation/guard.rb +5 -5
- data/lib/trailblazer/operation/model.rb +15 -10
- data/lib/trailblazer/operation/nested.rb +56 -85
- data/lib/trailblazer/operation/persist.rb +4 -2
- data/lib/trailblazer/operation/policy.rb +16 -7
- data/lib/trailblazer/operation/pundit.rb +3 -3
- data/lib/trailblazer/operation/representer.rb +5 -0
- data/lib/trailblazer/operation/rescue.rb +12 -9
- data/lib/trailblazer/operation/validate.rb +36 -29
- data/lib/trailblazer/operation/wrap.rb +49 -11
- data/lib/trailblazer/task.rb +20 -0
- data/lib/trailblazer/version.rb +1 -1
- data/test/benchmark.rb +63 -0
- data/test/deprecation/call_test.rb +42 -0
- data/test/deprecation/context_test.rb +19 -0
- data/test/docs/contract_test.rb +73 -53
- data/test/docs/dry_test.rb +2 -2
- data/test/docs/fast_test.rb +133 -13
- data/test/docs/guard_test.rb +28 -35
- data/test/docs/macro_test.rb +1 -1
- data/test/docs/model_test.rb +13 -13
- data/test/docs/nested_test.rb +54 -122
- data/test/docs/operation_test.rb +42 -43
- data/test/docs/pundit_test.rb +16 -16
- data/test/docs/representer_test.rb +18 -18
- data/test/docs/rescue_test.rb +29 -29
- data/test/docs/trace_test.rb +82 -0
- data/test/docs/wrap_test.rb +59 -26
- data/test/module_test.rb +75 -75
- data/test/nested_test.rb +293 -0
- data/test/operation/contract_test.rb +23 -153
- data/test/operation/dsl/contract_test.rb +9 -9
- data/test/operation/dsl/representer_test.rb +169 -169
- data/test/operation/model_test.rb +15 -21
- data/test/operation/persist_test.rb +18 -11
- data/test/operation/pundit_test.rb +25 -23
- data/test/operation/representer_test.rb +254 -254
- data/test/test_helper.rb +5 -2
- data/test/variables_test.rb +158 -0
- data/trailblazer.gemspec +1 -1
- data/untitled +33 -0
- metadata +25 -27
- data/lib/trailblazer/operation/callback.rb +0 -35
- data/lib/trailblazer/operation/procedural/contract.rb +0 -15
- data/lib/trailblazer/operation/procedural/validate.rb +0 -22
- data/test/operation/callback_test.rb +0 -70
- data/test/operation/dsl/callback_test.rb +0 -106
- data/test/operation/params_test.rb +0 -36
- data/test/operation/pipedream_test.rb +0 -59
- data/test/operation/pipetree_test.rb +0 -104
- data/test/operation/present_test.rb +0 -24
- data/test/operation/resolver_test.rb +0 -47
- 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
|
data/test/docs/wrap_test.rb
CHANGED
@@ -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.(
|
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.(
|
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(
|
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(
|
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
|
|
data/test/module_test.rb
CHANGED
@@ -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
|
-
|
8
|
-
|
6
|
+
# class OperationModuleTest < MiniTest::Spec
|
7
|
+
# Song = Struct.new(:name, :artist)
|
8
|
+
# Artist = Struct.new(:id, :full_name)
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
# class Create < Trailblazer::Operation
|
11
|
+
# include Trailblazer::Operation::Callback
|
12
|
+
# include Contract::Explicit
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
# contract do
|
15
|
+
# property :name
|
16
|
+
# property :artist, populate_if_empty: Artist do
|
17
|
+
# property :id
|
18
|
+
# end
|
19
|
+
# end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
# callback do
|
22
|
+
# on_change :notify_me!
|
23
|
+
# end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
# attr_reader :model
|
26
|
+
# def call(params)
|
27
|
+
# self["model"] = Song.new
|
28
28
|
|
29
|
-
|
30
|
-
|
29
|
+
# validate(params, model: self["model"]) do
|
30
|
+
# contract.sync
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
# dispatch!
|
33
|
+
# end
|
34
34
|
|
35
|
-
|
36
|
-
|
35
|
+
# self
|
36
|
+
# end
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
# def dispatched
|
39
|
+
# self["dispatched"] ||= []
|
40
|
+
# end
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
# private
|
43
|
+
# def notify_me!(*)
|
44
|
+
# dispatched << :notify_me!
|
45
|
+
# end
|
46
|
+
# end
|
47
47
|
|
48
48
|
|
49
|
-
|
50
|
-
|
49
|
+
# module SignedIn
|
50
|
+
# include Trailblazer::Operation::Module
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
# contract do
|
53
|
+
# property :artist, inherit: true do
|
54
|
+
# property :full_name
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
# puts definitions.inspect
|
57
|
+
# end
|
58
|
+
# end
|
59
59
|
|
60
|
-
|
61
|
-
|
62
|
-
|
60
|
+
# callback do
|
61
|
+
# on_change :notify_you!
|
62
|
+
# end
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
# def notify_you!(*)
|
65
|
+
# dispatched << :notify_you!
|
66
|
+
# end
|
67
|
+
# end
|
68
68
|
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
70
|
+
# class Update < Create
|
71
|
+
# callback do
|
72
|
+
# on_change :notify_them!
|
73
|
+
# end
|
74
74
|
|
75
|
-
|
75
|
+
# include SignedIn
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
77
|
+
# def notify_them!(*)
|
78
|
+
# dispatched << :notify_them!
|
79
|
+
# end
|
80
|
+
# end
|
81
81
|
|
82
82
|
|
83
|
-
|
84
|
-
|
83
|
+
# it do
|
84
|
+
# op = Create.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
93
|
-
|
92
|
+
# it do
|
93
|
+
# op = Update.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
|
94
94
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
data/test/nested_test.rb
ADDED
@@ -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
|