trailblazer-macro 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.
@@ -0,0 +1,133 @@
1
+ require "test_helper"
2
+
3
+ #:policy
4
+ class MyPolicy
5
+ def initialize(user, model)
6
+ @user, @model = user, model
7
+ end
8
+
9
+ def create?
10
+ @user == Module && @model.id.nil?
11
+ end
12
+
13
+ def new?
14
+ @user == Class
15
+ end
16
+ end
17
+ #:policy end
18
+
19
+ #--
20
+ # with policy
21
+ class DocsPunditProcTest < Minitest::Spec
22
+ Song = Struct.new(:id)
23
+
24
+ #:pundit
25
+ class Create < Trailblazer::Operation
26
+ step Model( Song, :new )
27
+ step Policy::Pundit( MyPolicy, :create? )
28
+ # ...
29
+ end
30
+ #:pundit end
31
+
32
+ it { Trailblazer::Operation::Inspect.(Create).must_equal %{[>model.build,>policy.default.eval]} }
33
+ it { Create.(params: {}, current_user: Module).inspect(:model).must_equal %{<Result:true [#<struct DocsPunditProcTest::Song id=nil>] >} }
34
+ it { Create.(params: {} ).inspect(:model).must_equal %{<Result:false [#<struct DocsPunditProcTest::Song id=nil>] >} }
35
+
36
+ it do
37
+ #:pundit-result
38
+ result = Create.(params: {}, current_user: Module)
39
+ result["result.policy.default"].success? #=> true
40
+ result["result.policy.default"]["policy"] #=> #<MyPolicy ...>
41
+ #:pundit-result end
42
+ result["result.policy.default"].success?.must_equal true
43
+ result["result.policy.default"]["policy"].is_a?(MyPolicy).must_equal true
44
+ end
45
+
46
+ #---
47
+ #- override
48
+ class New < Create
49
+ step Policy::Pundit( MyPolicy, :new? ), override: true
50
+ end
51
+
52
+ it { Trailblazer::Operation::Inspect.(New).must_equal %{[>model.build,>policy.default.eval]} }
53
+ it { New.(params: {}, current_user: Class ).inspect(:model).must_equal %{<Result:true [#<struct DocsPunditProcTest::Song id=nil>] >} }
54
+ it { New.(params: {}, current_user: nil ).inspect(:model).must_equal %{<Result:false [#<struct DocsPunditProcTest::Song id=nil>] >} }
55
+
56
+ #---
57
+ #- override with :name
58
+ class Edit < Trailblazer::Operation
59
+ step Policy::Pundit( MyPolicy, :create?, name: "first" )
60
+ step Policy::Pundit( MyPolicy, :new?, name: "second" )
61
+ end
62
+
63
+ class Update < Edit
64
+ step Policy::Pundit( MyPolicy, :new?, name: "first" ), override: true
65
+ end
66
+
67
+ it { Trailblazer::Operation::Inspect.(Edit).must_equal %{[>policy.first.eval,>policy.second.eval]} }
68
+ it { Edit.(params: {}, current_user: Class).inspect(:model).must_equal %{<Result:false [nil] >} }
69
+ it { Trailblazer::Operation::Inspect.(Update).must_equal %{[>policy.first.eval,>policy.second.eval]} }
70
+ it { Update.(params: {}, current_user: Class).inspect(:model).must_equal %{<Result:true [nil] >} }
71
+
72
+ #---
73
+ # dependency injection
74
+ class AnotherPolicy < MyPolicy
75
+ def create?
76
+ true
77
+ end
78
+ end
79
+
80
+ it {
81
+ result =
82
+ #:di-call
83
+ Create.(params: {},
84
+ current_user: Module,
85
+ "policy.default.eval" => Trailblazer::Operation::Policy::Pundit.build(AnotherPolicy, :create?)
86
+ )
87
+ #:di-call end
88
+ result.inspect("").must_equal %{<Result:true [nil] >} }
89
+ end
90
+
91
+ #-
92
+ # with name:
93
+ class PunditWithNameTest < Minitest::Spec
94
+ Song = Struct.new(:id)
95
+
96
+ #:name
97
+ class Create < Trailblazer::Operation
98
+ step Model( Song, :new )
99
+ step Policy::Pundit( MyPolicy, :create?, name: "after_model" )
100
+ # ...
101
+ end
102
+ #:name end
103
+
104
+ it {
105
+ #:name-call
106
+ result = Create.(params: {}, current_user: Module)
107
+ result["result.policy.after_model"].success? #=> true
108
+ #:name-call end
109
+ result["result.policy.after_model"].success?.must_equal true }
110
+ end
111
+
112
+ #---
113
+ # class-level guard
114
+ # class DocsGuardClassLevelTest < Minitest::Spec
115
+ # #:class-level
116
+ # class Create < Trailblazer::Operation
117
+ # step Policy::Guard[ ->(options) { options["current_user"] == Module } ],
118
+ # before: "operation.new"
119
+ # #~pipe--only
120
+ # step ->(options) { options["x"] = true }
121
+ # #~pipe--only end
122
+ # end
123
+ # #:class-level end
124
+
125
+ # it { Create.(); Create["result.policy"].must_be_nil }
126
+ # it { Create.(params: {}, current_user: Module)["x"].must_equal true }
127
+ # it { Create.(params: {} )["x"].must_be_nil }
128
+ # end
129
+
130
+
131
+
132
+ # TODO:
133
+ #policy.default
@@ -0,0 +1,126 @@
1
+ require "test_helper"
2
+
3
+ class NestedRescueTest < Minitest::Spec
4
+ #---
5
+ # nested raise (i hope people won't use this but it works)
6
+ A = Class.new(RuntimeError)
7
+ Y = Class.new(RuntimeError)
8
+
9
+ class NestedInsanity < Trailblazer::Operation
10
+ step Rescue {
11
+ step ->(options, **) { options["a"] = true }
12
+ step Rescue {
13
+ step ->(options, **) { options["y"] = true }
14
+ success ->(options, **) { raise Y if options["raise-y"] }
15
+ step ->(options, **) { options["z"] = true }
16
+ }
17
+ step ->(options, **) { options["b"] = true }
18
+ success ->(options, **) { raise A if options["raise-a"] }
19
+ step ->(options, **) { options["c"] = true }
20
+ failure ->(options, **) { options["inner-err"] = true }
21
+ }
22
+ step ->(options, **) { options["e"] = true }, id: "nested/e"
23
+ failure ->(options, **) { options["outer-err"] = true }, id: "nested/failure"
24
+ end
25
+
26
+ it { Trailblazer::Operation::Inspect.(NestedInsanity).must_match /\[>Rescue\(\d+\),>nested/ } # FIXME: better introspect tests for all id-generating macros.
27
+ it { NestedInsanity.().inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:true [true, true, true, true, true, true, nil, nil] >} }
28
+ it { NestedInsanity.( "raise-y" => true).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:false [true, true, nil, nil, nil, nil, true, true] >} }
29
+ it { NestedInsanity.( "raise-a" => true).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:false [true, true, true, true, nil, nil, nil, true] >} }
30
+
31
+ #-
32
+ # inheritance
33
+ class UbernestedInsanity < NestedInsanity
34
+ end
35
+
36
+ it { UbernestedInsanity.().inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:true [true, true, true, true, true, true, nil, nil] >} }
37
+ it { UbernestedInsanity.( "raise-a" => true).inspect("a", "y", "z", "b", "c", "e", "inner-err", "outer-err").must_equal %{<Result:false [true, true, true, true, nil, nil, nil, true] >} }
38
+ end
39
+
40
+ class RescueTest < Minitest::Spec
41
+
42
+ =begin
43
+ plain Rescue()
44
+ =end
45
+ class RescueWithoutHandlerTest < Minitest::Spec
46
+ Memo = Class.new
47
+
48
+ class Memo::Create < Trailblazer::Operation
49
+ step :find_model
50
+ step Rescue() {
51
+ step :update
52
+ step :rehash
53
+ }
54
+ step :notify
55
+ fail :log_error
56
+ #~methods
57
+ include Test::Methods
58
+ #~methods end
59
+ end
60
+
61
+ it { Memo::Create.( { seq: [] } ).inspect(:seq, :exception_class).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify], nil] >} }
62
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error]] >} }
63
+ end
64
+
65
+ =begin
66
+ Rescue( handler: X )
67
+ =end
68
+ class RescueWithHandlerTest < Minitest::Spec
69
+ Memo = Class.new
70
+
71
+ #:rescue-handler
72
+ class MyHandler
73
+ def self.call(exception, (ctx), *)
74
+ ctx[:exception_class] = exception.class
75
+ end
76
+ end
77
+ #:rescue-handler end
78
+
79
+ #:rescue
80
+ class Memo::Create < Trailblazer::Operation
81
+ step :find_model
82
+ step Rescue( RuntimeError, handler: MyHandler ) {
83
+ step :update
84
+ step :rehash
85
+ }
86
+ step :notify
87
+ fail :log_error
88
+ #~methods
89
+ include Test::Methods
90
+ #~methods end
91
+ end
92
+ #:rescue end
93
+
94
+ it { Memo::Create.( { seq: [], } ).inspect(:seq, :exception_class).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify], nil] >} }
95
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq, :exception_class).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error], RuntimeError] >} }
96
+ end
97
+
98
+ =begin
99
+ Rescue( handler: :instance_method )
100
+ =end
101
+ class RescueWithHandlerTest < Minitest::Spec
102
+ Memo = Class.new
103
+
104
+ #:rescue-method
105
+ class Memo::Create < Trailblazer::Operation
106
+ step :find_model
107
+ step Rescue( RuntimeError, handler: :my_handler ) {
108
+ step :update
109
+ step :rehash
110
+ }
111
+ step :notify
112
+ fail :log_error
113
+ #~methods
114
+ include Test::Methods
115
+ #~methods end
116
+
117
+ def my_handler(exception, (ctx), *)
118
+ ctx[:exception_class] = exception.class
119
+ end
120
+ end
121
+ #:rescue-method end
122
+
123
+ it { Memo::Create.( { seq: [], } ).inspect(:seq, :exception_class).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify], nil] >} }
124
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq, :exception_class).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error], RuntimeError] >} }
125
+ end
126
+ end
@@ -0,0 +1,274 @@
1
+ require "test_helper"
2
+
3
+ class DocsWrapTest < Minitest::Spec
4
+ module Memo
5
+ end
6
+
7
+ =begin
8
+ When success: return the block's returns
9
+ When raise: return {Railway.fail!}
10
+ =end
11
+ #:wrap-handler
12
+ class HandleUnsafeProcess
13
+ def self.call((ctx, flow_options), *, &block)
14
+ begin
15
+ yield # calls the wrapped steps
16
+ rescue
17
+ [ Trailblazer::Operation::Railway.fail!, [ctx, flow_options] ]
18
+ end
19
+ end
20
+ end
21
+ #:wrap-handler end
22
+
23
+ #:wrap
24
+ class Memo::Create < Trailblazer::Operation
25
+ step :find_model
26
+ step Wrap( HandleUnsafeProcess ) {
27
+ step :update
28
+ step :rehash
29
+ }
30
+ step :notify
31
+ fail :log_error
32
+ #~methods
33
+ include Test::Methods
34
+ #~methods end
35
+ end
36
+ #:wrap end
37
+
38
+ it { Memo::Create.( { seq: [] } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
39
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error]] >} }
40
+
41
+ =begin
42
+ Tracing with Wrap()
43
+ =end
44
+ it do
45
+ options = { seq: [] }
46
+ #:trace-call
47
+ result = Memo::Create.trace( options )
48
+ #:trace-call end
49
+ result.wtf.gsub("\n", "").must_match /.*Start.*find_model.*Wrap.*update.*rehash.*success.*notify.*success/
50
+ =begin
51
+ #:trace-success
52
+ result.wtf? #=>
53
+ |-- #<Trailblazer::Activity::Start semantic=:default>
54
+ |-- find_model
55
+ |-- Wrap/85
56
+ | |-- #<Trailblazer::Activity::Start semantic=:default>
57
+ | |-- update
58
+ | |-- rehash
59
+ | `-- #<Trailblazer::Operation::Railway::End::Success semantic=:success>
60
+ |-- notify
61
+ `-- #<Trailblazer::Operation::Railway::End::Success semantic=:success>
62
+ #:trace-success end
63
+ =end
64
+ end
65
+
66
+ =begin
67
+ When success: return the block's returns
68
+ When raise: return {Railway.fail!}, but wire Wrap() to {fail_fast: true}
69
+ =end
70
+ class WrapGoesIntoFailFastTest < Minitest::Spec
71
+ Memo = Module.new
72
+
73
+ class Memo::Create < Trailblazer::Operation
74
+ class HandleUnsafeProcess
75
+ def self.call((ctx), *, &block)
76
+ begin
77
+ yield # calls the wrapped steps
78
+ rescue
79
+ [ Trailblazer::Operation::Railway.fail!, [ctx, {}] ]
80
+ end
81
+ end
82
+ end
83
+
84
+ step :find_model
85
+ step Wrap( HandleUnsafeProcess ) {
86
+ step :update
87
+ step :rehash
88
+ }, fail_fast: true
89
+ step :notify
90
+ fail :log_error
91
+
92
+ #~methods
93
+ include Test::Methods
94
+ #~methods end
95
+ end
96
+
97
+ it { Memo::Create.( { seq: [] } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
98
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash]] >} }
99
+ end
100
+
101
+ =begin
102
+ When success: return the block's returns
103
+ When raise: return {Railway.fail_fast!} and configure Wrap() to {fast_track: true}
104
+ =end
105
+ class WrapGoesIntoFailFastViaFastTrackTest < Minitest::Spec
106
+ Memo = Module.new
107
+
108
+ #:fail-fast-handler
109
+ class HandleUnsafeProcess
110
+ def self.call((ctx), *, &block)
111
+ begin
112
+ yield # calls the wrapped steps
113
+ rescue
114
+ [ Trailblazer::Operation::Railway.fail_fast!, [ctx, {}] ]
115
+ end
116
+ end
117
+ end
118
+ #:fail-fast-handler end
119
+
120
+ #:fail-fast
121
+ class Memo::Create < Trailblazer::Operation
122
+ step :find_model
123
+ step Wrap( HandleUnsafeProcess ) {
124
+ step :update
125
+ step :rehash
126
+ }, fast_track: true
127
+ step :notify
128
+ fail :log_error
129
+ #~methods
130
+ include Test::Methods
131
+ #~methods end
132
+ end
133
+ #:fail-fast end
134
+
135
+ it { Memo::Create.( { seq: [] } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
136
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash]] >} }
137
+ end
138
+
139
+ =begin
140
+ When success: return the block's returns
141
+ When raise: return {Railway.fail!} or {Railway.pass!}
142
+ =end
143
+ class WrapWithTransactionTest < Minitest::Spec
144
+ Memo = Module.new
145
+
146
+ module Sequel
147
+ def self.transaction
148
+ begin
149
+ end_event, (ctx, flow_options) = yield
150
+ true
151
+ rescue
152
+ false
153
+ end
154
+ end
155
+ end
156
+
157
+ #:transaction-handler
158
+ class MyTransaction
159
+ def self.call((ctx, flow_options), *, &block)
160
+ result = Sequel.transaction { yield }
161
+
162
+ signal = result ? Trailblazer::Operation::Railway.pass! : Trailblazer::Operation::Railway.fail!
163
+
164
+ [ signal, [ctx, flow_options] ]
165
+ end
166
+ end
167
+ #:transaction-handler end
168
+
169
+ #:transaction
170
+ class Memo::Create < Trailblazer::Operation
171
+ step :find_model
172
+ step Wrap( MyTransaction ) {
173
+ step :update
174
+ step :rehash
175
+ }
176
+ step :notify
177
+ fail :log_error
178
+ #~methods
179
+ include Test::Methods
180
+ #~methods end
181
+ end
182
+ #:transaction end
183
+
184
+ it { Memo::Create.( { seq: [] } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
185
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash, :log_error]] >} }
186
+ end
187
+
188
+ =begin
189
+ When success: return the block's returns
190
+ When raise: return {Railway.fail!} or {Railway.pass!}
191
+ =end
192
+ class WrapWithCustomEndsTest < Minitest::Spec
193
+ Memo = Module.new
194
+ Sequel = WrapWithTransactionTest::Sequel
195
+
196
+ #:custom-handler
197
+ class MyTransaction
198
+ MyFailSignal = Class.new(Trailblazer::Activity::Signal)
199
+
200
+ def self.call((ctx, flow_options), *, &block)
201
+ result = Sequel.transaction { yield }
202
+
203
+ signal = result ? Trailblazer::Operation::Railway.pass! : MyFailSignal
204
+
205
+ [ signal, [ctx, flow_options] ]
206
+ end
207
+ end
208
+ #:custom-handler end
209
+
210
+ #:custom
211
+ class Memo::Create < Trailblazer::Operation
212
+ step :find_model
213
+ step Wrap( MyTransaction ) {
214
+ step :update
215
+ step :rehash
216
+ },
217
+ Output(:success) => End(:transaction_worked),
218
+ Output(MyTransaction::MyFailSignal, :failure) => End(:transaction_failed)
219
+ step :notify
220
+ fail :log_error
221
+ #~methods
222
+ include Test::Methods
223
+ #~methods end
224
+ end
225
+ #:custom end
226
+
227
+ it do
228
+ result = Memo::Create.( { seq: [] } )
229
+ result.inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash]] >}
230
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::End semantic=:transaction_worked>}
231
+ end
232
+
233
+ it do
234
+ result = Memo::Create.( { seq: [], rehash_raise: true } )
235
+ result.inspect(:seq).must_equal %{<Result:false [[:find_model, :update, :rehash]] >}
236
+ result.event.inspect.must_equal %{#<Trailblazer::Activity::End semantic=:transaction_failed>}
237
+ end
238
+ end
239
+
240
+ =begin
241
+ When success: return the block's returns
242
+ When raise: return {Railway.pass!} and go "successful"
243
+ =end
244
+ class WrapGoesIntoPassFromRescueTest < Minitest::Spec
245
+ Memo = Module.new
246
+
247
+ class Memo::Create < Trailblazer::Operation
248
+ class HandleUnsafeProcess
249
+ def self.call((ctx), *, &block)
250
+ begin
251
+ yield # calls the wrapped steps
252
+ rescue
253
+ [ Trailblazer::Operation::Railway.pass!, [ctx, {}] ]
254
+ end
255
+ end
256
+ end
257
+
258
+ step :find_model
259
+ step Wrap( HandleUnsafeProcess ) {
260
+ step :update
261
+ step :rehash
262
+ }
263
+ step :notify
264
+ fail :log_error
265
+
266
+ #~methods
267
+ include Test::Methods
268
+ #~methods end
269
+ end
270
+
271
+ it { Memo::Create.( { seq: [] } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
272
+ it { Memo::Create.( { seq: [], rehash_raise: true } ).inspect(:seq).must_equal %{<Result:true [[:find_model, :update, :rehash, :notify]] >} }
273
+ end
274
+ end