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.
- 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
|