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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -2
  3. data/CHANGES.md +15 -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 -19
  27. data/trailblazer-operation.gemspec +3 -2
  28. metadata +50 -25
  29. data/lib/trailblazer/operation/trace.rb +0 -53
  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