trailblazer-operation 0.10.0 → 0.11.0
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/.github/workflows/ci.yml +2 -2
- data/CHANGES.md +15 -0
- data/lib/trailblazer/operation/class_dependencies.rb +1 -1
- data/lib/trailblazer/operation/public_call.rb +24 -14
- data/lib/trailblazer/operation/railway.rb +13 -7
- data/lib/trailblazer/operation/version.rb +1 -1
- data/lib/trailblazer/operation/wtf.rb +11 -0
- data/lib/trailblazer/operation.rb +2 -2
- data/test/call_test.rb +32 -10
- data/test/class_dependencies_test.rb +8 -4
- data/test/docs/autogenerated/activity_basics_test.rb +72 -0
- data/test/docs/autogenerated/composable_variable_mapping_test.rb +880 -0
- data/test/docs/autogenerated/fast_track_layout_test.rb +76 -0
- data/test/docs/autogenerated/mechanics_test.rb +382 -0
- data/test/docs/autogenerated/sequence_options_test.rb +202 -0
- data/test/docs/autogenerated/subprocess_test.rb +257 -0
- data/test/docs/autogenerated/wiring_api_test.rb +435 -0
- data/test/docs/developer_test.rb +27 -0
- data/test/docs/public_call_monkeypatching_test.rb +96 -0
- data/test/docs/result_test.rb +38 -0
- data/test/docs/step_dsl_test.rb +93 -0
- data/test/operation_test.rb +53 -13
- data/test/result_test.rb +1 -1
- data/test/test_helper.rb +17 -5
- data/test/trace_test.rb +3 -19
- data/trailblazer-operation.gemspec +3 -2
- metadata +50 -25
- data/lib/trailblazer/operation/trace.rb +0 -53
- data/test/callable_test.rb +0 -147
- data/test/docs/doormat_test.rb +0 -190
- data/test/docs/macaroni_test.rb +0 -31
- data/test/skill_test.rb +0 -66
- data/test/wire_test.rb +0 -113
- data/test/wiring/defaults_test.rb +0 -193
- data/test/wiring/subprocess_test.rb +0 -70
@@ -0,0 +1,880 @@
|
|
1
|
+
# THIS FILE IS AUTOGENERATED FROM trailblazer-activity-dsl-linear/test/docs/composable_variable_mapping_test.rb
|
2
|
+
require "test_helper"
|
3
|
+
|
4
|
+
class ComposableVariableMappingDocTest < Minitest::Spec
|
5
|
+
class ApplicationPolicy
|
6
|
+
def self.can?(model, user, mode)
|
7
|
+
decision = !user.nil?
|
8
|
+
Struct.new(:allowed?).new(decision)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Steps
|
13
|
+
def create_model(ctx, **)
|
14
|
+
ctx[:model] = Object
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module A
|
19
|
+
#:policy
|
20
|
+
module Policy
|
21
|
+
# Explicit policy, not ideal as it results in a lot of code.
|
22
|
+
class Create
|
23
|
+
def self.call(ctx, model:, user:, **)
|
24
|
+
decision = ApplicationPolicy.can?(model, user, :create) # FIXME: how does pundit/cancan do this exactly?
|
25
|
+
#~decision
|
26
|
+
|
27
|
+
if decision.allowed?
|
28
|
+
return true
|
29
|
+
else
|
30
|
+
ctx[:status] = 422 # we're not interested in this field.
|
31
|
+
ctx[:message] = "Command {create} not allowed!"
|
32
|
+
return false
|
33
|
+
end
|
34
|
+
#~decision end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
#:policy end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#@ 0.1 No In()
|
43
|
+
class CVNoInTest < Minitest::Spec
|
44
|
+
Memo = Module.new
|
45
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
46
|
+
|
47
|
+
#:no-in
|
48
|
+
module Memo::Operation
|
49
|
+
class Create < Trailblazer::Operation
|
50
|
+
step :create_model
|
51
|
+
step Policy::Create # an imaginary policy step.
|
52
|
+
#~meths
|
53
|
+
include ComposableVariableMappingDocTest::Steps
|
54
|
+
#~meths end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
#:no-in end
|
58
|
+
|
59
|
+
it "why do we need In() ? because we get an exception" do
|
60
|
+
exception = assert_raises ArgumentError do
|
61
|
+
#:no-in-invoke
|
62
|
+
result = Memo::Operation::Create.(current_user: Module)
|
63
|
+
|
64
|
+
#=> ArgumentError: missing keyword: :user
|
65
|
+
#:no-in-invoke end
|
66
|
+
end
|
67
|
+
|
68
|
+
assert_equal exception.message, "missing keyword: #{Trailblazer::Core.symbol_inspect_for(:user)}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#@ In() 1.1 {:model => :model}
|
73
|
+
class CVInMappingHashTest < Minitest::Spec
|
74
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
75
|
+
Memo = Module.new
|
76
|
+
|
77
|
+
module A
|
78
|
+
#:in-mapping
|
79
|
+
class Create < Trailblazer::Operation
|
80
|
+
step :create_model
|
81
|
+
step Policy::Create,
|
82
|
+
In() => {
|
83
|
+
:current_user => :user, # rename {:current_user} to {:user}
|
84
|
+
:model => :model # add {:model} to the inner ctx.
|
85
|
+
}
|
86
|
+
#~meths
|
87
|
+
include ComposableVariableMappingDocTest::Steps
|
88
|
+
#~meths end
|
89
|
+
end
|
90
|
+
#:in-mapping end
|
91
|
+
|
92
|
+
end # A
|
93
|
+
|
94
|
+
it "why do we need In() ?" do
|
95
|
+
assert_invoke A::Create, current_user: Module, expected_ctx_variables: {model: Object}
|
96
|
+
end
|
97
|
+
|
98
|
+
#:in-mapping-keys
|
99
|
+
module Memo::Operation
|
100
|
+
class Create < Trailblazer::Operation
|
101
|
+
step :create_model
|
102
|
+
step :show_ctx,
|
103
|
+
In() => {
|
104
|
+
:current_user => :user, # rename {:current_user} to {:user}
|
105
|
+
:model => :model # add {:model} to the inner ctx.
|
106
|
+
}
|
107
|
+
|
108
|
+
def show_ctx(ctx, **)
|
109
|
+
p ctx.to_h
|
110
|
+
#=> {:user=>#<User email:...>, :model=>#<Memo name=nil>}
|
111
|
+
end
|
112
|
+
#~meths
|
113
|
+
include ComposableVariableMappingDocTest::Steps
|
114
|
+
#~meths end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
#:in-mapping-keys end
|
118
|
+
|
119
|
+
it "In() is only locally visible" do
|
120
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# In() 1.2
|
125
|
+
class CVInLimitTest < Minitest::Spec
|
126
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
127
|
+
Memo = Module.new
|
128
|
+
|
129
|
+
#:in-limit
|
130
|
+
module Memo::Operation
|
131
|
+
class Create < Trailblazer::Operation
|
132
|
+
step :create_model
|
133
|
+
step Policy::Create,
|
134
|
+
In() => {:current_user => :user},
|
135
|
+
In() => [:model]
|
136
|
+
#~meths
|
137
|
+
include ComposableVariableMappingDocTest::Steps
|
138
|
+
#~meths end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
#:in-limit end
|
142
|
+
|
143
|
+
it "In() can map and limit" do
|
144
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object}
|
145
|
+
end
|
146
|
+
|
147
|
+
it "Policy breach will add {ctx[:message]} and {:status}" do
|
148
|
+
assert_invoke Memo::Operation::Create, current_user: nil, terminus: :failure, expected_ctx_variables: {model: Object, status: 422, message: "Command {create} not allowed!"}
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# In() 1.3 (callable)
|
153
|
+
class CVInCallableTest < Minitest::Spec
|
154
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
155
|
+
Memo = Module.new
|
156
|
+
|
157
|
+
#:in-callable
|
158
|
+
module Memo::Operation
|
159
|
+
class Create < Trailblazer::Operation
|
160
|
+
step :create_model
|
161
|
+
step Policy::Create,
|
162
|
+
In() => ->(ctx, **) do
|
163
|
+
# only rename {:current_user} if it's there.
|
164
|
+
ctx[:current_user].nil? ? {} : {user: ctx[:current_user]}
|
165
|
+
end,
|
166
|
+
In() => [:model]
|
167
|
+
#~meths
|
168
|
+
include ComposableVariableMappingDocTest::Steps
|
169
|
+
#~meths end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
#:in-callable end
|
173
|
+
|
174
|
+
it "In() can map and limit" do
|
175
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object}
|
176
|
+
end
|
177
|
+
|
178
|
+
it "exception because we don't pass {:current_user}" do
|
179
|
+
exception = assert_raises ArgumentError do
|
180
|
+
result = Memo::Operation::Create.({}) # no {:current_user}
|
181
|
+
end
|
182
|
+
|
183
|
+
assert_equal exception.message, "missing keyword: #{Trailblazer::Core.symbol_inspect_for(:user)}"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# In() 1.4 (filter method)
|
188
|
+
class CVInMethodTest < Minitest::Spec
|
189
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
190
|
+
Memo = Module.new
|
191
|
+
|
192
|
+
#:in-method
|
193
|
+
module Memo::Operation
|
194
|
+
class Create < Trailblazer::Operation
|
195
|
+
step :create_model
|
196
|
+
step Policy::Create,
|
197
|
+
In() => :input_for_policy, # You can use an {:instance_method}!
|
198
|
+
In() => [:model]
|
199
|
+
|
200
|
+
def input_for_policy(ctx, **)
|
201
|
+
# only rename {:current_user} if it's there.
|
202
|
+
ctx[:current_user].nil? ? {} : {user: ctx[:current_user]}
|
203
|
+
end
|
204
|
+
#~meths
|
205
|
+
include ComposableVariableMappingDocTest::Steps
|
206
|
+
#~meths end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
#:in-method end
|
210
|
+
|
211
|
+
it { assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object} }
|
212
|
+
end
|
213
|
+
|
214
|
+
# In() 1.5 (callable with kwargs)
|
215
|
+
class CVInKwargsTest < Minitest::Spec
|
216
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
217
|
+
Memo = Module.new
|
218
|
+
|
219
|
+
#:in-kwargs
|
220
|
+
module Memo::Operation
|
221
|
+
class Create < Trailblazer::Operation
|
222
|
+
step :create_model
|
223
|
+
step Policy::Create,
|
224
|
+
# vvvvvvvvvvvv keyword arguments rock!
|
225
|
+
In() => ->(ctx, current_user: nil, **) do
|
226
|
+
current_user.nil? ? {} : {user: current_user}
|
227
|
+
end,
|
228
|
+
In() => [:model]
|
229
|
+
#~meths
|
230
|
+
include ComposableVariableMappingDocTest::Steps
|
231
|
+
#~meths end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
#:in-kwargs end
|
235
|
+
|
236
|
+
it { assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object} }
|
237
|
+
end
|
238
|
+
|
239
|
+
# Out() 1.1
|
240
|
+
class CVOutTest < Minitest::Spec
|
241
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
242
|
+
Memo = Module.new
|
243
|
+
|
244
|
+
#:out-array
|
245
|
+
module Memo::Operation
|
246
|
+
class Create < Trailblazer::Operation
|
247
|
+
step :create_model
|
248
|
+
step Policy::Create,
|
249
|
+
In() => {:current_user => :user},
|
250
|
+
In() => [:model],
|
251
|
+
Out() => [:message]
|
252
|
+
#~meths
|
253
|
+
include ComposableVariableMappingDocTest::Steps
|
254
|
+
#~meths end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
#:out-array end
|
258
|
+
|
259
|
+
it "Out() can limit" do
|
260
|
+
#= policy didn't set any message
|
261
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object, message: nil}
|
262
|
+
#= policy breach, {message_from_policy} set.
|
263
|
+
assert_invoke Memo::Operation::Create, current_user: nil, terminus: :failure, expected_ctx_variables: {model: Object, message: "Command {create} not allowed!"}
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
# Out() 1.2
|
269
|
+
class CVOutHashTest < Minitest::Spec
|
270
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
271
|
+
Memo = Module.new
|
272
|
+
|
273
|
+
#:out-hash
|
274
|
+
module Memo::Operation
|
275
|
+
class Create < Trailblazer::Operation
|
276
|
+
step :create_model
|
277
|
+
step Policy::Create,
|
278
|
+
In() => {:current_user => :user},
|
279
|
+
In() => [:model],
|
280
|
+
Out() => {:message => :message_from_policy}
|
281
|
+
#~meths
|
282
|
+
include ComposableVariableMappingDocTest::Steps
|
283
|
+
#~meths end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
#:out-hash end
|
287
|
+
|
288
|
+
it "Out() can map" do
|
289
|
+
#= policy didn't set any message
|
290
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object, message_from_policy: nil}
|
291
|
+
#= policy breach, {message_from_policy} set.
|
292
|
+
assert_invoke Memo::Operation::Create, current_user: nil, terminus: :failure, expected_ctx_variables: {model: Object, message_from_policy: "Command {create} not allowed!"}
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# Out() 1.3
|
297
|
+
class CVOutCallableTest < Minitest::Spec
|
298
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
299
|
+
Memo = Module.new
|
300
|
+
|
301
|
+
# Message = Struct.new(:data)
|
302
|
+
#:out-callable
|
303
|
+
module Memo::Operation
|
304
|
+
class Create < Trailblazer::Operation
|
305
|
+
step :create_model
|
306
|
+
step Policy::Create,
|
307
|
+
In() => {:current_user => :user},
|
308
|
+
In() => [:model],
|
309
|
+
Out() => ->(ctx, **) do
|
310
|
+
return {} unless ctx[:message]
|
311
|
+
|
312
|
+
{ # you always have to return a hash from a callable!
|
313
|
+
:message_from_policy => ctx[:message]
|
314
|
+
}
|
315
|
+
end
|
316
|
+
#~meths
|
317
|
+
include ComposableVariableMappingDocTest::Steps
|
318
|
+
#~meths end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
#:out-callable end
|
322
|
+
|
323
|
+
it "Out() can map with callable" do
|
324
|
+
#= policy didn't set any message
|
325
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object}
|
326
|
+
#= policy breach, {message_from_policy} set.
|
327
|
+
assert_invoke Memo::Operation::Create, current_user: nil, terminus: :failure, expected_ctx_variables: {model: Object, message_from_policy: "Command {create} not allowed!"}
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
# Out() 1.4
|
332
|
+
class CVOutKwTest < Minitest::Spec
|
333
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
334
|
+
Memo = Module.new
|
335
|
+
|
336
|
+
#:out-kw
|
337
|
+
module Memo::Operation
|
338
|
+
class Create < Trailblazer::Operation
|
339
|
+
step :create_model
|
340
|
+
step Policy::Create,
|
341
|
+
In() => {:current_user => :user},
|
342
|
+
In() => [:model],
|
343
|
+
Out() => ->(ctx, message: nil, **) do
|
344
|
+
return {} if message.nil?
|
345
|
+
|
346
|
+
{ # you always have to return a hash from a callable!
|
347
|
+
:message_from_policy => message
|
348
|
+
}
|
349
|
+
end
|
350
|
+
#~meths
|
351
|
+
include ComposableVariableMappingDocTest::Steps
|
352
|
+
#~meths end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
#:out-kw end
|
356
|
+
|
357
|
+
it "Out() can map with callable" do
|
358
|
+
#= policy didn't set any message
|
359
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object}
|
360
|
+
#= policy breach, {message_from_policy} set.
|
361
|
+
assert_invoke Memo::Operation::Create, current_user: nil, terminus: :failure, expected_ctx_variables: {model: Object, message_from_policy: "Command {create} not allowed!"}
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
# Out() 1.6
|
366
|
+
class CVOutOuterTest < Minitest::Spec
|
367
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
368
|
+
Memo = Module.new
|
369
|
+
|
370
|
+
#:out-outer
|
371
|
+
module Memo::Operation
|
372
|
+
class Create < Trailblazer::Operation
|
373
|
+
step :create_model
|
374
|
+
step Policy::Create,
|
375
|
+
In() => {:current_user => :user},
|
376
|
+
In() => [:model],
|
377
|
+
Out() => [:message],
|
378
|
+
|
379
|
+
Out(with_outer_ctx: true) => ->(inner_ctx, outer_ctx:, **) do
|
380
|
+
{
|
381
|
+
errors: outer_ctx[:errors].merge(policy_message: inner_ctx[:message])
|
382
|
+
}
|
383
|
+
end
|
384
|
+
#~meths
|
385
|
+
include ComposableVariableMappingDocTest::Steps
|
386
|
+
#~meths end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
#:out-outer end
|
390
|
+
|
391
|
+
it "Out() with {outer_ctx}" do
|
392
|
+
#= policy didn't set any message
|
393
|
+
assert_invoke Memo::Operation::Create, current_user: Module, errors: {}, expected_ctx_variables: {:errors=>{:policy_message=>nil}, model: Object, message: nil}
|
394
|
+
#= policy breach, {message_from_policy} set.
|
395
|
+
assert_invoke Memo::Operation::Create, current_user: nil, errors: {}, terminus: :failure, expected_ctx_variables: {:errors=>{:policy_message=>"Command {create} not allowed!"}, :model=>Object, :message=>"Command {create} not allowed!"}
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
# Macro 1.0
|
400
|
+
class CVMacroTest < Minitest::Spec
|
401
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
402
|
+
Memo = Module.new
|
403
|
+
|
404
|
+
#:macro
|
405
|
+
module Policy
|
406
|
+
def self.Create()
|
407
|
+
{
|
408
|
+
task: Policy::Create,
|
409
|
+
wrap_task: true,
|
410
|
+
Trailblazer::Activity::Railway.In() => {:current_user => :user},
|
411
|
+
Trailblazer::Activity::Railway.In() => [:model],
|
412
|
+
Trailblazer::Activity::Railway.Out() => {:message => :message_from_policy},
|
413
|
+
}
|
414
|
+
end
|
415
|
+
end
|
416
|
+
#:macro end
|
417
|
+
|
418
|
+
#:macro-use
|
419
|
+
module Memo::Operation
|
420
|
+
class Create < Trailblazer::Operation
|
421
|
+
step :create_model
|
422
|
+
step Policy::Create()
|
423
|
+
#~meths
|
424
|
+
include ComposableVariableMappingDocTest::Steps
|
425
|
+
#~meths end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
#:macro-use end
|
429
|
+
|
430
|
+
it "Out() with {outer_ctx}" do
|
431
|
+
#= policy didn't set any message
|
432
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object, message_from_policy: nil}
|
433
|
+
#= policy breach, {message_from_policy} set.
|
434
|
+
assert_invoke Memo::Operation::Create, current_user: nil, terminus: :failure, expected_ctx_variables: {model: Object, :message_from_policy=>"Command {create} not allowed!"}
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
# Macro 1.1
|
439
|
+
class CVMacroMergeTest < Minitest::Spec
|
440
|
+
Policy = CVMacroTest::Policy
|
441
|
+
Memo = Module.new
|
442
|
+
|
443
|
+
#:macro-merge
|
444
|
+
module Memo::Operation
|
445
|
+
class Create < Trailblazer::Operation
|
446
|
+
step :create_model
|
447
|
+
step Policy::Create(),
|
448
|
+
Out() => {:message => :copied_message} # user options!
|
449
|
+
#~meths
|
450
|
+
include ComposableVariableMappingDocTest::Steps
|
451
|
+
#~meths end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
#:macro-merge end
|
455
|
+
|
456
|
+
it do
|
457
|
+
#= policy didn't set any message
|
458
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object, message_from_policy: nil, :copied_message=>nil}
|
459
|
+
#= policy breach, {message_from_policy} set.
|
460
|
+
assert_invoke Memo::Operation::Create, current_user: nil, terminus: :failure, expected_ctx_variables: {model: Object, :message_from_policy=>"Command {create} not allowed!", :copied_message=>"Command {create} not allowed!"}
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
# Inheritance 1.0
|
465
|
+
class CVInheritanceTest < Minitest::Spec
|
466
|
+
Policy = CVMacroTest::Policy
|
467
|
+
Memo = Module.new
|
468
|
+
|
469
|
+
#:inheritance-base
|
470
|
+
module Memo::Operation
|
471
|
+
class Create < Trailblazer::Operation
|
472
|
+
|
473
|
+
step :create_model
|
474
|
+
step Policy::Create,
|
475
|
+
In() => {:current_user => :user},
|
476
|
+
In() => [:model],
|
477
|
+
Out() => [:message],
|
478
|
+
id: :policy
|
479
|
+
#~meths
|
480
|
+
include ComposableVariableMappingDocTest::Steps
|
481
|
+
#~meths end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
#:inheritance-base end
|
485
|
+
|
486
|
+
# puts Trailblazer::Developer::Render::TaskWrap.(Create, id: :policy)
|
487
|
+
=begin
|
488
|
+
#:tw-render
|
489
|
+
puts Trailblazer::Developer::Render::TaskWrap.(Memo::Operation::Create, id: :policy)
|
490
|
+
#:tw-render end
|
491
|
+
=end
|
492
|
+
|
493
|
+
=begin
|
494
|
+
#:tw-render-out
|
495
|
+
Memo::Operation::Create
|
496
|
+
`-- policy
|
497
|
+
|-- task_wrap.input..................Trailblazer::Operation::DSL::Linear::VariableMapping::Pipe::Input
|
498
|
+
| |-- input.init_hash.............................. ............................................. VariableMapping.initial_aggregate
|
499
|
+
| |-- input.add_variables.0.994[...]............... {:current_user=>:user}....................... VariableMapping::AddVariables
|
500
|
+
| |-- input.add_variables.0.592[...]............... [:model]..................................... VariableMapping::AddVariables
|
501
|
+
| `-- input.scope.................................. ............................................. VariableMapping.scope
|
502
|
+
|-- task_wrap.call_task..............Method
|
503
|
+
`-- task_wrap.output.................Trailblazer::Operation::DSL::Linear::VariableMapping::Pipe::Output
|
504
|
+
|-- output.init_hash............................. ............................................. VariableMapping.initial_aggregate
|
505
|
+
|-- output.add_variables.0.599[...].............. [:message]................................... VariableMapping::AddVariables::Output
|
506
|
+
`-- output.merge_with_original................... ............................................. VariableMapping.merge_with_original
|
507
|
+
#:tw-render-out end
|
508
|
+
=end
|
509
|
+
|
510
|
+
#:inheritance-sub
|
511
|
+
module Memo::Operation
|
512
|
+
class Admin < Create
|
513
|
+
step Policy::Create,
|
514
|
+
Out() => {:message => :raw_message_for_admin},
|
515
|
+
inherit: [:variable_mapping],
|
516
|
+
id: :policy, # you need to reference the :id when your step
|
517
|
+
replace: :policy
|
518
|
+
end
|
519
|
+
end
|
520
|
+
#:inheritance-sub end
|
521
|
+
|
522
|
+
# puts Trailblazer::Developer::Render::TaskWrap.(Admin, id: :policy)
|
523
|
+
=begin
|
524
|
+
#:sub-pipe
|
525
|
+
puts Trailblazer::Developer::Render::TaskWrap.(Memo::Operation::Admin, id: :policy)
|
526
|
+
|
527
|
+
Memo::Operation::Admin
|
528
|
+
# `-- policy
|
529
|
+
# |-- task_wrap.input..................Trailblazer::Operation::DSL::Linear::VariableMapping::Pipe::Input
|
530
|
+
# | |-- input.init_hash.............................. ............................................. VariableMapping.initial_aggregate
|
531
|
+
# | |-- input.add_variables.0.994[...]............... {:current_user=>:user}....................... VariableMapping::AddVariables
|
532
|
+
# | |-- input.add_variables.0.592[...]............... [:model]..................................... VariableMapping::AddVariables
|
533
|
+
# | `-- input.scope.................................. ............................................. VariableMapping.scope
|
534
|
+
# |-- task_wrap.call_task..............Method
|
535
|
+
# `-- task_wrap.output.................Trailblazer::Operation::DSL::Linear::VariableMapping::Pipe::Output
|
536
|
+
# |-- output.init_hash............................. ............................................. VariableMapping.initial_aggregate
|
537
|
+
# |-- output.add_variables.0.599[...].............. [:message]................................... VariableMapping::AddVariables::Output
|
538
|
+
# |-- output.add_variables.0.710[...].............. {:message=>:raw_message_for_admin}........... VariableMapping::AddVariables::Output
|
539
|
+
# `-- output.merge_with_original................... ............................................. VariableMapping.merge_with_original
|
540
|
+
#:sub-pipe end
|
541
|
+
=end
|
542
|
+
|
543
|
+
it do
|
544
|
+
#= policy didn't set any message
|
545
|
+
assert_invoke Memo::Operation::Admin, current_user: Module, expected_ctx_variables: {model: Object, message: nil, :raw_message_for_admin=>nil}
|
546
|
+
assert_invoke Memo::Operation::Admin, current_user: nil, terminus: :failure, expected_ctx_variables: {model: Object, :message=>"Command {create} not allowed!", :raw_message_for_admin=>"Command {create} not allowed!"}
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
# Inject() 1.0
|
551
|
+
class CVInjectTest < Minitest::Spec
|
552
|
+
Memo = Module.new
|
553
|
+
|
554
|
+
class ApplicationPolicy
|
555
|
+
def self.can?(model, user, action)
|
556
|
+
decision = !user.nil? && action == :create
|
557
|
+
Struct.new(:allowed?).new(decision)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
#:policy-check
|
562
|
+
module Policy
|
563
|
+
class Check
|
564
|
+
# vvvvvvvvvvvvvvv-- defaulted keyword arguments
|
565
|
+
def self.call(ctx, model:, user:, action: :create, **)
|
566
|
+
decision = ApplicationPolicy.can?(model, user, action) # FIXME: how does pundit/cancan do this exactly?
|
567
|
+
#~decision
|
568
|
+
|
569
|
+
if decision.allowed?
|
570
|
+
return true
|
571
|
+
else
|
572
|
+
ctx[:message] = "Command {#{action}} not allowed!"
|
573
|
+
return false
|
574
|
+
end
|
575
|
+
#~decision end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
end
|
579
|
+
#:policy-check end
|
580
|
+
|
581
|
+
#:inject
|
582
|
+
module Memo::Operation
|
583
|
+
class Create < Trailblazer::Operation
|
584
|
+
step :create_model
|
585
|
+
step Policy::Check,
|
586
|
+
In() => {:current_user => :user},
|
587
|
+
In() => [:model],
|
588
|
+
Inject() => [:action]
|
589
|
+
#~meths
|
590
|
+
include ComposableVariableMappingDocTest::Steps
|
591
|
+
#~meths end
|
592
|
+
end
|
593
|
+
end
|
594
|
+
#:inject end
|
595
|
+
|
596
|
+
it "Inject()" do
|
597
|
+
#= {:action} defaulted to {:create}
|
598
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object}
|
599
|
+
|
600
|
+
#= {:action} set explicitely to {:create}
|
601
|
+
assert_invoke Memo::Operation::Create, current_user: Module, action: :create, expected_ctx_variables: {model: Object}
|
602
|
+
|
603
|
+
#= {:action} set explicitely to {:update}, policy breach
|
604
|
+
assert_invoke Memo::Operation::Create, current_user: Module, action: :update, expected_ctx_variables: {model: Object, message: "Command {update} not allowed!"}, terminus: :failure
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
class CVNoInjectTest < Minitest::Spec
|
609
|
+
Policy = CVInjectTest::Policy
|
610
|
+
Memo = Module.new
|
611
|
+
|
612
|
+
#:no-inject
|
613
|
+
module Memo::Operation
|
614
|
+
class Create < Trailblazer::Operation
|
615
|
+
step :create_model
|
616
|
+
step Policy::Check,
|
617
|
+
In() => {:current_user => :user},
|
618
|
+
In() => [:model, :action]
|
619
|
+
#~meths
|
620
|
+
include ComposableVariableMappingDocTest::Steps
|
621
|
+
#~meths end
|
622
|
+
end
|
623
|
+
end
|
624
|
+
#:no-inject end
|
625
|
+
|
626
|
+
it "not using Inject()" do
|
627
|
+
#= {:action} not defaulted as In() passes nil
|
628
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object, message: "Command {} not allowed!"}, terminus: :failure
|
629
|
+
|
630
|
+
#= {:action} set explicitely to {:create}
|
631
|
+
assert_invoke Memo::Operation::Create, current_user: Module, action: :create, expected_ctx_variables: {model: Object}
|
632
|
+
|
633
|
+
#= {:action} set explicitely to {:update}
|
634
|
+
assert_invoke Memo::Operation::Create, current_user: Module, action: :update, expected_ctx_variables: {model: Object, message: "Command {update} not allowed!"}, terminus: :failure
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
class CVInjectDefaultTest < Minitest::Spec
|
639
|
+
ApplicationPolicy = CVInjectTest::ApplicationPolicy
|
640
|
+
Memo = Module.new
|
641
|
+
|
642
|
+
#:policy-check-nodef
|
643
|
+
module Policy
|
644
|
+
class Check
|
645
|
+
# vvvvvvv-- no defaulting!
|
646
|
+
def self.call(ctx, model:, user:, action:, **)
|
647
|
+
decision = ApplicationPolicy.can?(model, user, action) # FIXME: how does pundit/cancan do this exactly?
|
648
|
+
#~decision
|
649
|
+
|
650
|
+
if decision.allowed?
|
651
|
+
return true
|
652
|
+
else
|
653
|
+
ctx[:message] = "Command {#{action}} not allowed!"
|
654
|
+
return false
|
655
|
+
end
|
656
|
+
#~decision end
|
657
|
+
end
|
658
|
+
end
|
659
|
+
end
|
660
|
+
#:policy-check-nodef end
|
661
|
+
|
662
|
+
#:inject-default
|
663
|
+
module Memo::Operation
|
664
|
+
class Create < Trailblazer::Operation
|
665
|
+
step :create_model
|
666
|
+
step Policy::Check,
|
667
|
+
In() => {:current_user => :user},
|
668
|
+
In() => [:model],
|
669
|
+
Inject(:action) => ->(ctx, **) { :create }
|
670
|
+
#~meths
|
671
|
+
include ComposableVariableMappingDocTest::Steps
|
672
|
+
#~meths end
|
673
|
+
end
|
674
|
+
end
|
675
|
+
#:inject-default end
|
676
|
+
|
677
|
+
it "Inject() with default" do
|
678
|
+
#= {:action} defaulted by Inject()
|
679
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object}
|
680
|
+
|
681
|
+
#= {:action} set explicitely to {:create}
|
682
|
+
assert_invoke Memo::Operation::Create, current_user: Module, action: :create, expected_ctx_variables: {model: Object}
|
683
|
+
|
684
|
+
#= {:action} set explicitely to {:update}
|
685
|
+
assert_invoke Memo::Operation::Create, current_user: Module, action: :update, expected_ctx_variables: {model: Object, message: "Command {update} not allowed!"}, terminus: :failure
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
class CVInjectOverrideTest < Minitest::Spec
|
690
|
+
Policy = CVInjectDefaultTest::Policy
|
691
|
+
Memo = Module.new
|
692
|
+
|
693
|
+
#:inject-override
|
694
|
+
module Memo::Operation
|
695
|
+
class Create < Trailblazer::Operation
|
696
|
+
step :create_model
|
697
|
+
step Policy::Check,
|
698
|
+
In() => {:current_user => :user},
|
699
|
+
In() => [:model],
|
700
|
+
#:inject_override_iso
|
701
|
+
Inject(:action, override: true) => ->(*) { :create } # always used.
|
702
|
+
#:inject_override_iso end
|
703
|
+
#~meths
|
704
|
+
include ComposableVariableMappingDocTest::Steps
|
705
|
+
#~meths end
|
706
|
+
end
|
707
|
+
end
|
708
|
+
#:inject-override end
|
709
|
+
|
710
|
+
it "Inject() with default" do
|
711
|
+
#= {:action} override
|
712
|
+
assert_invoke Memo::Operation::Create, current_user: Module, expected_ctx_variables: {model: Object}
|
713
|
+
|
714
|
+
#= {:action} still overridden
|
715
|
+
assert_invoke Memo::Operation::Create, current_user: Module, action: :update, expected_ctx_variables: {model: Object}
|
716
|
+
|
717
|
+
current_user = Module
|
718
|
+
|
719
|
+
#:inject-override-call
|
720
|
+
result = Memo::Operation::Create.(
|
721
|
+
current_user: current_user,
|
722
|
+
action: :update # this is always overridden.
|
723
|
+
)
|
724
|
+
#~ctx_to_result
|
725
|
+
puts result[:model] #=> #<Memo id: 1, ...>
|
726
|
+
#~ctx_to_result end
|
727
|
+
#:inject-override-call end
|
728
|
+
|
729
|
+
assert_equal result[:model], Object
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
733
|
+
# def operation_for(&block)
|
734
|
+
# namespace = Module.new
|
735
|
+
# # namespace::Policy = ComposableVariableMappingDocTest::A::Policy
|
736
|
+
# namespace.const_set :Policy, A::Policy
|
737
|
+
|
738
|
+
# namespace.module_eval do
|
739
|
+
# operation = yield
|
740
|
+
# operation.class_eval do
|
741
|
+
# include ComposableVariableMappingDocTest::Steps
|
742
|
+
# end
|
743
|
+
# end
|
744
|
+
# end # operation_for
|
745
|
+
|
746
|
+
class DefaultInjectOnlyTest < Minitest::Spec
|
747
|
+
it "Inject(), only, without In()" do
|
748
|
+
class Create < Trailblazer::Operation
|
749
|
+
step :write,
|
750
|
+
Inject() => { name: ->(ctx, field:, **) { field } }
|
751
|
+
|
752
|
+
def write(ctx, name: nil, **)
|
753
|
+
ctx[:write] = %{
|
754
|
+
name: #{name.inspect}
|
755
|
+
}
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
assert_invoke Create, field: Module, expected_ctx_variables: {write: %{
|
760
|
+
name: Module
|
761
|
+
}}
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
765
|
+
class PassthroughInjectOnlyTest < Minitest::Spec
|
766
|
+
it "Inject() => [...], only, without In()" do
|
767
|
+
class Create < Trailblazer::Operation
|
768
|
+
step :write,
|
769
|
+
Inject() => [:name]
|
770
|
+
|
771
|
+
def write(ctx, name: nil, **)
|
772
|
+
ctx[:write] = %{
|
773
|
+
name: #{name.inspect}
|
774
|
+
}
|
775
|
+
end
|
776
|
+
end
|
777
|
+
|
778
|
+
assert_invoke Create, name: Module, expected_ctx_variables: {write: %{
|
779
|
+
name: Module
|
780
|
+
}}
|
781
|
+
end
|
782
|
+
end
|
783
|
+
|
784
|
+
#@ Out() 1.5
|
785
|
+
#@ First, blacklist all, then add whitelisted.
|
786
|
+
class OutMultipleTimes < Minitest::Spec
|
787
|
+
Policy = ComposableVariableMappingDocTest::A::Policy
|
788
|
+
|
789
|
+
class Create < Trailblazer::Operation
|
790
|
+
step :model
|
791
|
+
step Policy::Create,
|
792
|
+
In() => {:current_user => :user},
|
793
|
+
In() => [:model],
|
794
|
+
Out() => [],
|
795
|
+
Out() => [:message]
|
796
|
+
|
797
|
+
#~meths
|
798
|
+
def model(ctx, **)
|
799
|
+
ctx[:model] = Object
|
800
|
+
end
|
801
|
+
#~meths end
|
802
|
+
end
|
803
|
+
|
804
|
+
it "Out() can be used sequentially" do
|
805
|
+
#= policy didn't set any message
|
806
|
+
assert_invoke Create, current_user: Module, expected_ctx_variables: {model: Object, message: nil}
|
807
|
+
#= policy breach, {message_from_policy} set.
|
808
|
+
assert_invoke Create, current_user: nil, terminus: :failure, expected_ctx_variables: {model: Object, message: "Command {create} not allowed!"}
|
809
|
+
end
|
810
|
+
end
|
811
|
+
|
812
|
+
|
813
|
+
class IoOutDeleteTest < Minitest::Spec
|
814
|
+
#@ Delete a key in the outgoing ctx.
|
815
|
+
it "Out() DSL: {delete: true} forces deletion in aggregate." do
|
816
|
+
class Create < Trailblazer::Operation
|
817
|
+
step :create_model,
|
818
|
+
Out() => [:model],
|
819
|
+
Out() => ->(ctx, **) {
|
820
|
+
{errors: {}, # this is deleted.
|
821
|
+
status: 200} # this sticks around.
|
822
|
+
},
|
823
|
+
Out(delete: true) => [:errors] # always deletes from aggregate.
|
824
|
+
|
825
|
+
def create_model(ctx, current_user:, **)
|
826
|
+
ctx[:private] = "hi!"
|
827
|
+
ctx[:model] = [current_user, ctx.keys]
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
assert_invoke Create, current_user: Object, expected_ctx_variables: {
|
832
|
+
model: [Object, [:seq, :current_user, :private]],
|
833
|
+
:status=>200,
|
834
|
+
}
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
# {:read_from_aggregate} for the moment is only supposed to be used with SetVariable filters.
|
839
|
+
class IoOutDeleteReadFromAggregateTest < Minitest::Spec
|
840
|
+
#@ Rename a key *in the aggregate* and delete the original in {aggregate}.
|
841
|
+
# NOTE: this is currently experimental.
|
842
|
+
it "Out() DSL: {delete: true} forces deletion in outgoing ctx. Renaming can be applied on {:input_hash}" do
|
843
|
+
class Create < Trailblazer::Operation
|
844
|
+
step :create_model,
|
845
|
+
Out() => [:model],
|
846
|
+
Out() => ->(ctx, **) { {errors: {}} },
|
847
|
+
Out(read_from_aggregate: true) => {:errors => :create_model_errors},
|
848
|
+
Out(delete: true) => [:errors] # always on aggregate.
|
849
|
+
|
850
|
+
def create_model(ctx, current_user:, **)
|
851
|
+
ctx[:private] = "hi!"
|
852
|
+
ctx[:model] = [current_user, ctx.keys] # we want only this on the outside, as {:song} and {:hit}!
|
853
|
+
end
|
854
|
+
end
|
855
|
+
|
856
|
+
#@ we basically rename {:errors} to {:create_model_errors} in the {:aggregate} itself.
|
857
|
+
assert_invoke Create, current_user: Object, expected_ctx_variables: {
|
858
|
+
model: [Object, [:seq, :current_user, :private]],
|
859
|
+
create_model_errors: {},
|
860
|
+
}
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
864
|
+
#@ In() can override Inject() if it was added last.
|
865
|
+
class InInjectSortingTest < Minitest::Spec
|
866
|
+
it do
|
867
|
+
activity = Class.new(Trailblazer::Activity::Railway) do
|
868
|
+
step :params,
|
869
|
+
Inject() => [:params],
|
870
|
+
In() => ->(ctx, **) { {params: {id: 1}} }
|
871
|
+
|
872
|
+
def params(ctx, params:, **)
|
873
|
+
ctx[:captured_params] = params.inspect
|
874
|
+
end
|
875
|
+
end
|
876
|
+
|
877
|
+
assert_invoke activity, expected_ctx_variables: {captured_params: "#{{:id=>1}}"}
|
878
|
+
assert_invoke activity, params: {id: nil}, expected_ctx_variables: {params: {id: nil}, captured_params: "#{{:id=>1}}"}
|
879
|
+
end
|
880
|
+
end
|