ruby_slm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.idea/.gitignore +8 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +768 -0
- data/Rakefile +16 -0
- data/examples/test_complex_workflow.rb +747 -0
- data/examples/test_parallel_complex_workflow.rb +983 -0
- data/lib/ruby_slm/errors.rb +24 -0
- data/lib/ruby_slm/execution.rb +176 -0
- data/lib/ruby_slm/state.rb +47 -0
- data/lib/ruby_slm/state_machine.rb +140 -0
- data/lib/ruby_slm/states/base.rb +149 -0
- data/lib/ruby_slm/states/choice.rb +144 -0
- data/lib/ruby_slm/states/fail.rb +62 -0
- data/lib/ruby_slm/states/parallel.rb +178 -0
- data/lib/ruby_slm/states/pass.rb +42 -0
- data/lib/ruby_slm/states/succeed.rb +39 -0
- data/lib/ruby_slm/states/task.rb +523 -0
- data/lib/ruby_slm/states/wait.rb +123 -0
- data/lib/ruby_slm/version.rb +5 -0
- data/lib/ruby_slm.rb +50 -0
- data/sig/states_language_machine.rbs +4 -0
- data/test/test_state_machine.rb +52 -0
- metadata +146 -0
|
@@ -0,0 +1,983 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'yaml'
|
|
5
|
+
require 'securerandom'
|
|
6
|
+
|
|
7
|
+
# Enhanced ComplexOrderProcessor with parallel processing capabilities
|
|
8
|
+
class ComplexOrderProcessor
|
|
9
|
+
# Order Classification
|
|
10
|
+
def determine_order_type(input)
|
|
11
|
+
order = input['order'] || {}
|
|
12
|
+
|
|
13
|
+
total = order['total'].to_f
|
|
14
|
+
items = order['items'] || []
|
|
15
|
+
quantity = order['quantity'].to_i
|
|
16
|
+
|
|
17
|
+
puts " [determine_order_type] Analyzing order: #{order['id']} - Total: $#{total}, Items: #{items.size}, Quantity: #{quantity}"
|
|
18
|
+
|
|
19
|
+
# Business logic for order classification
|
|
20
|
+
if total > 500 || order['premium_customer']
|
|
21
|
+
{ "order_type" => "premium", "reason" => "high_value_or_premium_customer" }
|
|
22
|
+
elsif quantity > 10 || total > 1000
|
|
23
|
+
{ "order_type" => "bulk", "reason" => "large_quantity" }
|
|
24
|
+
elsif order['shipping'] && order['shipping']['country'] != 'US'
|
|
25
|
+
{ "order_type" => "international", "reason" => "international_shipping" }
|
|
26
|
+
elsif order['digital_product']
|
|
27
|
+
{ "order_type" => "digital", "reason" => "digital_product" }
|
|
28
|
+
else
|
|
29
|
+
{ "order_type" => "standard", "reason" => "regular_order" }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Premium Order Processing
|
|
34
|
+
def process_premium_order(input)
|
|
35
|
+
order = input['order']
|
|
36
|
+
puts " [process_premium_order] VIP processing for: #{order['id']}"
|
|
37
|
+
|
|
38
|
+
{
|
|
39
|
+
"premium_processed" => true,
|
|
40
|
+
"order_id" => order['id'],
|
|
41
|
+
"vip_handling" => true,
|
|
42
|
+
"dedicated_support" => true,
|
|
43
|
+
"processing_tier" => "premium",
|
|
44
|
+
"priority_level" => 1
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Bulk Order Processing
|
|
49
|
+
def check_bulk_inventory(input)
|
|
50
|
+
order = input['order']
|
|
51
|
+
required_quantity = input['required_quantity']
|
|
52
|
+
|
|
53
|
+
puts " [check_bulk_inventory] Checking bulk inventory for: #{order['id']}"
|
|
54
|
+
|
|
55
|
+
# Simulate inventory check with occasional delays
|
|
56
|
+
sleep(0.05) if rand(0..1) == 0 # Simulate API call
|
|
57
|
+
|
|
58
|
+
available = rand(0..1) == 1
|
|
59
|
+
{
|
|
60
|
+
"available" => available,
|
|
61
|
+
"checked_at" => Time.now.to_i,
|
|
62
|
+
"required_quantity" => required_quantity,
|
|
63
|
+
"available_quantity" => available ? required_quantity : 0,
|
|
64
|
+
"warehouse" => "main_warehouse"
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def process_bulk_order(input)
|
|
69
|
+
order = input['order']
|
|
70
|
+
puts " [process_bulk_order] Processing bulk order: #{order['id']}"
|
|
71
|
+
|
|
72
|
+
{
|
|
73
|
+
"bulk_processed" => true,
|
|
74
|
+
"order_id" => order['id'],
|
|
75
|
+
"volume_discount_applied" => true,
|
|
76
|
+
"special_handling" => true,
|
|
77
|
+
"bulk_tier" => "large"
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# International Order Processing
|
|
82
|
+
def process_international_order(input)
|
|
83
|
+
order = input['order']
|
|
84
|
+
country = input['destination_country']
|
|
85
|
+
|
|
86
|
+
puts " [process_international_order] International processing for: #{order['id']} to #{country}"
|
|
87
|
+
|
|
88
|
+
{
|
|
89
|
+
"international_processed" => true,
|
|
90
|
+
"order_id" => order['id'],
|
|
91
|
+
"destination_country" => country,
|
|
92
|
+
"export_documentation" => "required",
|
|
93
|
+
"customs_declaration" => "needed",
|
|
94
|
+
"requires_export_license" => country == 'IR' # Example restriction
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def calculate_customs_duty(input)
|
|
99
|
+
order_value = input['order_value'].to_f
|
|
100
|
+
country = input['country']
|
|
101
|
+
|
|
102
|
+
puts " [calculate_customs_duty] Calculating duty for $#{order_value} to #{country}"
|
|
103
|
+
|
|
104
|
+
# Simple customs calculation
|
|
105
|
+
duty_rate = case country
|
|
106
|
+
when 'CA', 'MX' then 0.05
|
|
107
|
+
when 'EU' then 0.15
|
|
108
|
+
when 'UK' then 0.12
|
|
109
|
+
else 0.10
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
duty_amount = order_value * duty_rate
|
|
113
|
+
|
|
114
|
+
{
|
|
115
|
+
"duty_calculated" => true,
|
|
116
|
+
"duty_rate" => duty_rate,
|
|
117
|
+
"duty_amount" => duty_amount,
|
|
118
|
+
"total_with_duty" => order_value + duty_amount,
|
|
119
|
+
"currency" => "USD",
|
|
120
|
+
"calculation_method" => "standard"
|
|
121
|
+
}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Digital Order Processing
|
|
125
|
+
def process_digital_order(input)
|
|
126
|
+
order = input['order']
|
|
127
|
+
puts " [process_digital_order] Digital order processing: #{order['id']}"
|
|
128
|
+
|
|
129
|
+
{
|
|
130
|
+
"digital_processed" => true,
|
|
131
|
+
"order_id" => order['id'],
|
|
132
|
+
"product_type" => order['digital_product']['type'],
|
|
133
|
+
"instant_delivery" => true,
|
|
134
|
+
"digital_rights" => "granted"
|
|
135
|
+
}
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def generate_digital_access(input)
|
|
139
|
+
puts " [generate_digital_access] Generating access for: #{input['order_id']}"
|
|
140
|
+
|
|
141
|
+
{
|
|
142
|
+
"access_generated" => true,
|
|
143
|
+
"order_id" => input['order_id'],
|
|
144
|
+
"access_codes" => ["CODE-#{SecureRandom.hex(8)}"],
|
|
145
|
+
"download_links" => ["https://download.example.com/#{SecureRandom.hex(4)}"],
|
|
146
|
+
"license_key" => "LIC-#{SecureRandom.hex(12)}",
|
|
147
|
+
"valid_until" => (Time.now + 365 * 24 * 60 * 60).to_i # 1 year
|
|
148
|
+
}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Standard Order Processing
|
|
152
|
+
def process_standard_order(input)
|
|
153
|
+
order = input['order']
|
|
154
|
+
puts " [process_standard_order] Standard processing: #{order['id']}"
|
|
155
|
+
|
|
156
|
+
{
|
|
157
|
+
"standard_processed" => true,
|
|
158
|
+
"order_id" => order['id'],
|
|
159
|
+
"processing_tier" => "standard",
|
|
160
|
+
"service_level" => "normal"
|
|
161
|
+
}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Payment Processing with retry simulation
|
|
165
|
+
def process_payment(input)
|
|
166
|
+
amount = input['amount'].to_f
|
|
167
|
+
payment_method = input['payment_method']
|
|
168
|
+
|
|
169
|
+
puts " [process_payment] Processing $#{amount} via #{payment_method}"
|
|
170
|
+
|
|
171
|
+
# Simulate payment processing with retry scenarios
|
|
172
|
+
@payment_attempts ||= {}
|
|
173
|
+
order_id = input['order_id']
|
|
174
|
+
@payment_attempts[order_id] ||= 0
|
|
175
|
+
@payment_attempts[order_id] += 1
|
|
176
|
+
|
|
177
|
+
# Fail first attempt for certain conditions to test retry
|
|
178
|
+
if @payment_attempts[order_id] == 1 && amount > 1500
|
|
179
|
+
raise "PaymentGatewayTimeout - Gateway busy, please retry"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
success = amount < 2000 && payment_method != 'expired_card'
|
|
183
|
+
|
|
184
|
+
if success
|
|
185
|
+
{
|
|
186
|
+
"status" => "completed",
|
|
187
|
+
"payment_id" => "pay_#{SecureRandom.hex(8)}",
|
|
188
|
+
"amount_charged" => amount,
|
|
189
|
+
"currency" => input['currency'],
|
|
190
|
+
"processed_at" => Time.now.to_i,
|
|
191
|
+
"attempts" => @payment_attempts[order_id]
|
|
192
|
+
}
|
|
193
|
+
else
|
|
194
|
+
raise "Payment declined: #{payment_method} cannot process $#{amount}"
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def wait_for_payment_confirmation(input)
|
|
199
|
+
payment_id = input['payment_id']
|
|
200
|
+
|
|
201
|
+
puts " [wait_for_payment_confirmation] Waiting for confirmation: #{payment_id}"
|
|
202
|
+
|
|
203
|
+
# Simulate waiting for confirmation
|
|
204
|
+
sleep(0.1)
|
|
205
|
+
|
|
206
|
+
{
|
|
207
|
+
"confirmed" => true,
|
|
208
|
+
"payment_id" => payment_id,
|
|
209
|
+
"confirmation_code" => "CONF-#{SecureRandom.hex(6)}",
|
|
210
|
+
"confirmed_at" => Time.now.to_i,
|
|
211
|
+
"confirmation_method" => "3ds_secure"
|
|
212
|
+
}
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def finalize_payment(input)
|
|
216
|
+
puts " [finalize_payment] Finalizing: #{input['payment_id']}"
|
|
217
|
+
|
|
218
|
+
{
|
|
219
|
+
"finalized" => true,
|
|
220
|
+
"payment_id" => input['payment_id'],
|
|
221
|
+
"status" => "completed",
|
|
222
|
+
"finalized_at" => Time.now.to_i,
|
|
223
|
+
"settlement_initiated" => true
|
|
224
|
+
}
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Inventory Management
|
|
228
|
+
def update_inventory(input)
|
|
229
|
+
order_id = input['order_id']
|
|
230
|
+
items = input['items']
|
|
231
|
+
|
|
232
|
+
puts " [update_inventory] Updating inventory for: #{order_id}"
|
|
233
|
+
|
|
234
|
+
# Simulate inventory update with occasional stock issues
|
|
235
|
+
out_of_stock = rand(0..9) == 0 # 10% chance of out of stock
|
|
236
|
+
|
|
237
|
+
if out_of_stock
|
|
238
|
+
raise "OutOfStock - Item unavailable for order #{order_id}"
|
|
239
|
+
else
|
|
240
|
+
{
|
|
241
|
+
"inventory_updated" => true,
|
|
242
|
+
"order_id" => order_id,
|
|
243
|
+
"items_processed" => items.size,
|
|
244
|
+
"updated_at" => Time.now.to_i,
|
|
245
|
+
"inventory_system" => "warehouse_db_v2"
|
|
246
|
+
}
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Shipping Methods
|
|
251
|
+
def schedule_express_shipping(input)
|
|
252
|
+
order = input['order']
|
|
253
|
+
puts " [schedule_express_shipping] Express shipping for: #{order['id']}"
|
|
254
|
+
|
|
255
|
+
{
|
|
256
|
+
"shipping_scheduled" => true,
|
|
257
|
+
"order_id" => order['id'],
|
|
258
|
+
"method" => "express",
|
|
259
|
+
"estimated_days" => 1,
|
|
260
|
+
"tracking_number" => "EXP#{SecureRandom.hex(6).upcase}",
|
|
261
|
+
"priority" => "high",
|
|
262
|
+
"carrier" => "fedex_priority"
|
|
263
|
+
}
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def schedule_standard_shipping(input)
|
|
267
|
+
order = input['order']
|
|
268
|
+
puts " [schedule_standard_shipping] Standard shipping for: #{order['id']}"
|
|
269
|
+
|
|
270
|
+
{
|
|
271
|
+
"shipping_scheduled" => true,
|
|
272
|
+
"order_id" => order['id'],
|
|
273
|
+
"method" => "standard",
|
|
274
|
+
"estimated_days" => 3,
|
|
275
|
+
"tracking_number" => "STD#{SecureRandom.hex(6).upcase}",
|
|
276
|
+
"carrier" => "ups_ground"
|
|
277
|
+
}
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def schedule_economy_shipping(input)
|
|
281
|
+
order = input['order']
|
|
282
|
+
puts " [schedule_economy_shipping] Economy shipping for: #{order['id']}"
|
|
283
|
+
|
|
284
|
+
{
|
|
285
|
+
"shipping_scheduled" => true,
|
|
286
|
+
"order_id" => order['id'],
|
|
287
|
+
"method" => "economy",
|
|
288
|
+
"estimated_days" => 7,
|
|
289
|
+
"tracking_number" => "ECO#{SecureRandom.hex(6).upcase}",
|
|
290
|
+
"carrier" => "usps_parcel"
|
|
291
|
+
}
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Digital Delivery
|
|
295
|
+
def send_digital_delivery(input)
|
|
296
|
+
order = input['order']
|
|
297
|
+
puts " [send_digital_delivery] Sending digital delivery: #{order['id']}"
|
|
298
|
+
|
|
299
|
+
{
|
|
300
|
+
"digital_delivered" => true,
|
|
301
|
+
"order_id" => order['id'],
|
|
302
|
+
"customer_email" => input['customer_email'],
|
|
303
|
+
"delivery_method" => "email",
|
|
304
|
+
"sent_at" => Time.now.to_i,
|
|
305
|
+
"delivery_status" => "sent"
|
|
306
|
+
}
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Parallel Processing Methods
|
|
310
|
+
def send_customer_notifications(input)
|
|
311
|
+
order = input['order']
|
|
312
|
+
puts " [send_customer_notifications] Sending notifications: #{order['id']}"
|
|
313
|
+
|
|
314
|
+
{
|
|
315
|
+
"notifications_sent" => true,
|
|
316
|
+
"order_id" => order['id'],
|
|
317
|
+
"email_sent" => true,
|
|
318
|
+
"sms_sent" => true,
|
|
319
|
+
"push_notification_sent" => true,
|
|
320
|
+
"notification_timestamp" => Time.now.to_i
|
|
321
|
+
}
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def update_customer_profile(input)
|
|
325
|
+
order = input['order']
|
|
326
|
+
puts " [update_customer_profile] Updating profile: #{order['customer_id']}"
|
|
327
|
+
|
|
328
|
+
{
|
|
329
|
+
"profile_updated" => true,
|
|
330
|
+
"customer_id" => order['customer_id'],
|
|
331
|
+
"order_count_incremented" => true,
|
|
332
|
+
"loyalty_points_added" => (order['total'].to_i / 10),
|
|
333
|
+
"last_order_date" => Time.now.to_i
|
|
334
|
+
}
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def generate_analytics(input)
|
|
338
|
+
order = input['order']
|
|
339
|
+
puts " [generate_analytics] Generating analytics: #{order['id']}"
|
|
340
|
+
|
|
341
|
+
{
|
|
342
|
+
"analytics_generated" => true,
|
|
343
|
+
"order_id" => order['id'],
|
|
344
|
+
"revenue_tracked" => order['total'],
|
|
345
|
+
"customer_segment" => order['total'] > 100 ? "high_value" : "standard",
|
|
346
|
+
"analytics_timestamp" => Time.now.to_i
|
|
347
|
+
}
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def process_loyalty_points(input)
|
|
351
|
+
order = input['order']
|
|
352
|
+
puts " [process_loyalty_points] Processing loyalty: #{order['customer_id']}"
|
|
353
|
+
|
|
354
|
+
points = (order['total'].to_i / 5).to_i # 1 point per $5
|
|
355
|
+
|
|
356
|
+
{
|
|
357
|
+
"loyalty_processed" => true,
|
|
358
|
+
"customer_id" => order['customer_id'],
|
|
359
|
+
"points_earned" => points,
|
|
360
|
+
"total_points" => rand(100..1000) + points,
|
|
361
|
+
"tier_checked" => true
|
|
362
|
+
}
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# Notifications
|
|
366
|
+
def send_order_confirmation(input)
|
|
367
|
+
order = input['order']
|
|
368
|
+
puts " [send_order_confirmation] Sending confirmation: #{order['id']}"
|
|
369
|
+
|
|
370
|
+
{
|
|
371
|
+
"confirmation_sent" => true,
|
|
372
|
+
"order_id" => order['id'],
|
|
373
|
+
"customer_id" => input['customer'],
|
|
374
|
+
"sent_via" => ["email", "sms"],
|
|
375
|
+
"confirmation_id" => "CONF-#{SecureRandom.hex(6)}",
|
|
376
|
+
"template_used" => "order_confirmation_v2"
|
|
377
|
+
}
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
# Error Handling Methods
|
|
381
|
+
def handle_classification_error(input)
|
|
382
|
+
puts " [handle_classification_error] Recovering from classification error"
|
|
383
|
+
|
|
384
|
+
{
|
|
385
|
+
"classification_recovered" => true,
|
|
386
|
+
"order_id" => input['order']['id'],
|
|
387
|
+
"fallback_strategy" => "standard_processing",
|
|
388
|
+
"recovered_at" => Time.now.to_i,
|
|
389
|
+
"recovery_method" => "fallback_to_standard"
|
|
390
|
+
}
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def handle_payment_failure(input)
|
|
394
|
+
order = input['order']
|
|
395
|
+
puts " [handle_payment_failure] Handling payment failure: #{order['id']}"
|
|
396
|
+
|
|
397
|
+
{
|
|
398
|
+
"payment_failure_handled" => true,
|
|
399
|
+
"order_id" => order['id'],
|
|
400
|
+
"attempt" => input['payment_attempt'],
|
|
401
|
+
"next_action" => "notify_customer",
|
|
402
|
+
"handled_at" => Time.now.to_i,
|
|
403
|
+
"escalation_level" => "customer_support"
|
|
404
|
+
}
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def notify_payment_failure(input)
|
|
408
|
+
order = input['order']
|
|
409
|
+
puts " [notify_payment_failure] Notifying customer: #{order['id']}"
|
|
410
|
+
|
|
411
|
+
{
|
|
412
|
+
"payment_failure_notified" => true,
|
|
413
|
+
"order_id" => order['id'],
|
|
414
|
+
"customer_notified" => true,
|
|
415
|
+
"notification_method" => "email",
|
|
416
|
+
"notified_at" => Time.now.to_i,
|
|
417
|
+
"notification_template" => "payment_failed_v1"
|
|
418
|
+
}
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def handle_out_of_stock(input)
|
|
422
|
+
order = input['order']
|
|
423
|
+
puts " [handle_out_of_stock] Handling out of stock: #{order['id']}"
|
|
424
|
+
|
|
425
|
+
{
|
|
426
|
+
"out_of_stock_handled" => true,
|
|
427
|
+
"order_id" => order['id'],
|
|
428
|
+
"action" => "notify_customer_and_restock",
|
|
429
|
+
"handled_at" => Time.now.to_i,
|
|
430
|
+
"restock_eta" => Time.now + 7 * 24 * 60 * 60 # 7 days
|
|
431
|
+
}
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def notify_out_of_stock(input)
|
|
435
|
+
order = input['order']
|
|
436
|
+
puts " [notify_out_of_stock] Notifying out of stock: #{order['id']}"
|
|
437
|
+
|
|
438
|
+
{
|
|
439
|
+
"out_of_stock_notified" => true,
|
|
440
|
+
"order_id" => order['id'],
|
|
441
|
+
"customer_notified" => true,
|
|
442
|
+
"notification_method" => "email",
|
|
443
|
+
"notified_at" => Time.now.to_i,
|
|
444
|
+
"alternative_suggested" => true
|
|
445
|
+
}
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def handle_bulk_unavailable(input)
|
|
449
|
+
order = input['order']
|
|
450
|
+
puts " [handle_bulk_unavailable] Handling bulk unavailable: #{order['id']}"
|
|
451
|
+
|
|
452
|
+
{
|
|
453
|
+
"bulk_unavailable_handled" => true,
|
|
454
|
+
"order_id" => order['id'],
|
|
455
|
+
"action" => "offer_alternative",
|
|
456
|
+
"handled_at" => Time.now.to_i,
|
|
457
|
+
"fallback_to_standard" => true
|
|
458
|
+
}
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Simple executor that routes to ComplexOrderProcessor methods
|
|
463
|
+
class LocalMethodExecutor
|
|
464
|
+
def initialize
|
|
465
|
+
@processor = ComplexOrderProcessor.new
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def call(resource, input, credentials = nil)
|
|
469
|
+
method_name = resource.sub('method:', '')
|
|
470
|
+
|
|
471
|
+
unless @processor.respond_to?(method_name)
|
|
472
|
+
raise "Method not found: #{method_name}"
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
puts " ๐ Executing: #{method_name}"
|
|
476
|
+
@processor.send(method_name, input)
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# Enhanced workflow tester with Parallel states
|
|
481
|
+
class WorkflowTester
|
|
482
|
+
def initialize
|
|
483
|
+
@executor = LocalMethodExecutor.new
|
|
484
|
+
@workflow_file = 'complex_workflow_with_parallel.yaml'
|
|
485
|
+
generate_workflow_file
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def generate_workflow_file
|
|
489
|
+
workflow_yaml = <<~YAML
|
|
490
|
+
Comment: Complex E-commerce Workflow with Parallel Processing
|
|
491
|
+
StartAt: ValidateInput
|
|
492
|
+
States:
|
|
493
|
+
# PASS State - Initial data transformation
|
|
494
|
+
ValidateInput:
|
|
495
|
+
Type: Pass
|
|
496
|
+
Parameters:
|
|
497
|
+
order_id.$: $.order.id
|
|
498
|
+
customer_id.$: $.order.customer_id
|
|
499
|
+
total_amount.$: $.order.total
|
|
500
|
+
item_count.$: $.order.items.length
|
|
501
|
+
timestamp: #{Time.now.to_i}
|
|
502
|
+
workflow_version: "2.1.0"
|
|
503
|
+
ResultPath: $.validation_metadata
|
|
504
|
+
Next: CheckOrderType
|
|
505
|
+
|
|
506
|
+
# TASK State - Order classification
|
|
507
|
+
CheckOrderType:
|
|
508
|
+
Type: Task
|
|
509
|
+
Resource: method:determine_order_type
|
|
510
|
+
ResultPath: $.order_type_result
|
|
511
|
+
Next: RouteOrder
|
|
512
|
+
Retry:
|
|
513
|
+
- ErrorEquals: ["States.ALL"]
|
|
514
|
+
IntervalSeconds: 2
|
|
515
|
+
MaxAttempts: 2
|
|
516
|
+
BackoffRate: 1.5
|
|
517
|
+
|
|
518
|
+
# CHOICE State - Route based on order type
|
|
519
|
+
RouteOrder:
|
|
520
|
+
Type: Choice
|
|
521
|
+
Choices:
|
|
522
|
+
- Variable: $.order_type_result.order_type
|
|
523
|
+
StringEquals: "premium"
|
|
524
|
+
Next: ProcessPremiumOrder
|
|
525
|
+
- Variable: $.order_type_result.order_type
|
|
526
|
+
StringEquals: "bulk"
|
|
527
|
+
Next: CheckBulkInventory
|
|
528
|
+
- Variable: $.order_type_result.order_type
|
|
529
|
+
StringEquals: "international"
|
|
530
|
+
Next: ProcessInternationalOrder
|
|
531
|
+
- Variable: $.order_type_result.order_type
|
|
532
|
+
StringEquals: "digital"
|
|
533
|
+
Next: ProcessDigitalOrder
|
|
534
|
+
Default: ProcessStandardOrder
|
|
535
|
+
|
|
536
|
+
# TASK States for different order types
|
|
537
|
+
ProcessPremiumOrder:
|
|
538
|
+
Type: Task
|
|
539
|
+
Resource: method:process_premium_order
|
|
540
|
+
ResultPath: $.premium_result
|
|
541
|
+
Next: ProcessPayment
|
|
542
|
+
|
|
543
|
+
CheckBulkInventory:
|
|
544
|
+
Type: Task
|
|
545
|
+
Resource: method:check_bulk_inventory
|
|
546
|
+
Parameters:
|
|
547
|
+
order.$: $.order
|
|
548
|
+
required_quantity.$: $.order.quantity
|
|
549
|
+
ResultPath: $.bulk_inventory_result
|
|
550
|
+
Next: VerifyBulkAvailability
|
|
551
|
+
Retry:
|
|
552
|
+
- ErrorEquals: ["States.Timeout"]
|
|
553
|
+
IntervalSeconds: 5
|
|
554
|
+
MaxAttempts: 3
|
|
555
|
+
BackoffRate: 2.0
|
|
556
|
+
|
|
557
|
+
VerifyBulkAvailability:
|
|
558
|
+
Type: Choice
|
|
559
|
+
Choices:
|
|
560
|
+
- Variable: $.bulk_inventory_result.available
|
|
561
|
+
BooleanEquals: true
|
|
562
|
+
Next: ProcessBulkOrder
|
|
563
|
+
- Variable: $.bulk_inventory_result.available
|
|
564
|
+
BooleanEquals: false
|
|
565
|
+
Next: HandleBulkUnavailable
|
|
566
|
+
Default: HandleBulkUnavailable
|
|
567
|
+
|
|
568
|
+
ProcessBulkOrder:
|
|
569
|
+
Type: Task
|
|
570
|
+
Resource: method:process_bulk_order
|
|
571
|
+
ResultPath: $.bulk_result
|
|
572
|
+
Next: ProcessPayment
|
|
573
|
+
|
|
574
|
+
HandleBulkUnavailable:
|
|
575
|
+
Type: Task
|
|
576
|
+
Resource: method:handle_bulk_unavailable
|
|
577
|
+
ResultPath: $.bulk_unavailable_result
|
|
578
|
+
Next: ProcessStandardOrder
|
|
579
|
+
|
|
580
|
+
ProcessInternationalOrder:
|
|
581
|
+
Type: Task
|
|
582
|
+
Resource: method:process_international_order
|
|
583
|
+
ResultPath: $.international_result
|
|
584
|
+
Next: ProcessPayment
|
|
585
|
+
|
|
586
|
+
ProcessDigitalOrder:
|
|
587
|
+
Type: Task
|
|
588
|
+
Resource: method:process_digital_order
|
|
589
|
+
ResultPath: $.digital_result
|
|
590
|
+
Next: GenerateDigitalAccess
|
|
591
|
+
|
|
592
|
+
GenerateDigitalAccess:
|
|
593
|
+
Type: Task
|
|
594
|
+
Resource: method:generate_digital_access
|
|
595
|
+
ResultPath: $.digital_access_result
|
|
596
|
+
Next: SendDigitalDelivery
|
|
597
|
+
|
|
598
|
+
ProcessStandardOrder:
|
|
599
|
+
Type: Task
|
|
600
|
+
Resource: method:process_standard_order
|
|
601
|
+
ResultPath: $.standard_result
|
|
602
|
+
Next: ProcessPayment
|
|
603
|
+
|
|
604
|
+
# TASK State - Payment processing with retry
|
|
605
|
+
ProcessPayment:
|
|
606
|
+
Type: Task
|
|
607
|
+
Resource: method:process_payment
|
|
608
|
+
Parameters:
|
|
609
|
+
order_id.$: $.order.id
|
|
610
|
+
amount.$: $.order.total
|
|
611
|
+
currency: "USD"
|
|
612
|
+
payment_method.$: $.order.payment_method
|
|
613
|
+
customer_id.$: $.order.customer_id
|
|
614
|
+
ResultPath: $.payment_result
|
|
615
|
+
Next: VerifyPaymentSuccess
|
|
616
|
+
Retry:
|
|
617
|
+
- ErrorEquals: ["PaymentGatewayTimeout", "States.Timeout"]
|
|
618
|
+
IntervalSeconds: 3
|
|
619
|
+
MaxAttempts: 3
|
|
620
|
+
BackoffRate: 2.0
|
|
621
|
+
Catch:
|
|
622
|
+
- ErrorEquals: ["States.ALL"]
|
|
623
|
+
Next: HandlePaymentFailure
|
|
624
|
+
ResultPath: $.payment_error
|
|
625
|
+
|
|
626
|
+
# CHOICE State - Verify payment outcome
|
|
627
|
+
VerifyPaymentSuccess:
|
|
628
|
+
Type: Choice
|
|
629
|
+
Choices:
|
|
630
|
+
- Variable: $.payment_result.status
|
|
631
|
+
StringEquals: "completed"
|
|
632
|
+
Next: ParallelPostPayment
|
|
633
|
+
- Variable: $.payment_result.status
|
|
634
|
+
StringEquals: "pending"
|
|
635
|
+
Next: WaitForPaymentConfirmation
|
|
636
|
+
Default: HandlePaymentFailure
|
|
637
|
+
|
|
638
|
+
WaitForPaymentConfirmation:
|
|
639
|
+
Type: Task
|
|
640
|
+
Resource: method:wait_for_payment_confirmation
|
|
641
|
+
ResultPath: $.payment_confirmation
|
|
642
|
+
Next: FinalizePayment
|
|
643
|
+
|
|
644
|
+
FinalizePayment:
|
|
645
|
+
Type: Task
|
|
646
|
+
Resource: method:finalize_payment
|
|
647
|
+
ResultPath: $.final_payment_result
|
|
648
|
+
Next: ParallelPostPayment
|
|
649
|
+
|
|
650
|
+
HandlePaymentFailure:
|
|
651
|
+
Type: Task
|
|
652
|
+
Resource: method:handle_payment_failure
|
|
653
|
+
Parameters:
|
|
654
|
+
order.$: $.order
|
|
655
|
+
error.$: $.payment_error
|
|
656
|
+
payment_attempt: 1
|
|
657
|
+
ResultPath: $.payment_failure_result
|
|
658
|
+
Next: NotifyPaymentFailure
|
|
659
|
+
|
|
660
|
+
NotifyPaymentFailure:
|
|
661
|
+
Type: Task
|
|
662
|
+
Resource: method:notify_payment_failure
|
|
663
|
+
ResultPath: $.payment_failure_notification
|
|
664
|
+
End: true
|
|
665
|
+
|
|
666
|
+
# PARALLEL State - Execute multiple tasks concurrently
|
|
667
|
+
ParallelPostPayment:
|
|
668
|
+
Type: Parallel
|
|
669
|
+
Branches:
|
|
670
|
+
- StartAt: UpdateInventoryBranch
|
|
671
|
+
States:
|
|
672
|
+
UpdateInventoryBranch:
|
|
673
|
+
Type: Task
|
|
674
|
+
Resource: method:update_inventory
|
|
675
|
+
Parameters:
|
|
676
|
+
order_id.$: $.order.id
|
|
677
|
+
items.$: $.order.items
|
|
678
|
+
action: "decrement"
|
|
679
|
+
End: true
|
|
680
|
+
ResultPath: $.inventory_result
|
|
681
|
+
Catch:
|
|
682
|
+
- ErrorEquals: ["OutOfStock"]
|
|
683
|
+
Next: HandleOutOfStockBranch
|
|
684
|
+
ResultPath: $.inventory_error
|
|
685
|
+
|
|
686
|
+
HandleOutOfStockBranch:
|
|
687
|
+
Type: Task
|
|
688
|
+
Resource: method:handle_out_of_stock
|
|
689
|
+
ResultPath: $.out_of_stock_result
|
|
690
|
+
End: true
|
|
691
|
+
|
|
692
|
+
- StartAt: CustomerNotificationsBranch
|
|
693
|
+
States:
|
|
694
|
+
CustomerNotificationsBranch:
|
|
695
|
+
Type: Task
|
|
696
|
+
Resource: method:send_customer_notifications
|
|
697
|
+
Parameters:
|
|
698
|
+
order.$: $.order
|
|
699
|
+
End: true
|
|
700
|
+
ResultPath: $.notification_result
|
|
701
|
+
|
|
702
|
+
- StartAt: AnalyticsBranch
|
|
703
|
+
States:
|
|
704
|
+
AnalyticsBranch:
|
|
705
|
+
Type: Task
|
|
706
|
+
Resource: method:generate_analytics
|
|
707
|
+
Parameters:
|
|
708
|
+
order.$: $.order
|
|
709
|
+
End: true
|
|
710
|
+
ResultPath: $.analytics_result
|
|
711
|
+
|
|
712
|
+
- StartAt: LoyaltyBranch
|
|
713
|
+
States:
|
|
714
|
+
LoyaltyBranch:
|
|
715
|
+
Type: Task
|
|
716
|
+
Resource: method:process_loyalty_points
|
|
717
|
+
Parameters:
|
|
718
|
+
order.$: $.order
|
|
719
|
+
End: true
|
|
720
|
+
ResultPath: $.loyalty_result
|
|
721
|
+
|
|
722
|
+
Next: HandleShipping
|
|
723
|
+
Catch:
|
|
724
|
+
- ErrorEquals: ["States.ALL"]
|
|
725
|
+
Next: HandleParallelError
|
|
726
|
+
ResultPath: $.parallel_error
|
|
727
|
+
|
|
728
|
+
HandleParallelError:
|
|
729
|
+
Type: Pass
|
|
730
|
+
Parameters:
|
|
731
|
+
parallel_error_handled: true
|
|
732
|
+
timestamp: #{Time.now.to_i}
|
|
733
|
+
ResultPath: $.parallel_recovery
|
|
734
|
+
Next: HandleShipping
|
|
735
|
+
|
|
736
|
+
# CHOICE State - Shipping decisions
|
|
737
|
+
HandleShipping:
|
|
738
|
+
Type: Choice
|
|
739
|
+
Choices:
|
|
740
|
+
- Variable: $.order.shipping.required
|
|
741
|
+
BooleanEquals: false
|
|
742
|
+
Next: SendDigitalDelivery
|
|
743
|
+
- Variable: $.order_type_result.order_type
|
|
744
|
+
StringEquals: "premium"
|
|
745
|
+
Next: ScheduleExpressShipping
|
|
746
|
+
- Variable: $.order.total
|
|
747
|
+
NumericGreaterThan: 100
|
|
748
|
+
Next: ScheduleStandardShipping
|
|
749
|
+
Default: ScheduleEconomyShipping
|
|
750
|
+
|
|
751
|
+
# TASK States - Shipping methods
|
|
752
|
+
ScheduleExpressShipping:
|
|
753
|
+
Type: Task
|
|
754
|
+
Resource: method:schedule_express_shipping
|
|
755
|
+
ResultPath: $.shipping_result
|
|
756
|
+
Next: FinalConfirmation
|
|
757
|
+
|
|
758
|
+
ScheduleStandardShipping:
|
|
759
|
+
Type: Task
|
|
760
|
+
Resource: method:schedule_standard_shipping
|
|
761
|
+
ResultPath: $.shipping_result
|
|
762
|
+
Next: FinalConfirmation
|
|
763
|
+
|
|
764
|
+
ScheduleEconomyShipping:
|
|
765
|
+
Type: Task
|
|
766
|
+
Resource: method:schedule_economy_shipping
|
|
767
|
+
ResultPath: $.shipping_result
|
|
768
|
+
Next: FinalConfirmation
|
|
769
|
+
|
|
770
|
+
SendDigitalDelivery:
|
|
771
|
+
Type: Task
|
|
772
|
+
Resource: method:send_digital_delivery
|
|
773
|
+
ResultPath: $.digital_delivery_result
|
|
774
|
+
Next: FinalConfirmation
|
|
775
|
+
|
|
776
|
+
# Final confirmation with potential FAIL state
|
|
777
|
+
FinalConfirmation:
|
|
778
|
+
Type: Task
|
|
779
|
+
Resource: method:send_order_confirmation
|
|
780
|
+
ResultPath: $.confirmation_result
|
|
781
|
+
Next: OrderSuccessCheck
|
|
782
|
+
|
|
783
|
+
# CHOICE State - Final success/fail check
|
|
784
|
+
OrderSuccessCheck:
|
|
785
|
+
Type: Choice
|
|
786
|
+
Choices:
|
|
787
|
+
- Variable: $.confirmation_result.confirmation_sent
|
|
788
|
+
BooleanEquals: true
|
|
789
|
+
Next: OrderCompleted
|
|
790
|
+
Default: OrderFailed
|
|
791
|
+
|
|
792
|
+
# SUCCEED State - Successful completion
|
|
793
|
+
OrderCompleted:
|
|
794
|
+
Type: Succeed
|
|
795
|
+
|
|
796
|
+
# FAIL State - Final failure
|
|
797
|
+
OrderFailed:
|
|
798
|
+
Type: Fail
|
|
799
|
+
Cause: "Order confirmation failed to send"
|
|
800
|
+
Error: "ConfirmationError"
|
|
801
|
+
YAML
|
|
802
|
+
|
|
803
|
+
File.write(@workflow_file, workflow_yaml)
|
|
804
|
+
puts "๐ Generated enhanced workflow file: #{@workflow_file}"
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
def run_test_cases
|
|
808
|
+
test_cases = [
|
|
809
|
+
{
|
|
810
|
+
name: "๐ Premium Order with Parallel Processing",
|
|
811
|
+
input: {
|
|
812
|
+
"order" => {
|
|
813
|
+
"id" => "ORD-PREM-001",
|
|
814
|
+
"total" => 750.00,
|
|
815
|
+
"customer_id" => "CUST-PREM-001",
|
|
816
|
+
"items" => ["premium_item_1", "premium_item_2"],
|
|
817
|
+
"quantity" => 2,
|
|
818
|
+
"premium_customer" => true,
|
|
819
|
+
"payment_method" => "credit_card",
|
|
820
|
+
"shipping" => { "required" => true, "country" => "US" }
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
name: "๐ฆ Bulk Order (Testing Retry)",
|
|
826
|
+
input: {
|
|
827
|
+
"order" => {
|
|
828
|
+
"id" => "ORD-BULK-001",
|
|
829
|
+
"total" => 1800.00, # Will trigger payment retry
|
|
830
|
+
"customer_id" => "CUST-BULK-001",
|
|
831
|
+
"items" => ["bulk_item_1"],
|
|
832
|
+
"quantity" => 25,
|
|
833
|
+
"payment_method" => "credit_card",
|
|
834
|
+
"shipping" => { "required" => true, "country" => "US" }
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
name: "๐ป Digital Order (No Shipping)",
|
|
840
|
+
input: {
|
|
841
|
+
"order" => {
|
|
842
|
+
"id" => "ORD-DIG-001",
|
|
843
|
+
"total" => 49.99,
|
|
844
|
+
"customer_id" => "CUST-DIG-001",
|
|
845
|
+
"items" => ["ebook"],
|
|
846
|
+
"quantity" => 1,
|
|
847
|
+
"payment_method" => "paypal",
|
|
848
|
+
"shipping" => { "required" => false },
|
|
849
|
+
"digital_product" => { "type" => "ebook" },
|
|
850
|
+
"customer_email" => "customer@example.com"
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
name: "๐ International Order",
|
|
856
|
+
input: {
|
|
857
|
+
"order" => {
|
|
858
|
+
"id" => "ORD-INT-001",
|
|
859
|
+
"total" => 299.99,
|
|
860
|
+
"customer_id" => "CUST-INT-001",
|
|
861
|
+
"items" => ["international_item"],
|
|
862
|
+
"quantity" => 1,
|
|
863
|
+
"payment_method" => "paypal",
|
|
864
|
+
"shipping" => { "required" => true, "country" => "UK" }
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
name: "๐ธ Payment Failure Scenario",
|
|
870
|
+
input: {
|
|
871
|
+
"order" => {
|
|
872
|
+
"id" => "ORD-FAIL-001",
|
|
873
|
+
"total" => 2500.00, # High amount that will fail payment
|
|
874
|
+
"customer_id" => "CUST-FAIL-001",
|
|
875
|
+
"items" => ["expensive_item"],
|
|
876
|
+
"quantity" => 1,
|
|
877
|
+
"payment_method" => "expired_card", # This will cause failure
|
|
878
|
+
"shipping" => { "required" => true, "country" => "US" }
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
]
|
|
883
|
+
|
|
884
|
+
test_cases.each do |test_case|
|
|
885
|
+
puts "\n" + "="*70
|
|
886
|
+
puts "๐งช #{test_case[:name]}"
|
|
887
|
+
puts "="*70
|
|
888
|
+
|
|
889
|
+
start_time = Time.now
|
|
890
|
+
|
|
891
|
+
begin
|
|
892
|
+
# Load the state machine from YAML
|
|
893
|
+
state_machine = StatesLanguageMachine.from_yaml_file(@workflow_file)
|
|
894
|
+
|
|
895
|
+
# Start execution
|
|
896
|
+
execution = state_machine.start_execution(test_case[:input], "test-#{test_case[:input]['order']['id']}")
|
|
897
|
+
|
|
898
|
+
# Set the executor in context
|
|
899
|
+
execution.context[:task_executor] = @executor
|
|
900
|
+
|
|
901
|
+
# Run the workflow
|
|
902
|
+
execution.run_all
|
|
903
|
+
|
|
904
|
+
execution_time = Time.now - start_time
|
|
905
|
+
|
|
906
|
+
# Display results
|
|
907
|
+
puts "โ
Workflow completed successfully!"
|
|
908
|
+
puts "๐ Final Status: #{execution.status}"
|
|
909
|
+
puts "๐ฃ๏ธ Execution Path: #{execution.history.map { |h| h[:state_name] }.join(' โ ')}"
|
|
910
|
+
puts "โฑ๏ธ Execution Time: #{execution_time.round(4)} seconds"
|
|
911
|
+
puts "๐ States Visited: #{execution.history.size}"
|
|
912
|
+
|
|
913
|
+
if execution.output
|
|
914
|
+
puts "๐ฆ Final Output Summary:"
|
|
915
|
+
execution.output.each do |key, value|
|
|
916
|
+
if value.is_a?(Hash)
|
|
917
|
+
puts " - #{key}: #{value.keys.join(', ')}"
|
|
918
|
+
else
|
|
919
|
+
puts " - #{key}: #{value}"
|
|
920
|
+
end
|
|
921
|
+
end
|
|
922
|
+
|
|
923
|
+
# Show parallel results specifically
|
|
924
|
+
if execution.output['inventory_result'] || execution.output['notification_result']
|
|
925
|
+
puts "๐ Parallel Branch Results:"
|
|
926
|
+
%w[inventory_result notification_result analytics_result loyalty_result].each do |branch|
|
|
927
|
+
if execution.output[branch]
|
|
928
|
+
status = execution.output[branch].keys.first rescue 'unknown'
|
|
929
|
+
puts " - #{branch}: #{status}"
|
|
930
|
+
end
|
|
931
|
+
end
|
|
932
|
+
end
|
|
933
|
+
end
|
|
934
|
+
|
|
935
|
+
rescue => e
|
|
936
|
+
execution_time = Time.now - start_time
|
|
937
|
+
puts "โ Workflow failed!"
|
|
938
|
+
puts "๐ฅ Error: #{e.message}"
|
|
939
|
+
if execution
|
|
940
|
+
puts "๐ Final Status: #{execution.status}"
|
|
941
|
+
puts "๐ Last State: #{execution.history.last[:state_name]}" if execution.history.any?
|
|
942
|
+
puts "๐ History: #{execution.history.map { |h| h[:state_name] }.join(' โ ')}"
|
|
943
|
+
end
|
|
944
|
+
puts "โฑ๏ธ Execution Time: #{execution_time.round(4)} seconds"
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
puts "\n"
|
|
948
|
+
end
|
|
949
|
+
end
|
|
950
|
+
end
|
|
951
|
+
|
|
952
|
+
# Main execution
|
|
953
|
+
if __FILE__ == $0
|
|
954
|
+
begin
|
|
955
|
+
require 'ruby_slm'
|
|
956
|
+
|
|
957
|
+
puts "๐ Starting Enhanced Complex Workflow Test"
|
|
958
|
+
puts "Testing ALL state types: Pass, Task, Choice, Parallel, Succeed, Fail"
|
|
959
|
+
puts "With retry mechanisms, error handling, and parallel processing"
|
|
960
|
+
puts ""
|
|
961
|
+
|
|
962
|
+
tester = WorkflowTester.new
|
|
963
|
+
tester.run_test_cases
|
|
964
|
+
|
|
965
|
+
puts "๐ All tests completed!"
|
|
966
|
+
puts ""
|
|
967
|
+
puts "๐ State Types Demonstrated:"
|
|
968
|
+
puts " โ
Pass - Data transformation"
|
|
969
|
+
puts " โ
Task - Business logic execution"
|
|
970
|
+
puts " โ
Choice - Conditional routing"
|
|
971
|
+
puts " โ
Parallel - Concurrent branch execution"
|
|
972
|
+
puts " โ
Succeed - Successful termination"
|
|
973
|
+
puts " โ
Fail - Error termination"
|
|
974
|
+
puts " โ
Retry - Automatic retry mechanisms"
|
|
975
|
+
puts " โ
Catch - Error handling blocks"
|
|
976
|
+
|
|
977
|
+
rescue LoadError => e
|
|
978
|
+
puts "โ Error: Could not load ruby_slm gem"
|
|
979
|
+
puts "Make sure the gem is installed and in your load path"
|
|
980
|
+
puts "Details: #{e.message}"
|
|
981
|
+
end
|
|
982
|
+
end# frozen_string_literal: true
|
|
983
|
+
|