trailblazer-macro 2.1.10.beta1 → 2.1.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +6 -4
- data/.github/workflows/ci_jruby.yml +19 -0
- data/.github/workflows/ci_legacy.yml +19 -0
- data/.github/workflows/ci_truffleruby.yml +19 -0
- data/CHANGES.md +34 -1
- data/Gemfile +7 -5
- data/lib/trailblazer/macro/each.rb +170 -0
- data/lib/trailblazer/macro/model.rb +4 -5
- data/lib/trailblazer/macro/nested.rb +130 -68
- data/lib/trailblazer/macro/rescue.rb +2 -0
- data/lib/trailblazer/macro/strategy.rb +32 -0
- data/lib/trailblazer/macro/version.rb +1 -1
- data/lib/trailblazer/macro/wrap.rb +72 -65
- data/lib/trailblazer/macro.rb +53 -2
- data/test/docs/autogenerated/operation_each_test.rb +585 -0
- data/test/docs/autogenerated/operation_model_test.rb +263 -0
- data/test/docs/each_test.rb +907 -0
- data/test/docs/macro_test.rb +18 -0
- data/test/docs/model_test.rb +204 -88
- data/test/docs/nested_static_test.rb +730 -0
- data/test/docs/wrap_test.rb +348 -66
- data/test/test_helper.rb +29 -0
- data/trailblazer-macro.gemspec +2 -6
- metadata +23 -60
- data/.rubocop.yml +0 -6
- data/.rubocop_todo.yml +0 -423
- data/test/docs/nested_test.rb +0 -218
- data/test/operation/model_test.rb +0 -89
- data/test/operation/nested_test.rb +0 -98
@@ -0,0 +1,730 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
# Use {ComputeNested.method(:compute_nested)}
|
4
|
+
# Use #trace
|
5
|
+
# Use #assert_invoke
|
6
|
+
|
7
|
+
class DocsNestedStaticTest < Minitest::Spec
|
8
|
+
#@ {:auto_wire} without any other options
|
9
|
+
module A
|
10
|
+
class Song
|
11
|
+
end
|
12
|
+
|
13
|
+
#:id3
|
14
|
+
module Song::Activity
|
15
|
+
class Id3Tag < Trailblazer::Activity::Railway
|
16
|
+
step :parse
|
17
|
+
step :encode_id3
|
18
|
+
#~meths
|
19
|
+
include T.def_steps(:parse, :encode_id3)
|
20
|
+
|
21
|
+
# def parse(ctx, seq:, **)
|
22
|
+
# ctx[:seq] = seq + [:parse]
|
23
|
+
# end
|
24
|
+
#~meths end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
#:id3 end
|
28
|
+
|
29
|
+
module Song::Activity
|
30
|
+
class VorbisComment < Trailblazer::Activity::Railway
|
31
|
+
step :prepare_metadata
|
32
|
+
step :encode_cover
|
33
|
+
#~meths
|
34
|
+
include T.def_steps(:prepare_metadata, :encode_cover)
|
35
|
+
#~meths end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
#:create
|
40
|
+
module Song::Activity
|
41
|
+
class Create < Trailblazer::Activity::Railway
|
42
|
+
step :model
|
43
|
+
step Nested(:decide_file_type,
|
44
|
+
auto_wire: [Id3Tag, VorbisComment]) # explicitely define possible nested activities.
|
45
|
+
step :save
|
46
|
+
#~meths
|
47
|
+
include T.def_steps(:model, :save)
|
48
|
+
#~meths end
|
49
|
+
|
50
|
+
def decide_file_type(ctx, params:, **)
|
51
|
+
params[:type] == "mp3" ? Id3Tag : VorbisComment
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
#:create end
|
56
|
+
|
57
|
+
end # A
|
58
|
+
|
59
|
+
#@ and the same with a decider callable
|
60
|
+
module AA
|
61
|
+
module Song
|
62
|
+
module Activity
|
63
|
+
Id3Tag = A::Song::Activity::Id3Tag
|
64
|
+
VorbisComment = A::Song::Activity::VorbisComment
|
65
|
+
|
66
|
+
class Create < Trailblazer::Activity::Railway
|
67
|
+
class MyDecider
|
68
|
+
def self.call(ctx, params:, **)
|
69
|
+
params[:type] == "mp3" ? Id3Tag : VorbisComment
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
step :model
|
74
|
+
step Nested(MyDecider,
|
75
|
+
auto_wire: [Id3Tag, VorbisComment]) # explicitely define possible nested activities.
|
76
|
+
step :save
|
77
|
+
#~meths
|
78
|
+
include T.def_steps(:model, :save)
|
79
|
+
#~meths end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "wires all nested termini to the outer tracks" do
|
86
|
+
#@ success for Id3Tag
|
87
|
+
assert_invoke A::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3, :save]}, params: {type: "mp3"}
|
88
|
+
assert_invoke AA::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3, :save]}, params: {type: "mp3"}
|
89
|
+
|
90
|
+
#@ failure for Id3Tag
|
91
|
+
assert_invoke A::Song::Activity::Create, seq: %{[:model, :parse]}, params: {type: "mp3"}, parse: false, terminus: :failure
|
92
|
+
assert_invoke AA::Song::Activity::Create, seq: %{[:model, :parse]}, params: {type: "mp3"}, parse: false, terminus: :failure
|
93
|
+
|
94
|
+
#@ success for VorbisComment
|
95
|
+
assert_invoke A::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
|
96
|
+
assert_invoke AA::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
|
97
|
+
#@ failure for VorbisComment
|
98
|
+
assert_invoke A::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover]}, params: {type: "vorbis"}, encode_cover: false, terminus: :failure
|
99
|
+
assert_invoke AA::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover]}, params: {type: "vorbis"}, encode_cover: false, terminus: :failure
|
100
|
+
end
|
101
|
+
|
102
|
+
#@ Additional terminus {End.invalid_metadata} in Nested activity
|
103
|
+
module B
|
104
|
+
class Song
|
105
|
+
end
|
106
|
+
|
107
|
+
module Song::Activity
|
108
|
+
class Id3Tag < Trailblazer::Activity::Railway
|
109
|
+
InvalidMetadata = Class.new(Trailblazer::Activity::Signal)
|
110
|
+
|
111
|
+
step :parse, Output(InvalidMetadata, :invalid_metadata) => End(:invalid_metadata) # We have a new terminus {End.invalid_metadata}
|
112
|
+
step :encode_id3
|
113
|
+
#~meths
|
114
|
+
include T.def_steps(:parse, :encode_id3)
|
115
|
+
|
116
|
+
def validate_metadata(params)
|
117
|
+
params[:is_valid]
|
118
|
+
end
|
119
|
+
#~meths end
|
120
|
+
def parse(ctx, params:, **)
|
121
|
+
unless validate_metadata(params)
|
122
|
+
return InvalidMetadata
|
123
|
+
end
|
124
|
+
#~body
|
125
|
+
ctx[:seq] << :parse
|
126
|
+
true
|
127
|
+
# TODO: also test returning false.
|
128
|
+
#~body end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
VorbisComment = A::Song::Activity::VorbisComment
|
133
|
+
|
134
|
+
class Create < Trailblazer::Activity::Railway
|
135
|
+
step :model
|
136
|
+
step Nested(:decide_file_type,
|
137
|
+
auto_wire: [Id3Tag, VorbisComment]), # explicitely define possible nested activities.
|
138
|
+
Output(:invalid_metadata) => Track(:failure)
|
139
|
+
|
140
|
+
step :save
|
141
|
+
#~meths
|
142
|
+
include T.def_steps(:model, :save)
|
143
|
+
#~meths end
|
144
|
+
|
145
|
+
def decide_file_type(ctx, params:, **)
|
146
|
+
params[:type] == "mp3" ? Id3Tag : VorbisComment
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end # B
|
152
|
+
|
153
|
+
# Add another End.ExeedsLimit to {VorbisComment}
|
154
|
+
module C
|
155
|
+
class Song
|
156
|
+
end
|
157
|
+
|
158
|
+
#:unsupported-terminus
|
159
|
+
module Song::Activity
|
160
|
+
class VorbisComment < Trailblazer::Activity::Railway
|
161
|
+
step :prepare_metadata
|
162
|
+
step :encode_cover, Output(:failure) => End(:unsupported_file_format)
|
163
|
+
#~meths
|
164
|
+
include T.def_steps(:prepare_metadata, :encode_cover)
|
165
|
+
#~meths end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
#:unsupported-terminus end
|
169
|
+
|
170
|
+
module Song::Activity
|
171
|
+
Id3Tag = B::Song::Activity::Id3Tag
|
172
|
+
|
173
|
+
class Create < Trailblazer::Activity::Railway
|
174
|
+
step :model
|
175
|
+
step Nested(:decide_file_type,
|
176
|
+
auto_wire: [Id3Tag, VorbisComment]), # explicitely define possible nested activities.
|
177
|
+
Output(:invalid_metadata) => Track(:failure),
|
178
|
+
Output(:unsupported_file_format) => End(:internal_error)
|
179
|
+
|
180
|
+
step :save
|
181
|
+
include T.def_steps(:model, :save)
|
182
|
+
|
183
|
+
def decide_file_type(ctx, params:, **)
|
184
|
+
params[:type] == "mp3" ? Id3Tag : VorbisComment
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
it "handle {InvalidMetadata}" do
|
192
|
+
#@ Id3Tag with valid metadata
|
193
|
+
assert_invoke B::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3, :save]}, params: {type: "mp3", is_valid: true}, terminus: :success
|
194
|
+
|
195
|
+
#@ InvalidMetadata returned from Id3Tag#parse
|
196
|
+
assert_invoke B::Song::Activity::Create, seq: %{[:model]}, params: {type: "mp3", is_valid: false}, terminus: :failure
|
197
|
+
end
|
198
|
+
|
199
|
+
it "handle {failure} from VorbisComment" do
|
200
|
+
assert_invoke C::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, params: {type: "vorbis"}, terminus: :failure, prepare_metadata: false
|
201
|
+
|
202
|
+
#@ UnsupportedFileFormat
|
203
|
+
assert_invoke C::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover]}, params: {type: "vorbis"}, terminus: :internal_error, encode_cover: false
|
204
|
+
end
|
205
|
+
|
206
|
+
module D
|
207
|
+
class Song; end
|
208
|
+
|
209
|
+
module Song::Activity
|
210
|
+
Id3Tag = A::Song::Activity::Id3Tag
|
211
|
+
VorbisComment = C::Song::Activity::VorbisComment
|
212
|
+
end
|
213
|
+
|
214
|
+
#:create-output
|
215
|
+
module Song::Activity
|
216
|
+
class Create < Trailblazer::Activity::Railway
|
217
|
+
step :model
|
218
|
+
step Nested(
|
219
|
+
:decide_file_type,
|
220
|
+
auto_wire: [Id3Tag, VorbisComment]
|
221
|
+
),
|
222
|
+
# Output and friends are used *after* Nested().
|
223
|
+
# Connect VorbisComment's {unsupported_file_format} to our {failure} track:
|
224
|
+
Output(:unsupported_file_format) => Track(:failure)
|
225
|
+
|
226
|
+
step :save
|
227
|
+
#~meths
|
228
|
+
include T.def_steps(:model, :save)
|
229
|
+
|
230
|
+
def decide_file_type(ctx, params:, **)
|
231
|
+
params[:type] == "mp3" ? Id3Tag : VorbisComment
|
232
|
+
end
|
233
|
+
#~meths end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
#:create-output end
|
237
|
+
end
|
238
|
+
|
239
|
+
it "Id3Tag's invalid_metadata goes to {End.failure}" do
|
240
|
+
assert_invoke D::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, params: {type: "vorbis"}, terminus: :failure, prepare_metadata: false
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
#@ FastTrack is mapped to outer FastTrack.
|
245
|
+
module E
|
246
|
+
class Song
|
247
|
+
end
|
248
|
+
|
249
|
+
module Song::Activity
|
250
|
+
class Id3Tag < Trailblazer::Activity::FastTrack
|
251
|
+
step :parse,
|
252
|
+
fail_fast: true,
|
253
|
+
pass_fast: true
|
254
|
+
step :encode_id3
|
255
|
+
include T.def_steps(:parse, :encode_id3)
|
256
|
+
end
|
257
|
+
|
258
|
+
VorbisComment = DocsNestedStaticTest::C::Song::Activity::VorbisComment # has an {End.unsupported_file_format} terminus.
|
259
|
+
end
|
260
|
+
|
261
|
+
#:static-fasttrack
|
262
|
+
module Song::Activity
|
263
|
+
class Create < Trailblazer::Activity::FastTrack
|
264
|
+
step :model
|
265
|
+
step Nested(:decide_file_type, auto_wire: [Id3Tag, VorbisComment]),
|
266
|
+
fast_track: true,
|
267
|
+
Output(:unsupported_file_format) => End(:unsupported_file_format)
|
268
|
+
step :save
|
269
|
+
#~meths
|
270
|
+
include T.def_steps(:model, :save)
|
271
|
+
def decide_file_type(ctx, params:, **)
|
272
|
+
params[:type] == "mp3" ? Id3Tag : VorbisComment
|
273
|
+
end
|
274
|
+
#~meths end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
#:static-fasttrack end
|
278
|
+
# puts Trailblazer::Developer.render(Song::Activity::Create)
|
279
|
+
end # C
|
280
|
+
|
281
|
+
it "FastTrack from nested_activity are mapped to respective tracks" do
|
282
|
+
#@ {End.pass_fast} goes success for Id3Tag
|
283
|
+
assert_invoke E::Song::Activity::Create, seq: %{[:model, :parse]}, params: {type: "mp3"}, terminus: :pass_fast
|
284
|
+
#@ {End.fail_fast} goes failure for Id3Tag
|
285
|
+
assert_invoke E::Song::Activity::Create, seq: %{[:model, :parse]}, params: {type: "mp3"}, parse: false, terminus: :fail_fast
|
286
|
+
|
287
|
+
assert_invoke E::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
|
288
|
+
assert_invoke E::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, params: {type: "vorbis"}, prepare_metadata: false, terminus: :failure
|
289
|
+
#@ VorbisComment :unsupported_file_format is mapped to :failure
|
290
|
+
assert_invoke E::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover]}, params: {type: "vorbis"}, encode_cover: false, terminus: :unsupported_file_format
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
class DocsNestedDynamicTest < Minitest::Spec
|
295
|
+
#@ dynamic without any other options
|
296
|
+
module A
|
297
|
+
class Song
|
298
|
+
end
|
299
|
+
|
300
|
+
module Song::Activity
|
301
|
+
Id3Tag = DocsNestedStaticTest::A::Song::Activity::Id3Tag
|
302
|
+
VorbisComment = DocsNestedStaticTest::A::Song::Activity::VorbisComment
|
303
|
+
end
|
304
|
+
|
305
|
+
#:dynamic
|
306
|
+
module Song::Activity
|
307
|
+
class Create < Trailblazer::Activity::Railway
|
308
|
+
step :model
|
309
|
+
step Nested(:decide_file_type) # Run either {Id3Tag} or {VorbisComment}
|
310
|
+
step :save
|
311
|
+
#~meths
|
312
|
+
include T.def_steps(:model, :save)
|
313
|
+
#~meths end
|
314
|
+
def decide_file_type(ctx, params:, **)
|
315
|
+
params[:type] == "mp3" ? Id3Tag : VorbisComment
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
#:dynamic end
|
320
|
+
end # A
|
321
|
+
|
322
|
+
#@ and the same with a decider callable
|
323
|
+
module AA
|
324
|
+
module Song
|
325
|
+
module Activity
|
326
|
+
Id3Tag = A::Song::Activity::Id3Tag
|
327
|
+
VorbisComment = A::Song::Activity::VorbisComment
|
328
|
+
|
329
|
+
class Create < Trailblazer::Activity::Railway
|
330
|
+
MyDecider = DocsNestedStaticTest::AA::Song::Activity::Create::MyDecider
|
331
|
+
|
332
|
+
step :model
|
333
|
+
step Nested(MyDecider,
|
334
|
+
auto_wire: [Id3Tag, VorbisComment]) # explicitely define possible nested activities.
|
335
|
+
step :save
|
336
|
+
#~meths
|
337
|
+
include T.def_steps(:model, :save)
|
338
|
+
#~meths end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
it "nested {success} and {failure} are wired to respective tracks on the outside" do
|
345
|
+
#@ success for Id3Tag means success track on the outside
|
346
|
+
assert_invoke A::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3, :save]}, params: {type: "mp3"}
|
347
|
+
assert_invoke AA::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3, :save]}, params: {type: "mp3"}
|
348
|
+
assert_invoke A::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3]}, terminus: :failure, params: {type: "mp3"}, encode_id3: false
|
349
|
+
assert_invoke AA::Song::Activity::Create, seq: %{[:model, :parse, :encode_id3]}, terminus: :failure, params: {type: "mp3"}, encode_id3: false
|
350
|
+
|
351
|
+
#@ success for {VorbisComment}
|
352
|
+
assert_invoke A::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
|
353
|
+
assert_invoke AA::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
|
354
|
+
assert_invoke A::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, terminus: :failure, params: {type: "vorbis"}, prepare_metadata: false
|
355
|
+
assert_invoke AA::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, terminus: :failure, params: {type: "vorbis"}, prepare_metadata: false
|
356
|
+
end
|
357
|
+
|
358
|
+
#@ raises RuntimeError if we try to wire special terminus.
|
359
|
+
it "raises when wiring special termini" do
|
360
|
+
exception = assert_raises RuntimeError do
|
361
|
+
module B
|
362
|
+
class Song; end
|
363
|
+
#:dynamic-output
|
364
|
+
module Song::Activity
|
365
|
+
class Create < Trailblazer::Activity::Railway
|
366
|
+
step :model
|
367
|
+
step Nested(:decide_file_type),
|
368
|
+
Output(:unsupported_file_format) => Track(:failure) # error!
|
369
|
+
step :save
|
370
|
+
end
|
371
|
+
end
|
372
|
+
#:dynamic-output end
|
373
|
+
end
|
374
|
+
end # B
|
375
|
+
|
376
|
+
assert_equal exception.message[0..34], %{No `unsupported_file_format` output}
|
377
|
+
end
|
378
|
+
|
379
|
+
#@ any internal "special" terminus is mapped to either failure or success.
|
380
|
+
#@ FastTrack is converted to Binary outcome.
|
381
|
+
module C
|
382
|
+
class Song
|
383
|
+
end
|
384
|
+
|
385
|
+
module Song::Activity
|
386
|
+
Id3Tag = DocsNestedStaticTest::E::Song::Activity::Id3Tag # has {fail_fast} and {pass_fast} termini.
|
387
|
+
VorbisComment = DocsNestedStaticTest::C::Song::Activity::VorbisComment # has an {End.unsupported_file_format} terminus.
|
388
|
+
end
|
389
|
+
|
390
|
+
#:dynamic-unsupported
|
391
|
+
module Song::Activity
|
392
|
+
class Create < Trailblazer::Activity::Railway
|
393
|
+
step :model
|
394
|
+
step Nested(:decide_file_type) # Run either {Id3Tag} or {VorbisComment}
|
395
|
+
step :save
|
396
|
+
#~meths
|
397
|
+
include T.def_steps(:model, :save)
|
398
|
+
def decide_file_type(ctx, params:, **)
|
399
|
+
params[:type] == "mp3" ? Id3Tag : VorbisComment
|
400
|
+
end
|
401
|
+
#~meths end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
#:dynamic-unsupported end
|
405
|
+
end # C
|
406
|
+
|
407
|
+
it "{VorbisComment}'s {End.unsupported_file_format} is mapped to {:failure}" do
|
408
|
+
#@ {End.pass_fast} goes success for Id3Tag
|
409
|
+
assert_invoke C::Song::Activity::Create, seq: %{[:model, :parse, :save]}, params: {type: "mp3"}
|
410
|
+
#@ {End.fail_fast} goes failure for Id3Tag
|
411
|
+
assert_invoke C::Song::Activity::Create, seq: %{[:model, :parse]}, params: {type: "mp3"}, parse: false, terminus: :failure
|
412
|
+
|
413
|
+
assert_invoke C::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover, :save]}, params: {type: "vorbis"}
|
414
|
+
assert_invoke C::Song::Activity::Create, seq: %{[:model, :prepare_metadata]}, params: {type: "vorbis"}, prepare_metadata: false, terminus: :failure
|
415
|
+
#@ VorbisComment :unsupported_file_format is mapped to :failure
|
416
|
+
assert_invoke C::Song::Activity::Create, seq: %{[:model, :prepare_metadata, :encode_cover]}, params: {type: "vorbis"}, encode_cover: false, terminus: :failure
|
417
|
+
end
|
418
|
+
|
419
|
+
it "is compatible with Debugging API" do
|
420
|
+
output, _ = trace A::Song::Activity::Create, params: {type: "mp3"}, seq: []
|
421
|
+
|
422
|
+
assert_equal output, %{TOP
|
423
|
+
|-- Start.default
|
424
|
+
|-- model
|
425
|
+
|-- Nested/decide_file_type
|
426
|
+
| |-- Start.default
|
427
|
+
| |-- call_dynamic_nested_activity
|
428
|
+
| | `-- DocsNestedStaticTest::A::Song::Activity::Id3Tag
|
429
|
+
| | |-- Start.default
|
430
|
+
| | |-- parse
|
431
|
+
| | |-- encode_id3
|
432
|
+
| | `-- End.success
|
433
|
+
| `-- End.success
|
434
|
+
|-- save
|
435
|
+
`-- End.success}
|
436
|
+
|
437
|
+
#@ we can look into non-dynamic parts
|
438
|
+
assert_equal Trailblazer::Developer::Introspect.find_path(A::Song::Activity::Create,
|
439
|
+
["Nested/decide_file_type", :call_dynamic_nested_activity])[0].task.class.inspect, %{Method}
|
440
|
+
|
441
|
+
#@ we can't look into the dynamic parts
|
442
|
+
assert_raises do
|
443
|
+
Trailblazer::Developer::Introspect.find_path(A::Song::Activity::Create,
|
444
|
+
["Nested/decide_file_type", :call_dynamic_nested_activity, DocsNestedStaticTest::A::Song::Activity::Id3Tag])[0]
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
class GenericNestedUnitTest < Minitest::Spec
|
450
|
+
module ComputeNested
|
451
|
+
module_function
|
452
|
+
|
453
|
+
def compute_nested(ctx, what:, **)
|
454
|
+
what
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
it "shows warning if `Nested()` is being used instead of `Subprocess()`" do
|
459
|
+
activity_classes = [Trailblazer::Activity::Path, Trailblazer::Activity::Railway, Trailblazer::Activity::FastTrack, Trailblazer::Operation]
|
460
|
+
|
461
|
+
activity_classes.each do |activity_class|
|
462
|
+
activity = Class.new(activity_class) # the "nested" activity.
|
463
|
+
|
464
|
+
_, warnings = capture_io do
|
465
|
+
Class.new(Trailblazer::Activity::Railway) do
|
466
|
+
step Nested(activity)
|
467
|
+
end
|
468
|
+
end
|
469
|
+
line_number_for_nested = __LINE__ - 3
|
470
|
+
puts _
|
471
|
+
|
472
|
+
assert_equal warnings, %{[Trailblazer] #{File.realpath(__FILE__)}:#{line_number_for_nested} Using the `Nested()` macro without a dynamic decider is deprecated.
|
473
|
+
To simply nest an activity or operation, replace `Nested(#{activity})` with `Subprocess(#{activity})`.
|
474
|
+
Check the Subprocess API docs to learn more about nesting: https://trailblazer.to/2.1/docs/activity.html#activity-wiring-api-subprocess
|
475
|
+
}
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
it "allows using multiple Nested() per operation" do
|
480
|
+
activity = Class.new(Trailblazer::Activity::Railway) do
|
481
|
+
step :a
|
482
|
+
step Nested(:decide)
|
483
|
+
step Nested(:decide), id: "Nested(2)"
|
484
|
+
# TODO: with static, too!
|
485
|
+
step :b
|
486
|
+
|
487
|
+
def decide(ctx, **)
|
488
|
+
DocsNestedStaticTest::A::Song::Activity::Create
|
489
|
+
end
|
490
|
+
|
491
|
+
include T.def_steps(:a, :b)
|
492
|
+
end
|
493
|
+
|
494
|
+
assert_invoke activity, seq: %{[:a, :model, :parse, :encode_id3, :save, :model, :parse, :encode_id3, :save, :b]}, params: {type: "mp3"}
|
495
|
+
end
|
496
|
+
|
497
|
+
it "allows I/O when using Nested(Activity) in Subprocess mode" do
|
498
|
+
activity = Class.new(Trailblazer::Activity::Railway) do
|
499
|
+
nested_activity = Class.new(Trailblazer::Activity::Railway) do
|
500
|
+
step ->(ctx, **) { ctx[:message] = Object }
|
501
|
+
step ->(ctx, **) { ctx[:status] = Class }
|
502
|
+
end
|
503
|
+
|
504
|
+
step Nested(nested_activity),
|
505
|
+
Out() => [:status]
|
506
|
+
end
|
507
|
+
|
508
|
+
assert_invoke activity, seq: %{[]}, expected_ctx_variables: {status: Class}
|
509
|
+
end
|
510
|
+
|
511
|
+
# TODO: move this to some testing gem? We need it a lot of times.
|
512
|
+
def self.step_capturing_visible_variables(ctx, **)
|
513
|
+
ctx[:visible] = ctx.keys
|
514
|
+
end
|
515
|
+
|
516
|
+
def activity_with_visible_variable
|
517
|
+
Class.new(Trailblazer::Activity::Railway) do
|
518
|
+
step GenericNestedUnitTest.method(:step_capturing_visible_variables)
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
def decider_with_visible_variable(ctx, what:, **)
|
523
|
+
ctx[:visible_in_decider] << ctx.keys # this is horrible, we're bleeding through to a "global" variable.
|
524
|
+
what
|
525
|
+
end
|
526
|
+
|
527
|
+
it "nested activity can see everything Nested() can see" do
|
528
|
+
sub_activity = activity_with_visible_variable()
|
529
|
+
|
530
|
+
#@ nested can see everything.
|
531
|
+
activity = Class.new(Trailblazer::Activity::Railway)
|
532
|
+
activity.step Trailblazer::Activity::Railway.Nested(ComputeNested.method(:compute_nested),
|
533
|
+
auto_wire: [sub_activity])
|
534
|
+
|
535
|
+
assert_invoke activity, what: sub_activity, dont_look_at_me: true, expected_ctx_variables: {visible: [:seq, :what, :dont_look_at_me]}
|
536
|
+
|
537
|
+
|
538
|
+
#@ nested can only see {:what}.
|
539
|
+
activity = Class.new(Trailblazer::Activity::Railway)
|
540
|
+
activity.step Trailblazer::Activity::Railway.Nested(ComputeNested.method(:compute_nested),
|
541
|
+
auto_wire: [sub_activity]),
|
542
|
+
Trailblazer::Activity::Railway.In() => [:what]
|
543
|
+
|
544
|
+
assert_invoke activity, what: sub_activity, dont_look_at_me: true, expected_ctx_variables: {visible: [:what]}
|
545
|
+
end
|
546
|
+
|
547
|
+
it "decider's variables are not discarded" do
|
548
|
+
sub_activity = activity_with_visible_variable()
|
549
|
+
compute_nested = ->(ctx, what:, **) do
|
550
|
+
ctx[:please_discard_me] = true # this bleeds through to all descendents.
|
551
|
+
what # this however is discarded.
|
552
|
+
end
|
553
|
+
|
554
|
+
#@ for Static
|
555
|
+
activity = Class.new(Trailblazer::Activity::Railway)
|
556
|
+
activity.step Trailblazer::Activity::Railway.Nested(compute_nested,
|
557
|
+
auto_wire: [sub_activity])
|
558
|
+
|
559
|
+
#@ nested_activity and top activity can see things from decider.
|
560
|
+
expected_variables = {please_discard_me: true, visible: [:seq, :what, :please_discard_me]}
|
561
|
+
|
562
|
+
assert_invoke activity, what: sub_activity, expected_ctx_variables: expected_variables
|
563
|
+
|
564
|
+
#@ for dynamic
|
565
|
+
activity = Class.new(Trailblazer::Activity::Railway)
|
566
|
+
activity.step Trailblazer::Activity::Railway.Nested(compute_nested)
|
567
|
+
|
568
|
+
#@ nested_activity and top activity cannot see things from decider.
|
569
|
+
assert_invoke activity, what: sub_activity, expected_ctx_variables: expected_variables
|
570
|
+
end
|
571
|
+
|
572
|
+
it "without In/Out, decider and nested_activity see the same" do
|
573
|
+
sub_activity = activity_with_visible_variable()
|
574
|
+
compute_nested = method(:decider_with_visible_variable)
|
575
|
+
|
576
|
+
#@ for Static
|
577
|
+
activity = Class.new(Trailblazer::Activity::Railway)
|
578
|
+
activity.step Trailblazer::Activity::Railway.Nested(compute_nested,
|
579
|
+
auto_wire: [sub_activity])
|
580
|
+
|
581
|
+
#@ nested_activity and top activity cannot see things from decider.
|
582
|
+
options = {
|
583
|
+
what: sub_activity, expected_ctx_variables: {
|
584
|
+
visible: [:seq, :what, :visible_in_decider],
|
585
|
+
visible_in_decider: [[:seq, :what, :visible_in_decider]]
|
586
|
+
}
|
587
|
+
}
|
588
|
+
|
589
|
+
assert_invoke activity, **options, visible_in_decider: []
|
590
|
+
|
591
|
+
#@ for dynamic
|
592
|
+
activity = Class.new(Trailblazer::Activity::Railway)
|
593
|
+
activity.step Trailblazer::Activity::Railway.Nested(compute_nested)
|
594
|
+
|
595
|
+
#@ nested_activity and top activity cannot see things from decider.
|
596
|
+
assert_invoke activity, **options, visible_in_decider: []
|
597
|
+
end
|
598
|
+
|
599
|
+
it "with In/Out, decider sees original ctx, nested_activity sees filtered" do
|
600
|
+
# sees {:activity_to_nest}
|
601
|
+
decider_with_visible_variable = ->(ctx, activity_to_nest:, **) do
|
602
|
+
ctx[:visible_in_decider] << ctx.keys # this is horrible, we're bleeding through to a "global" variable.
|
603
|
+
activity_to_nest
|
604
|
+
end
|
605
|
+
|
606
|
+
sub_activity = activity_with_visible_variable()
|
607
|
+
compute_nested = decider_with_visible_variable
|
608
|
+
in_out_options = {
|
609
|
+
Trailblazer::Activity::Railway.In() => {:activity_to_nest => :what}#,
|
610
|
+
# Trailblazer::Activity::Railway.In() => [:visible_in_decider]
|
611
|
+
}
|
612
|
+
|
613
|
+
#@ for Static
|
614
|
+
activity = Class.new(Trailblazer::Activity::Railway)
|
615
|
+
activity.step Trailblazer::Activity::Railway.Nested(compute_nested,
|
616
|
+
auto_wire: [sub_activity]).merge(in_out_options)
|
617
|
+
|
618
|
+
#@ nested_activity and top activity cannot see things from decider.
|
619
|
+
options = {
|
620
|
+
please_discard_me: true,
|
621
|
+
activity_to_nest: sub_activity, # renamed to {:what}
|
622
|
+
expected_ctx_variables: {
|
623
|
+
visible: [:what],
|
624
|
+
visible_in_decider: [[:seq, :please_discard_me, :activity_to_nest, :visible_in_decider]]
|
625
|
+
}
|
626
|
+
}
|
627
|
+
|
628
|
+
assert_invoke activity, **options, visible_in_decider: []
|
629
|
+
|
630
|
+
#@ for dynamic
|
631
|
+
activity = Class.new(Trailblazer::Activity::Railway)
|
632
|
+
activity.step Trailblazer::Activity::Railway.Nested(compute_nested).
|
633
|
+
merge(in_out_options)
|
634
|
+
|
635
|
+
#@ nested_activity and top activity cannot see things from decider.
|
636
|
+
assert_invoke activity, **options, visible_in_decider: []
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
class NestedStrategyComplianceTest < Minitest::Spec
|
641
|
+
Song = DocsNestedStaticTest::A::Song
|
642
|
+
|
643
|
+
it "is compatible with Debugging API" do
|
644
|
+
trace = %{
|
645
|
+
#:create-trace
|
646
|
+
Song::Activity::Create
|
647
|
+
|-- Start.default
|
648
|
+
|-- model
|
649
|
+
|-- Nested/decide_file_type
|
650
|
+
| |-- Start.default
|
651
|
+
| |-- route_to_nested_activity
|
652
|
+
| |-- DocsNestedStaticTest::A::Song::Activity::VorbisComment
|
653
|
+
| | |-- Start.default
|
654
|
+
| | |-- prepare_metadata
|
655
|
+
| | |-- encode_cover
|
656
|
+
| | `-- End.success
|
657
|
+
| `-- End.success
|
658
|
+
|-- save
|
659
|
+
`-- End.success
|
660
|
+
#:create-trace end
|
661
|
+
}
|
662
|
+
|
663
|
+
Trailblazer::Developer.wtf?(Song::Activity::Create, [{params: {type: "vorbis"}, seq: []}])
|
664
|
+
|
665
|
+
output, _ = trace Song::Activity::Create, params: {type: "vorbis"}, seq: []
|
666
|
+
assert_equal output, trace.split("\n")[2..-2].join("\n").sub("Song::Activity::Create", "TOP")
|
667
|
+
|
668
|
+
output, _ = trace Song::Activity::Create, params: {type: "mp3"}, seq: []
|
669
|
+
assert_equal output, %{TOP
|
670
|
+
|-- Start.default
|
671
|
+
|-- model
|
672
|
+
|-- Nested/decide_file_type
|
673
|
+
| |-- Start.default
|
674
|
+
| |-- route_to_nested_activity
|
675
|
+
| |-- DocsNestedStaticTest::A::Song::Activity::Id3Tag
|
676
|
+
| | |-- Start.default
|
677
|
+
| | |-- parse
|
678
|
+
| | |-- encode_id3
|
679
|
+
| | `-- End.success
|
680
|
+
| `-- End.success
|
681
|
+
|-- save
|
682
|
+
`-- End.success}
|
683
|
+
|
684
|
+
|
685
|
+
#@ compile time
|
686
|
+
#@ make sure we can find tasks/compile-time artifacts in Each by using their {compile_id}.
|
687
|
+
assert_equal Trailblazer::Developer::Introspect.find_path(Song::Activity::Create,
|
688
|
+
["Nested/decide_file_type", DocsNestedStaticTest::A::Song::Activity::Id3Tag, :encode_id3])[0].task.inspect,
|
689
|
+
%{#<Trailblazer::Activity::TaskBuilder::Task user_proc=encode_id3>}
|
690
|
+
|
691
|
+
output =
|
692
|
+
#:create-introspect
|
693
|
+
Trailblazer::Developer.render(Song::Activity::Create,
|
694
|
+
path: [
|
695
|
+
"Nested/decide_file_type", # ID of Nested()
|
696
|
+
Song::Activity::Id3Tag # ID of the nested {Id3Tag} activity.
|
697
|
+
]
|
698
|
+
)
|
699
|
+
#:create-introspect end
|
700
|
+
assert_match /user_proc=encode_id3>/, output
|
701
|
+
end
|
702
|
+
|
703
|
+
# TODO: test more options.
|
704
|
+
it "assigns ID via {Macro.id_for}" do
|
705
|
+
activity = Class.new(Trailblazer::Activity::Railway) do
|
706
|
+
step Nested(:decide_file_type, auto_wire: [Trailblazer::Activity::Railway])
|
707
|
+
end
|
708
|
+
|
709
|
+
assert_equal Trailblazer::Developer::Introspect.find_path(activity,
|
710
|
+
["Nested/decide_file_type"])[0].id, %{Nested/decide_file_type}
|
711
|
+
end
|
712
|
+
|
713
|
+
it do
|
714
|
+
skip
|
715
|
+
|
716
|
+
#:patch
|
717
|
+
faster_mp3 = Trailblazer::Activity::DSL::Linear.Patch(
|
718
|
+
Song::Activity::Create,
|
719
|
+
["Nested/decide_file_type", Song::Activity::Id3Tag] => -> { step :fast_encode_id3, replace: :encode_id3 }
|
720
|
+
)
|
721
|
+
#:patch end
|
722
|
+
faster_mp3.include(T.def_steps(:fast_encode_id3))
|
723
|
+
# Trailblazer::Developer.wtf?(faster_mp3, [{params: {type: "mp3"}, seq: []}])
|
724
|
+
|
725
|
+
#@ Original class isn't changed.
|
726
|
+
assert_invoke Song::Activity::Create, params: {type: "mp3"}, seq: "[:model, :parse, :encode_id3, :save]"
|
727
|
+
#@ Patched class runs
|
728
|
+
assert_invoke faster_mp3, params: {type: "mp3"}, seq: "[:model, :parse, :fast_encode_id3, :save]"
|
729
|
+
end
|
730
|
+
end
|