trailblazer-operation 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -2
  3. data/CHANGES.md +10 -0
  4. data/lib/trailblazer/operation/class_dependencies.rb +1 -1
  5. data/lib/trailblazer/operation/public_call.rb +24 -14
  6. data/lib/trailblazer/operation/railway.rb +13 -7
  7. data/lib/trailblazer/operation/version.rb +1 -1
  8. data/lib/trailblazer/operation/wtf.rb +11 -0
  9. data/lib/trailblazer/operation.rb +2 -2
  10. data/test/call_test.rb +32 -10
  11. data/test/class_dependencies_test.rb +8 -4
  12. data/test/docs/autogenerated/activity_basics_test.rb +72 -0
  13. data/test/docs/autogenerated/composable_variable_mapping_test.rb +880 -0
  14. data/test/docs/autogenerated/fast_track_layout_test.rb +76 -0
  15. data/test/docs/autogenerated/mechanics_test.rb +382 -0
  16. data/test/docs/autogenerated/sequence_options_test.rb +202 -0
  17. data/test/docs/autogenerated/subprocess_test.rb +257 -0
  18. data/test/docs/autogenerated/wiring_api_test.rb +435 -0
  19. data/test/docs/developer_test.rb +27 -0
  20. data/test/docs/public_call_monkeypatching_test.rb +96 -0
  21. data/test/docs/result_test.rb +38 -0
  22. data/test/docs/step_dsl_test.rb +93 -0
  23. data/test/operation_test.rb +53 -13
  24. data/test/result_test.rb +1 -1
  25. data/test/test_helper.rb +17 -5
  26. data/test/trace_test.rb +3 -43
  27. data/trailblazer-operation.gemspec +2 -1
  28. metadata +42 -23
  29. data/lib/trailblazer/operation/trace.rb +0 -67
  30. data/test/callable_test.rb +0 -147
  31. data/test/docs/doormat_test.rb +0 -190
  32. data/test/docs/macaroni_test.rb +0 -31
  33. data/test/skill_test.rb +0 -66
  34. data/test/wire_test.rb +0 -113
  35. data/test/wiring/defaults_test.rb +0 -193
  36. 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