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
data/README.md
ADDED
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
# Ruby State Language Machine (SLM)
|
|
2
|
+
|
|
3
|
+
A powerful, flexible state machine implementation for Ruby that's compatible with AWS Step Functions state language. Define complex workflows using YAML/JSON and execute them with local Ruby methods or external resources.
|
|
4
|
+
|
|
5
|
+
[](https://ruby-lang.org)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- ๐ **AWS Step Functions Compatible** - Use the same state language as AWS Step Functions
|
|
11
|
+
- ๐ https://states-language.net/
|
|
12
|
+
- ๐ง **Local Method Execution** - Execute Ruby methods directly from state tasks
|
|
13
|
+
- ๐ **YAML/JSON Support** - Define workflows in human-readable formats
|
|
14
|
+
- ๐ก๏ธ **Error Handling** - Built-in retry and catch mechanisms
|
|
15
|
+
- โฑ๏ธ **Timeout & Heartbeat** - Control task execution timing
|
|
16
|
+
- ๐ **All State Types** - Support for Pass, Task, Choice, Parallel, Wait, Succeed, Fail
|
|
17
|
+
- ๐งช **Test-Friendly** - Easy to mock and test workflows
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Add this line to your application's Gemfile:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
gem 'ruby_slm'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
And then execute:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
$ bundle install
|
|
31
|
+
```
|
|
32
|
+
Quick Start
|
|
33
|
+
|
|
34
|
+
1. Define Your Workflow
|
|
35
|
+
Create a YAML file (order_workflow.yaml):
|
|
36
|
+
```yaml
|
|
37
|
+
Comment: Order Processing Workflow
|
|
38
|
+
StartAt: ValidateOrder
|
|
39
|
+
States:
|
|
40
|
+
ValidateOrder:
|
|
41
|
+
Type: Task
|
|
42
|
+
Resource: method:validate_order
|
|
43
|
+
Next: ProcessPayment
|
|
44
|
+
ResultPath: $.validation_result
|
|
45
|
+
|
|
46
|
+
ProcessPayment:
|
|
47
|
+
Type: Task
|
|
48
|
+
Resource: method:process_payment
|
|
49
|
+
Next: UpdateInventory
|
|
50
|
+
ResultPath: $.payment_result
|
|
51
|
+
Catch:
|
|
52
|
+
- ErrorEquals: ["States.ALL"]
|
|
53
|
+
Next: HandlePaymentFailure
|
|
54
|
+
ResultPath: $.error_info
|
|
55
|
+
|
|
56
|
+
HandlePaymentFailure:
|
|
57
|
+
Type: Task
|
|
58
|
+
Resource: method:handle_payment_failure
|
|
59
|
+
End: true
|
|
60
|
+
|
|
61
|
+
UpdateInventory:
|
|
62
|
+
Type: Task
|
|
63
|
+
Resource: method:update_inventory
|
|
64
|
+
Next: SendConfirmation
|
|
65
|
+
ResultPath: $.inventory_result
|
|
66
|
+
|
|
67
|
+
SendConfirmation:
|
|
68
|
+
Type: Task
|
|
69
|
+
Resource: method:send_confirmation
|
|
70
|
+
End: true
|
|
71
|
+
ResultPath: $.confirmation_result
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
2. Implement Your Business Logic
|
|
75
|
+
```ruby
|
|
76
|
+
class OrderProcessor
|
|
77
|
+
def validate_order(input)
|
|
78
|
+
order = input['order']
|
|
79
|
+
{
|
|
80
|
+
"valid" => true,
|
|
81
|
+
"order_id" => order['id'],
|
|
82
|
+
"customer_id" => order['customer_id']
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def process_payment(input)
|
|
87
|
+
{
|
|
88
|
+
"payment_status" => "completed",
|
|
89
|
+
"payment_id" => "pay_#{SecureRandom.hex(8)}",
|
|
90
|
+
"amount_charged" => input['order']['total']
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def update_inventory(input)
|
|
95
|
+
{
|
|
96
|
+
"inventory_updated" => true,
|
|
97
|
+
"order_id" => input['order']['id']
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def send_confirmation(input)
|
|
102
|
+
{
|
|
103
|
+
"confirmation_sent" => true,
|
|
104
|
+
"order_id" => input['order']['id']
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def handle_payment_failure(input)
|
|
109
|
+
{
|
|
110
|
+
"payment_failure_handled" => true,
|
|
111
|
+
"order_id" => input['order']['id']
|
|
112
|
+
}
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
3. Execute the Workflow
|
|
118
|
+
```ruby
|
|
119
|
+
require 'ruby_slm'
|
|
120
|
+
|
|
121
|
+
# Create your business logic processor
|
|
122
|
+
processor = OrderProcessor.new
|
|
123
|
+
|
|
124
|
+
# Create an executor that routes to local methods
|
|
125
|
+
executor = ->(resource, input, credentials) do
|
|
126
|
+
method_name = resource.sub('method:', '')
|
|
127
|
+
processor.send(method_name, input)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Load and execute the workflow
|
|
131
|
+
state_machine = StatesLanguageMachine.from_yaml_file('order_workflow.yaml')
|
|
132
|
+
|
|
133
|
+
input = {
|
|
134
|
+
"order" => {
|
|
135
|
+
"id" => "ORD-123",
|
|
136
|
+
"total" => 99.99,
|
|
137
|
+
"customer_id" => "CUST-456"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
execution = state_machine.start_execution(input, "my-order-execution")
|
|
142
|
+
execution.context[:task_executor] = executor
|
|
143
|
+
execution.run_all
|
|
144
|
+
|
|
145
|
+
puts "Status: #{execution.status}" # => "succeeded"
|
|
146
|
+
puts "Output: #{execution.output}"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## State Types
|
|
150
|
+
### Task State
|
|
151
|
+
#### Execute work using local methods or external resources.
|
|
152
|
+
|
|
153
|
+
```yaml
|
|
154
|
+
ProcessData:
|
|
155
|
+
Type: Task
|
|
156
|
+
Resource: method:process_data
|
|
157
|
+
Next: NextState
|
|
158
|
+
TimeoutSeconds: 300
|
|
159
|
+
Retry:
|
|
160
|
+
- ErrorEquals: ["States.Timeout"]
|
|
161
|
+
IntervalSeconds: 5
|
|
162
|
+
MaxAttempts: 3
|
|
163
|
+
Catch:
|
|
164
|
+
- ErrorEquals: ["States.ALL"]
|
|
165
|
+
Next: HandleError
|
|
166
|
+
ResultPath: $.processing_result
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Pass State
|
|
170
|
+
#### Transform data without performing work.
|
|
171
|
+
```yaml
|
|
172
|
+
TransformData:
|
|
173
|
+
Type: Pass
|
|
174
|
+
Parameters:
|
|
175
|
+
original_id.$: $.order.id
|
|
176
|
+
processed_at: 1698765432
|
|
177
|
+
status: "processing"
|
|
178
|
+
ResultPath: $.metadata
|
|
179
|
+
Next: NextState
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Choice State
|
|
183
|
+
#### Make decisions based on data.
|
|
184
|
+
```yaml
|
|
185
|
+
CheckInventory:
|
|
186
|
+
Type: Choice
|
|
187
|
+
Choices:
|
|
188
|
+
- Variable: $.inventory.available
|
|
189
|
+
BooleanEquals: true
|
|
190
|
+
Next: ProcessOrder
|
|
191
|
+
- Variable: $.order.priority
|
|
192
|
+
StringEquals: "high"
|
|
193
|
+
Next: ExpediteOrder
|
|
194
|
+
Default: WaitForStock
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Parallel State
|
|
198
|
+
#### Execute multiple branches concurrently.
|
|
199
|
+
```yaml
|
|
200
|
+
ProcessInParallel:
|
|
201
|
+
Type: Parallel
|
|
202
|
+
Branches:
|
|
203
|
+
- StartAt: UpdateInventory
|
|
204
|
+
States: { ... }
|
|
205
|
+
- StartAt: NotifyCustomer
|
|
206
|
+
States: { ... }
|
|
207
|
+
Next: AggregateResults
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Wait State
|
|
211
|
+
#### Wait for a specified time or until a timestamp.
|
|
212
|
+
|
|
213
|
+
```yaml
|
|
214
|
+
WaitForPayment:
|
|
215
|
+
Type: Wait
|
|
216
|
+
Seconds: 300
|
|
217
|
+
Next: CheckPaymentStatus
|
|
218
|
+
`````
|
|
219
|
+
|
|
220
|
+
### Succeed & Fail States
|
|
221
|
+
#### Terminate execution successfully or with error.
|
|
222
|
+
|
|
223
|
+
```yaml
|
|
224
|
+
OrderCompleted:
|
|
225
|
+
Type: Succeed
|
|
226
|
+
|
|
227
|
+
OrderFailed:
|
|
228
|
+
Type: Fail
|
|
229
|
+
Cause: "Payment processing failed"
|
|
230
|
+
Error: "PaymentError"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Advanced Features
|
|
234
|
+
|
|
235
|
+
### Retry Mechanisms
|
|
236
|
+
|
|
237
|
+
```yaml
|
|
238
|
+
CallExternalAPI:
|
|
239
|
+
Type: Task
|
|
240
|
+
Resource: method:call_external_api
|
|
241
|
+
Retry:
|
|
242
|
+
- ErrorEquals: ["States.Timeout", "NetworkError"]
|
|
243
|
+
IntervalSeconds: 1
|
|
244
|
+
MaxAttempts: 5
|
|
245
|
+
BackoffRate: 2.0
|
|
246
|
+
- ErrorEquals: ["RateLimitExceeded"]
|
|
247
|
+
IntervalSeconds: 60
|
|
248
|
+
MaxAttempts: 3
|
|
249
|
+
Next: NextState
|
|
250
|
+
```
|
|
251
|
+
### Input/Output Processing
|
|
252
|
+
|
|
253
|
+
```yaml
|
|
254
|
+
ProcessOrder:
|
|
255
|
+
Type: Task
|
|
256
|
+
Resource: method:process_order
|
|
257
|
+
InputPath: $.order_data
|
|
258
|
+
OutputPath: $.result
|
|
259
|
+
ResultPath: $.processing_result
|
|
260
|
+
ResultSelector:
|
|
261
|
+
success: true
|
|
262
|
+
processed_at: 1698765432
|
|
263
|
+
order_id.$: $.order.id
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Intrinsic Functions
|
|
267
|
+
```yaml
|
|
268
|
+
FormatMessage:
|
|
269
|
+
Type: Pass
|
|
270
|
+
Parameters:
|
|
271
|
+
message: "States.Format('Hello {}, your order {} is ready!', $.customer.name, $.order.id)"
|
|
272
|
+
uuid: "States.UUID()"
|
|
273
|
+
random_number: "States.MathRandom(1, 100)"
|
|
274
|
+
ResultPath: $.formatted_data
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Local Method Execution
|
|
278
|
+
#### Method Routing
|
|
279
|
+
|
|
280
|
+
```ruby
|
|
281
|
+
class BusinessLogic
|
|
282
|
+
def process_data(input)
|
|
283
|
+
# Your business logic here
|
|
284
|
+
{ "processed" => true, "result" => input.transform_values(&:upcase) }
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def validate_input(input)
|
|
288
|
+
# Validation logic
|
|
289
|
+
raise "Invalid input" unless input['required_field']
|
|
290
|
+
{ "valid" => true }
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Create executor
|
|
295
|
+
logic = BusinessLogic.new
|
|
296
|
+
executor = ->(resource, input, credentials) do
|
|
297
|
+
method_name = resource.sub('method:', '')
|
|
298
|
+
logic.send(method_name, input)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Use in execution context
|
|
302
|
+
execution.context[:task_executor] = executor
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
#### Custom Executors
|
|
306
|
+
|
|
307
|
+
```ruby
|
|
308
|
+
class CustomExecutor
|
|
309
|
+
def call(resource, input, credentials)
|
|
310
|
+
case resource
|
|
311
|
+
when /^method:/
|
|
312
|
+
execute_local_method(resource, input)
|
|
313
|
+
when /^arn:aws:lambda:/
|
|
314
|
+
execute_lambda(resource, input, credentials)
|
|
315
|
+
when /^arn:aws:sqs:/
|
|
316
|
+
send_sqs_message(resource, input)
|
|
317
|
+
else
|
|
318
|
+
{ "error" => "Unknown resource type" }
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
private
|
|
323
|
+
|
|
324
|
+
def execute_local_method(resource, input)
|
|
325
|
+
method_name = resource.sub('method:', '')
|
|
326
|
+
# Your method dispatch logic
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
execution.context[:task_executor] = CustomExecutor.new
|
|
331
|
+
```
|
|
332
|
+
### Error Handling
|
|
333
|
+
|
|
334
|
+
#### Catch Blocks
|
|
335
|
+
|
|
336
|
+
```yaml
|
|
337
|
+
ProcessPayment:
|
|
338
|
+
Type: Task
|
|
339
|
+
Resource: method:process_payment
|
|
340
|
+
Catch:
|
|
341
|
+
- ErrorEquals: ["InsufficientFunds", "CardDeclined"]
|
|
342
|
+
Next: HandlePaymentFailure
|
|
343
|
+
ResultPath: $.payment_error
|
|
344
|
+
- ErrorEquals: ["States.Timeout"]
|
|
345
|
+
Next: HandleTimeout
|
|
346
|
+
- ErrorEquals: ["States.ALL"]
|
|
347
|
+
Next: HandleGenericError
|
|
348
|
+
Next: NextState
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Testing
|
|
352
|
+
|
|
353
|
+
### Unit Testing Workflows
|
|
354
|
+
```ruby
|
|
355
|
+
require 'spec_helper'
|
|
356
|
+
|
|
357
|
+
RSpec.describe 'Order Workflow' do
|
|
358
|
+
let(:processor) { OrderProcessor.new }
|
|
359
|
+
let(:executor) { ->(r, i, c) { processor.send(r.sub('method:', ''), i) } }
|
|
360
|
+
|
|
361
|
+
it 'processes orders successfully' do
|
|
362
|
+
state_machine = StatesLanguageMachine.from_yaml_file('workflows/order_processing.yaml')
|
|
363
|
+
execution = state_machine.start_execution(order_input, "test-execution")
|
|
364
|
+
execution.context[:task_executor] = executor
|
|
365
|
+
execution.run_all
|
|
366
|
+
|
|
367
|
+
expect(execution.status).to eq('succeeded')
|
|
368
|
+
expect(execution.output['confirmation_result']['confirmation_sent']).to be true
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
```
|
|
372
|
+
### Testing Error Scenarios
|
|
373
|
+
|
|
374
|
+
```ruby
|
|
375
|
+
it 'handles payment failures gracefully' do
|
|
376
|
+
# Mock payment failure
|
|
377
|
+
allow(processor).to receive(:process_payment).and_raise('Payment declined')
|
|
378
|
+
|
|
379
|
+
execution.run_all
|
|
380
|
+
|
|
381
|
+
expect(execution.status).to eq('succeeded') # Because catch handled it
|
|
382
|
+
expect(execution.output['payment_failure_handled']).to be true
|
|
383
|
+
end
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Configuration
|
|
387
|
+
### Execution Context
|
|
388
|
+
```ruby
|
|
389
|
+
execution.context = {
|
|
390
|
+
task_executor: your_executor,
|
|
391
|
+
method_receiver: your_object, # For method: resources
|
|
392
|
+
logger: Logger.new($stdout), # For execution logging
|
|
393
|
+
metrics: your_metrics_client # For monitoring
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Timeout configuration
|
|
398
|
+
```yaml
|
|
399
|
+
LongRunningTask:
|
|
400
|
+
Type: Task
|
|
401
|
+
Resource: method:long_running_process
|
|
402
|
+
TimeoutSeconds: 3600 # 1 hour timeout
|
|
403
|
+
HeartbeatSeconds: 300 # 5 minute heartbeat
|
|
404
|
+
Next: NextState
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Examples
|
|
408
|
+
### Check the examples/ directory for complete working examples:
|
|
409
|
+
|
|
410
|
+
- exammples/ - Complex order processing workflow
|
|
411
|
+
- examples/test_complex_workflow.rb
|
|
412
|
+
```bash
|
|
413
|
+
๐ Starting Complex Workflow Test
|
|
414
|
+
This tests Pass, Task, Choice, Succeed, and Fail states with local methods
|
|
415
|
+
|
|
416
|
+
๐ Generated workflow file: complex_workflow.yaml
|
|
417
|
+
|
|
418
|
+
============================================================
|
|
419
|
+
๐งช Testing: Premium Order
|
|
420
|
+
============================================================
|
|
421
|
+
๐ Executing: determine_order_type
|
|
422
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "premium", "Next" => "ProcessPremiumOrder"}
|
|
423
|
+
StringEquals
|
|
424
|
+
true
|
|
425
|
+
๐ Executing: process_premium_order
|
|
426
|
+
๐ Executing: process_payment
|
|
427
|
+
๐ Executing: update_inventory
|
|
428
|
+
โ
Workflow completed successfully!
|
|
429
|
+
๐ Final Status: failed
|
|
430
|
+
๐ฃ๏ธ Execution Path: ValidateInput โ CheckOrderType โ RouteOrder โ ProcessPremiumOrder โ ProcessPayment
|
|
431
|
+
โฑ๏ธ Execution Time: 0.0008 seconds
|
|
432
|
+
๐ฆ Output Keys: order, $, order_type_result, premium_result, payment_result
|
|
433
|
+
- order_type_result: order_type,reason
|
|
434
|
+
- premium_result: premium_processed,order_id,vip_handling,dedicated_support,processing_tier
|
|
435
|
+
- payment_result: status,payment_id,amount_charged,currency,processed_at
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
============================================================
|
|
439
|
+
๐งช Testing: Bulk Order (Available)
|
|
440
|
+
============================================================
|
|
441
|
+
๐ Executing: determine_order_type
|
|
442
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "premium", "Next" => "ProcessPremiumOrder"}
|
|
443
|
+
StringEquals
|
|
444
|
+
true
|
|
445
|
+
๐ Executing: process_premium_order
|
|
446
|
+
๐ Executing: process_payment
|
|
447
|
+
๐ Executing: update_inventory
|
|
448
|
+
โ
Workflow completed successfully!
|
|
449
|
+
๐ Final Status: failed
|
|
450
|
+
๐ฃ๏ธ Execution Path: ValidateInput โ CheckOrderType โ RouteOrder โ ProcessPremiumOrder โ ProcessPayment
|
|
451
|
+
โฑ๏ธ Execution Time: 0.0003 seconds
|
|
452
|
+
๐ฆ Output Keys: order, $, order_type_result, premium_result, payment_result
|
|
453
|
+
- order_type_result: order_type,reason
|
|
454
|
+
- premium_result: premium_processed,order_id,vip_handling,dedicated_support,processing_tier
|
|
455
|
+
- payment_result: status,payment_id,amount_charged,currency,processed_at
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
============================================================
|
|
459
|
+
๐งช Testing: Digital Order
|
|
460
|
+
============================================================
|
|
461
|
+
๐ Executing: determine_order_type
|
|
462
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "premium", "Next" => "ProcessPremiumOrder"}
|
|
463
|
+
StringEquals
|
|
464
|
+
false
|
|
465
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "bulk", "Next" => "CheckBulkInventory"}
|
|
466
|
+
StringEquals
|
|
467
|
+
false
|
|
468
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "international", "Next" => "ProcessInternationalOrder"}
|
|
469
|
+
StringEquals
|
|
470
|
+
true
|
|
471
|
+
๐ Executing: process_international_order
|
|
472
|
+
๐ Executing: process_payment
|
|
473
|
+
๐ Executing: update_inventory
|
|
474
|
+
โ
Workflow completed successfully!
|
|
475
|
+
๐ Final Status: failed
|
|
476
|
+
๐ฃ๏ธ Execution Path: ValidateInput โ CheckOrderType โ RouteOrder โ ProcessInternationalOrder โ ProcessPayment
|
|
477
|
+
โฑ๏ธ Execution Time: 0.0003 seconds
|
|
478
|
+
๐ฆ Output Keys: order, $, order_type_result, international_result, payment_result
|
|
479
|
+
- order_type_result: order_type,reason
|
|
480
|
+
- international_result: international_processed,order_id,destination_country,export_documentation,customs_declaration
|
|
481
|
+
- payment_result: status,payment_id,amount_charged,currency,processed_at
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
============================================================
|
|
485
|
+
๐งช Testing: Standard Order
|
|
486
|
+
============================================================
|
|
487
|
+
๐ Executing: determine_order_type
|
|
488
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "premium", "Next" => "ProcessPremiumOrder"}
|
|
489
|
+
StringEquals
|
|
490
|
+
false
|
|
491
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "bulk", "Next" => "CheckBulkInventory"}
|
|
492
|
+
StringEquals
|
|
493
|
+
false
|
|
494
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "international", "Next" => "ProcessInternationalOrder"}
|
|
495
|
+
StringEquals
|
|
496
|
+
false
|
|
497
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "digital", "Next" => "ProcessDigitalOrder"}
|
|
498
|
+
StringEquals
|
|
499
|
+
false
|
|
500
|
+
๐ Executing: process_standard_order
|
|
501
|
+
๐ Executing: process_payment
|
|
502
|
+
๐ Executing: update_inventory
|
|
503
|
+
โ
Workflow completed successfully!
|
|
504
|
+
๐ Final Status: failed
|
|
505
|
+
๐ฃ๏ธ Execution Path: ValidateInput โ CheckOrderType โ RouteOrder โ ProcessStandardOrder โ ProcessPayment
|
|
506
|
+
โฑ๏ธ Execution Time: 0.0003 seconds
|
|
507
|
+
๐ฆ Output Keys: order, $, order_type_result, standard_result, payment_result
|
|
508
|
+
- order_type_result: order_type,reason
|
|
509
|
+
- standard_result: standard_processed,order_id,processing_tier
|
|
510
|
+
- payment_result: status,payment_id,amount_charged,currency,processed_at
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
============================================================
|
|
514
|
+
๐งช Testing: Payment Failure Order
|
|
515
|
+
============================================================
|
|
516
|
+
๐ Executing: determine_order_type
|
|
517
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "premium", "Next" => "ProcessPremiumOrder"}
|
|
518
|
+
StringEquals
|
|
519
|
+
true
|
|
520
|
+
๐ Executing: process_premium_order
|
|
521
|
+
๐ Executing: process_payment
|
|
522
|
+
๐ Executing: update_inventory
|
|
523
|
+
{"Variable" => "$.order.shipping.required", "BooleanEquals" => false, "Next" => "SendDigitalDelivery"}
|
|
524
|
+
BooleanEquals
|
|
525
|
+
false
|
|
526
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "premium", "Next" => "ScheduleExpressShipping"}
|
|
527
|
+
StringEquals
|
|
528
|
+
true
|
|
529
|
+
๐ Executing: schedule_express_shipping
|
|
530
|
+
๐ Executing: send_order_confirmation
|
|
531
|
+
โ
Workflow completed successfully!
|
|
532
|
+
๐ Final Status: succeeded
|
|
533
|
+
๐ฃ๏ธ Execution Path: ValidateInput โ CheckOrderType โ RouteOrder โ ProcessPremiumOrder โ ProcessPayment โ HandleShipping โ ScheduleExpressShipping โ SendOrderConfirmation
|
|
534
|
+
โฑ๏ธ Execution Time: 0.0005 seconds
|
|
535
|
+
๐ฆ Output Keys: order, $, order_type_result, premium_result, payment_result, inventory_error, shipping_result, confirmation_result
|
|
536
|
+
- order_type_result: order_type,reason
|
|
537
|
+
- premium_result: premium_processed,order_id,vip_handling,dedicated_support,processing_tier
|
|
538
|
+
- payment_result: status,payment_id,amount_charged,currency,processed_at
|
|
539
|
+
- shipping_result: shipping_scheduled,order_id,method,estimated_days,tracking_number,priority
|
|
540
|
+
- confirmation_result: confirmation_sent,order_id,customer_id,sent_via,confirmation_id
|
|
541
|
+
|
|
542
|
+
๐ All tests completed!
|
|
543
|
+
```
|
|
544
|
+
- examples/test_parallel_complex_workflow.rb
|
|
545
|
+
```bash
|
|
546
|
+
๐ Starting Enhanced Complex Workflow Test
|
|
547
|
+
Testing ALL state types: Pass, Task, Choice, Parallel, Succeed, Fail
|
|
548
|
+
With retry mechanisms, error handling, and parallel processing
|
|
549
|
+
|
|
550
|
+
๐ Generated enhanced workflow file: complex_workflow_with_parallel.yaml
|
|
551
|
+
|
|
552
|
+
======================================================================
|
|
553
|
+
๐งช ๐ Premium Order with Parallel Processing
|
|
554
|
+
======================================================================
|
|
555
|
+
๐ Executing: determine_order_type
|
|
556
|
+
[determine_order_type] Analyzing order: ORD-PREM-001 - Total: $750.0, Items: 2, Quantity: 2
|
|
557
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "premium", "Next" => "ProcessPremiumOrder"}
|
|
558
|
+
StringEquals
|
|
559
|
+
true
|
|
560
|
+
๐ Executing: process_premium_order
|
|
561
|
+
[process_premium_order] VIP processing for: ORD-PREM-001
|
|
562
|
+
๐ Executing: process_payment
|
|
563
|
+
[process_payment] Processing $0.0 via
|
|
564
|
+
{"Variable" => "$.payment_result.status", "StringEquals" => "completed", "Next" => "ParallelPostPayment"}
|
|
565
|
+
StringEquals
|
|
566
|
+
true
|
|
567
|
+
๐ Executing: update_inventory
|
|
568
|
+
[update_inventory] Updating inventory for:
|
|
569
|
+
๐ Executing: send_customer_notifications
|
|
570
|
+
[send_customer_notifications] Sending notifications: ORD-PREM-001
|
|
571
|
+
๐ Executing: generate_analytics
|
|
572
|
+
[generate_analytics] Generating analytics: ORD-PREM-001
|
|
573
|
+
๐ Executing: process_loyalty_points
|
|
574
|
+
[process_loyalty_points] Processing loyalty: CUST-PREM-001
|
|
575
|
+
โ
Workflow completed successfully!
|
|
576
|
+
๐ Final Status: failed
|
|
577
|
+
๐ฃ๏ธ Execution Path: ValidateInput โ CheckOrderType โ RouteOrder โ ProcessPremiumOrder โ ProcessPayment โ VerifyPaymentSuccess
|
|
578
|
+
โฑ๏ธ Execution Time: 0.0066 seconds
|
|
579
|
+
๐ States Visited: 6
|
|
580
|
+
๐ฆ Final Output Summary:
|
|
581
|
+
- order: id, total, customer_id, items, quantity, premium_customer, payment_method, shipping
|
|
582
|
+
- $: validation_metadata
|
|
583
|
+
- order_type_result: order_type, reason
|
|
584
|
+
- premium_result: premium_processed, order_id, vip_handling, dedicated_support, processing_tier, priority_level
|
|
585
|
+
- payment_result: status, payment_id, amount_charged, currency, processed_at, attempts
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
======================================================================
|
|
589
|
+
๐งช ๐ฆ Bulk Order (Testing Retry)
|
|
590
|
+
======================================================================
|
|
591
|
+
๐ Executing: determine_order_type
|
|
592
|
+
[determine_order_type] Analyzing order: ORD-BULK-001 - Total: $1800.0, Items: 1, Quantity: 25
|
|
593
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "premium", "Next" => "ProcessPremiumOrder"}
|
|
594
|
+
StringEquals
|
|
595
|
+
true
|
|
596
|
+
๐ Executing: process_premium_order
|
|
597
|
+
[process_premium_order] VIP processing for: ORD-BULK-001
|
|
598
|
+
๐ Executing: process_payment
|
|
599
|
+
[process_payment] Processing $0.0 via
|
|
600
|
+
{"Variable" => "$.payment_result.status", "StringEquals" => "completed", "Next" => "ParallelPostPayment"}
|
|
601
|
+
StringEquals
|
|
602
|
+
true
|
|
603
|
+
๐ Executing: update_inventory
|
|
604
|
+
[update_inventory] Updating inventory for:
|
|
605
|
+
๐ Executing: send_customer_notifications
|
|
606
|
+
[send_customer_notifications] Sending notifications: ORD-BULK-001
|
|
607
|
+
๐ Executing: generate_analytics
|
|
608
|
+
[generate_analytics] Generating analytics: ORD-BULK-001
|
|
609
|
+
๐ Executing: process_loyalty_points
|
|
610
|
+
[process_loyalty_points] Processing loyalty: CUST-BULK-001
|
|
611
|
+
โ
Workflow completed successfully!
|
|
612
|
+
๐ Final Status: failed
|
|
613
|
+
๐ฃ๏ธ Execution Path: ValidateInput โ CheckOrderType โ RouteOrder โ ProcessPremiumOrder โ ProcessPayment โ VerifyPaymentSuccess
|
|
614
|
+
โฑ๏ธ Execution Time: 0.0033 seconds
|
|
615
|
+
๐ States Visited: 6
|
|
616
|
+
๐ฆ Final Output Summary:
|
|
617
|
+
- order: id, total, customer_id, items, quantity, payment_method, shipping
|
|
618
|
+
- $: validation_metadata
|
|
619
|
+
- order_type_result: order_type, reason
|
|
620
|
+
- premium_result: premium_processed, order_id, vip_handling, dedicated_support, processing_tier, priority_level
|
|
621
|
+
- payment_result: status, payment_id, amount_charged, currency, processed_at, attempts
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
======================================================================
|
|
625
|
+
๐งช ๐ป Digital Order (No Shipping)
|
|
626
|
+
======================================================================
|
|
627
|
+
๐ Executing: determine_order_type
|
|
628
|
+
[determine_order_type] Analyzing order: ORD-DIG-001 - Total: $49.99, Items: 1, Quantity: 1
|
|
629
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "premium", "Next" => "ProcessPremiumOrder"}
|
|
630
|
+
StringEquals
|
|
631
|
+
false
|
|
632
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "bulk", "Next" => "CheckBulkInventory"}
|
|
633
|
+
StringEquals
|
|
634
|
+
false
|
|
635
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "international", "Next" => "ProcessInternationalOrder"}
|
|
636
|
+
StringEquals
|
|
637
|
+
true
|
|
638
|
+
๐ Executing: process_international_order
|
|
639
|
+
[process_international_order] International processing for: ORD-DIG-001 to
|
|
640
|
+
๐ Executing: process_payment
|
|
641
|
+
[process_payment] Processing $0.0 via
|
|
642
|
+
{"Variable" => "$.payment_result.status", "StringEquals" => "completed", "Next" => "ParallelPostPayment"}
|
|
643
|
+
StringEquals
|
|
644
|
+
true
|
|
645
|
+
๐ Executing: update_inventory
|
|
646
|
+
[update_inventory] Updating inventory for:
|
|
647
|
+
๐ Executing: send_customer_notifications
|
|
648
|
+
[send_customer_notifications] Sending notifications: ORD-DIG-001
|
|
649
|
+
๐ Executing: generate_analytics
|
|
650
|
+
[generate_analytics] Generating analytics: ORD-DIG-001
|
|
651
|
+
๐ Executing: process_loyalty_points
|
|
652
|
+
[process_loyalty_points] Processing loyalty: CUST-DIG-001
|
|
653
|
+
โ
Workflow completed successfully!
|
|
654
|
+
๐ Final Status: failed
|
|
655
|
+
๐ฃ๏ธ Execution Path: ValidateInput โ CheckOrderType โ RouteOrder โ ProcessInternationalOrder โ ProcessPayment โ VerifyPaymentSuccess
|
|
656
|
+
โฑ๏ธ Execution Time: 0.003 seconds
|
|
657
|
+
๐ States Visited: 6
|
|
658
|
+
๐ฆ Final Output Summary:
|
|
659
|
+
- order: id, total, customer_id, items, quantity, payment_method, shipping, digital_product, customer_email
|
|
660
|
+
- $: validation_metadata
|
|
661
|
+
- order_type_result: order_type, reason
|
|
662
|
+
- international_result: international_processed, order_id, destination_country, export_documentation, customs_declaration, requires_export_license
|
|
663
|
+
- payment_result: status, payment_id, amount_charged, currency, processed_at, attempts
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
======================================================================
|
|
667
|
+
๐งช ๐ International Order
|
|
668
|
+
======================================================================
|
|
669
|
+
๐ Executing: determine_order_type
|
|
670
|
+
[determine_order_type] Analyzing order: ORD-INT-001 - Total: $299.99, Items: 1, Quantity: 1
|
|
671
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "premium", "Next" => "ProcessPremiumOrder"}
|
|
672
|
+
StringEquals
|
|
673
|
+
false
|
|
674
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "bulk", "Next" => "CheckBulkInventory"}
|
|
675
|
+
StringEquals
|
|
676
|
+
false
|
|
677
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "international", "Next" => "ProcessInternationalOrder"}
|
|
678
|
+
StringEquals
|
|
679
|
+
true
|
|
680
|
+
๐ Executing: process_international_order
|
|
681
|
+
[process_international_order] International processing for: ORD-INT-001 to
|
|
682
|
+
๐ Executing: process_payment
|
|
683
|
+
[process_payment] Processing $0.0 via
|
|
684
|
+
{"Variable" => "$.payment_result.status", "StringEquals" => "completed", "Next" => "ParallelPostPayment"}
|
|
685
|
+
StringEquals
|
|
686
|
+
true
|
|
687
|
+
๐ Executing: update_inventory
|
|
688
|
+
[update_inventory] Updating inventory for:
|
|
689
|
+
๐ Executing: send_customer_notifications
|
|
690
|
+
[send_customer_notifications] Sending notifications: ORD-INT-001
|
|
691
|
+
๐ Executing: generate_analytics
|
|
692
|
+
[generate_analytics] Generating analytics: ORD-INT-001
|
|
693
|
+
๐ Executing: process_loyalty_points
|
|
694
|
+
[process_loyalty_points] Processing loyalty: CUST-INT-001
|
|
695
|
+
โ
Workflow completed successfully!
|
|
696
|
+
๐ Final Status: failed
|
|
697
|
+
๐ฃ๏ธ Execution Path: ValidateInput โ CheckOrderType โ RouteOrder โ ProcessInternationalOrder โ ProcessPayment โ VerifyPaymentSuccess
|
|
698
|
+
โฑ๏ธ Execution Time: 0.0031 seconds
|
|
699
|
+
๐ States Visited: 6
|
|
700
|
+
๐ฆ Final Output Summary:
|
|
701
|
+
- order: id, total, customer_id, items, quantity, payment_method, shipping
|
|
702
|
+
- $: validation_metadata
|
|
703
|
+
- order_type_result: order_type, reason
|
|
704
|
+
- international_result: international_processed, order_id, destination_country, export_documentation, customs_declaration, requires_export_license
|
|
705
|
+
- payment_result: status, payment_id, amount_charged, currency, processed_at, attempts
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
======================================================================
|
|
709
|
+
๐งช ๐ธ Payment Failure Scenario
|
|
710
|
+
======================================================================
|
|
711
|
+
๐ Executing: determine_order_type
|
|
712
|
+
[determine_order_type] Analyzing order: ORD-FAIL-001 - Total: $2500.0, Items: 1, Quantity: 1
|
|
713
|
+
{"Variable" => "$.order_type_result.order_type", "StringEquals" => "premium", "Next" => "ProcessPremiumOrder"}
|
|
714
|
+
StringEquals
|
|
715
|
+
true
|
|
716
|
+
๐ Executing: process_premium_order
|
|
717
|
+
[process_premium_order] VIP processing for: ORD-FAIL-001
|
|
718
|
+
๐ Executing: process_payment
|
|
719
|
+
[process_payment] Processing $0.0 via
|
|
720
|
+
{"Variable" => "$.payment_result.status", "StringEquals" => "completed", "Next" => "ParallelPostPayment"}
|
|
721
|
+
StringEquals
|
|
722
|
+
true
|
|
723
|
+
๐ Executing: update_inventory
|
|
724
|
+
[update_inventory] Updating inventory for:
|
|
725
|
+
๐ Executing: send_customer_notifications
|
|
726
|
+
[send_customer_notifications] Sending notifications: ORD-FAIL-001
|
|
727
|
+
๐ Executing: generate_analytics
|
|
728
|
+
[generate_analytics] Generating analytics: ORD-FAIL-001
|
|
729
|
+
๐ Executing: process_loyalty_points
|
|
730
|
+
[process_loyalty_points] Processing loyalty: CUST-FAIL-001
|
|
731
|
+
โ
Workflow completed successfully!
|
|
732
|
+
๐ Final Status: failed
|
|
733
|
+
๐ฃ๏ธ Execution Path: ValidateInput โ CheckOrderType โ RouteOrder โ ProcessPremiumOrder โ ProcessPayment โ VerifyPaymentSuccess
|
|
734
|
+
โฑ๏ธ Execution Time: 0.0028 seconds
|
|
735
|
+
๐ States Visited: 6
|
|
736
|
+
๐ฆ Final Output Summary:
|
|
737
|
+
- order: id, total, customer_id, items, quantity, payment_method, shipping
|
|
738
|
+
- $: validation_metadata
|
|
739
|
+
- order_type_result: order_type, reason
|
|
740
|
+
- premium_result: premium_processed, order_id, vip_handling, dedicated_support, processing_tier, priority_level
|
|
741
|
+
- payment_result: status, payment_id, amount_charged, currency, processed_at, attempts
|
|
742
|
+
|
|
743
|
+
๐ All tests completed!
|
|
744
|
+
|
|
745
|
+
๐ State Types Demonstrated:
|
|
746
|
+
โ
Pass - Data transformation
|
|
747
|
+
โ
Task - Business logic execution
|
|
748
|
+
โ
Choice - Conditional routing
|
|
749
|
+
โ
Parallel - Concurrent branch execution
|
|
750
|
+
โ
Succeed - Successful termination
|
|
751
|
+
โ
Fail - Error termination
|
|
752
|
+
โ
Retry - Automatic retry mechanisms
|
|
753
|
+
โ
Catch - Error handling blocks
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
## License
|
|
758
|
+
The gem is available as open source under the terms of the MIT License.
|
|
759
|
+
|
|
760
|
+
## Acknowledgments
|
|
761
|
+
Inspired by AWS Step Functions state language
|
|
762
|
+
|
|
763
|
+
Built for Ruby developers who need workflow orchestration
|
|
764
|
+
|
|
765
|
+
Designed for both simple and complex business processes
|
|
766
|
+
|
|
767
|
+
## Author
|
|
768
|
+
- Hussain Pithawala (https://www.linkedin.com/in/hussainpithawala)
|