trailblazer-macro 2.1.11 → 2.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +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 +33 -0
- data/Gemfile +7 -5
- data/lib/trailblazer/macro/each.rb +187 -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 +940 -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 +21 -58
- 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
|