simplerubysteps 0.0.7 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +68 -25
- data/exe/simplerubysteps +3 -3
- data/exe/srs +6 -0
- data/lib/function.rb +6 -3
- data/lib/{statemachine.yaml → simplerubysteps/cloudformation.rb} +77 -60
- data/lib/simplerubysteps/dsl.rb +86 -0
- data/lib/simplerubysteps/model.rb +204 -0
- data/lib/simplerubysteps/tool.rb +599 -0
- data/lib/simplerubysteps/version.rb +1 -1
- data/lib/simplerubysteps.rb +2 -273
- data/simplerubysteps.gemspec +2 -4
- metadata +10 -16
- data/.gitignore +0 -9
- data/Gemfile +0 -5
- data/LICENSE.txt +0 -21
- data/bin/console +0 -7
- data/bin/setup +0 -6
- data/lib/tool.rb +0 -448
- data/samples/sample1/sample-task-worker.sh +0 -5
- data/samples/sample1/start-callbackbranch.sh +0 -3
- data/samples/sample1/start-directbranch.sh +0 -3
- data/samples/sample1/workflow.rb +0 -39
- data/samples/sample2/workflow.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32057d9ea30569964077a231e6e5ed88aff396dabc03151c1ef77bbac237a7e2
|
4
|
+
data.tar.gz: 81d13b619c57a3b793ed7039a06e0ec2a730aa8844d70c8603f35277799090e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68f92e9f3b4e7d0e5249e7df03f97a9e133ab51274117b2538b8672bf8e6221c6c8d2130679395ff0e1e57ee18f6e0bf4c321fe8ce034e0fe14c53ca37150e65
|
7
|
+
data.tar.gz: 1185695c5f0bc3c3d093cb7984c2947ec1ef2f4be737586f73ae058d2266ba90121d9d48b37608993877784eee729010867c15754309584beafe88c01d6b5836
|
data/README.md
CHANGED
@@ -1,55 +1,96 @@
|
|
1
1
|
# Simplerubysteps
|
2
2
|
|
3
|
-
Simplerubysteps
|
3
|
+
Simplerubysteps makes it easy to manage AWS Step Functions with ruby.
|
4
4
|
|
5
|
-
|
5
|
+
* Phase I (we are here): Experimenting and exploring the problem and solution domain. The aim is to explore the DSL capabilities and user experience of the automation tool. Things will work, but may change over time. The released gem versions of Simplerubysteps are early alpha versions and therefore should not be used by anyone in production.
|
6
|
+
* Phase II: First release candidate (possibly rewritten from scratch)
|
7
|
+
* Phase III: Maintain and evolve
|
6
8
|
|
7
|
-
## Installation
|
9
|
+
## Installation and Usage
|
8
10
|
|
9
|
-
Prerequisites
|
10
|
-
* AWS CLI installed
|
11
|
+
### Prerequisites
|
11
12
|
|
12
|
-
|
13
|
+
* AWS CLI installed (mainly for debugging privileges)
|
14
|
+
* Configured AWS CLI profile with sufficient permissions to create IAM roles and policies, create Lambda functions, create and run Step Functions state machines, run CloudWatch log queries, etc.
|
13
15
|
|
14
|
-
### Install the gem and the
|
16
|
+
### Install the gem and the srs CLI
|
15
17
|
|
16
18
|
```
|
17
19
|
gem install simplerubysteps
|
18
20
|
```
|
19
21
|
|
20
|
-
### Create AWS Step
|
22
|
+
### Create an AWS Step Function State Machine with the simplerubysteps Ruby DSL
|
21
23
|
|
22
24
|
```
|
23
|
-
|
25
|
+
mkdir -p samples/hello-world-2
|
26
|
+
cd samples/hello-world-2
|
27
|
+
|
24
28
|
vi workflow.rb
|
25
29
|
```
|
26
30
|
|
27
|
-
|
31
|
+
#### Hello World State Machine (workflow.rb)
|
28
32
|
|
29
33
|
```
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
require "simplerubysteps"
|
35
|
+
include Simplerubysteps
|
36
|
+
kind "EXPRESS"
|
37
|
+
|
38
|
+
GERMAN_WORDS = ["Hallo"]
|
39
|
+
|
40
|
+
def is_german?(word)
|
41
|
+
GERMAN_WORDS.include? word
|
42
|
+
end
|
43
|
+
|
44
|
+
task :start do
|
45
|
+
transition_to :german do |data|
|
46
|
+
is_german? data["hello"]
|
47
|
+
end
|
48
|
+
|
49
|
+
default_transition_to :english
|
50
|
+
end
|
51
|
+
|
52
|
+
task :german do
|
53
|
+
action do |data|
|
54
|
+
{ hello_world: "#{data["hello"]} Welt!" }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
task :english do
|
59
|
+
action do |data|
|
60
|
+
{ hello_world: "#{data["hello"]} World!" }
|
61
|
+
end
|
62
|
+
end
|
33
63
|
```
|
34
64
|
|
35
|
-
###
|
65
|
+
### Deploy the Step Functions State Machine and the Lambda function that implements the Task Actions (with srs deploy)
|
66
|
+
|
67
|
+
```
|
68
|
+
export AWS_PROFILE=<AWS CLI profile name with sufficient privileges>
|
69
|
+
cd samples/hello-world-2
|
36
70
|
|
71
|
+
srs deploy
|
37
72
|
```
|
38
|
-
export AWS_PROFILE=...
|
39
|
-
cd samples/sample1
|
40
73
|
|
41
|
-
|
74
|
+
### Trigger State Machine executions (with srs start)
|
42
75
|
|
43
|
-
./sample-task-worker.sh &
|
44
|
-
./start-callbackbranch.sh
|
45
76
|
```
|
77
|
+
export AWS_PROFILE=<AWS CLI profile name with sufficient privileges>
|
78
|
+
cd samples/hello-world-2
|
46
79
|
|
47
|
-
|
80
|
+
# Bellow: will print "Hello World!"
|
81
|
+
echo '{"hello":"Hello"}'|srs start|jq -r ".output"|jq -r ".hello_world"
|
48
82
|
|
83
|
+
# Bellow: will print "Hallo Welt!"
|
84
|
+
echo '{"hello":"Hallo"}'|srs start|jq -r ".output"|jq -r ".hello_world"
|
49
85
|
```
|
50
|
-
|
51
|
-
|
52
|
-
|
86
|
+
|
87
|
+
### Delete CloudFormation stack (with srs destroy)
|
88
|
+
|
89
|
+
```
|
90
|
+
export AWS_PROFILE=<AWS CLI profile name with sufficient privileges>
|
91
|
+
cd samples/hello-world-2
|
92
|
+
|
93
|
+
srs destroy
|
53
94
|
```
|
54
95
|
|
55
96
|
## Development
|
@@ -58,8 +99,10 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
58
99
|
|
59
100
|
### TODOs
|
60
101
|
|
61
|
-
* Custom IAM policies per Lambda task
|
62
|
-
* Workflow action unit test support
|
102
|
+
* Custom IAM policies per Lambda task (e.g. to allow a task to send a message to an SQS queue)
|
103
|
+
* Workflow task/action unit test support
|
104
|
+
* Better error handling and reporting
|
105
|
+
* Improved stack update strategy (e.g. renamed or added task scenario)
|
63
106
|
* ...
|
64
107
|
|
65
108
|
## Contributing
|
data/exe/simplerubysteps
CHANGED
data/exe/srs
ADDED
data/lib/function.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
|
-
require "
|
1
|
+
require "simplerubysteps"
|
2
|
+
|
2
3
|
require "./workflow.rb"
|
3
4
|
|
5
|
+
include Simplerubysteps
|
6
|
+
|
4
7
|
def handler(event:, context:)
|
5
8
|
puts ENV.inspect if ENV["DEBUG"]
|
6
9
|
puts event if ENV["DEBUG"]
|
7
10
|
puts context.inspect if ENV["DEBUG"]
|
8
11
|
|
9
12
|
if event["Token"]
|
10
|
-
$sm.states[
|
13
|
+
$sm.states[ENV["task"].to_sym].perform_action event["Input"], event["Token"]
|
11
14
|
else
|
12
|
-
$sm.states[
|
15
|
+
$sm.states[ENV["task"].to_sym].perform_action event["Input"]
|
13
16
|
end
|
14
17
|
end
|
@@ -1,66 +1,53 @@
|
|
1
|
+
require "erb"
|
2
|
+
|
3
|
+
module Simplerubysteps
|
4
|
+
CLOUDFORMATION_ERB_TEMPLATE = <<-YAML
|
1
5
|
---
|
2
6
|
AWSTemplateFormatVersion: "2010-09-09"
|
3
|
-
|
7
|
+
|
8
|
+
<% if resources[:functions] %>
|
9
|
+
Parameters:
|
4
10
|
LambdaS3:
|
5
11
|
Description: LambdaS3.
|
6
|
-
|
7
|
-
|
12
|
+
Type: String
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<% if resources[:state_machine] %>
|
8
16
|
StepFunctionsS3:
|
9
17
|
Description: StepFunctionsS3.
|
10
|
-
Default: my-state-machine-definition.json
|
11
|
-
Type: String
|
12
|
-
DeployLambda:
|
13
|
-
Description: DeployLambda.
|
14
|
-
Default: "yes"
|
15
|
-
Type: String
|
16
|
-
AllowedValues:
|
17
|
-
- "yes"
|
18
|
-
- "no"
|
19
|
-
ConstraintDescription: must specify yes or no.
|
20
|
-
DeployStepfunctions:
|
21
|
-
Description: DeployStepfunctions.
|
22
|
-
Default: "yes"
|
23
18
|
Type: String
|
24
|
-
AllowedValues:
|
25
|
-
- "yes"
|
26
|
-
- "no"
|
27
|
-
ConstraintDescription: must specify yes or no.
|
28
19
|
StateMachineType:
|
29
20
|
Description: StateMachineType.
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
CreateLambda: !Equals
|
34
|
-
- !Ref DeployLambda
|
35
|
-
- "yes"
|
36
|
-
CreateStepfunctions: !Equals
|
37
|
-
- !Ref DeployStepfunctions
|
38
|
-
- "yes"
|
21
|
+
Type: String
|
22
|
+
<% end %>
|
23
|
+
|
39
24
|
Resources:
|
40
|
-
DeployBucket:
|
25
|
+
DeployBucket:
|
41
26
|
Type: AWS::S3::Bucket
|
42
|
-
|
43
|
-
|
44
|
-
|
27
|
+
|
28
|
+
<% if resources[:functions] %>
|
29
|
+
<% resources[:functions].each_with_index do |resource, index| %>
|
30
|
+
LambdaFunction<%= index %>:
|
31
|
+
Type: "AWS::Lambda::Function"
|
45
32
|
Properties:
|
46
33
|
Code:
|
47
34
|
S3Bucket: !Ref DeployBucket
|
48
35
|
S3Key: !Ref LambdaS3
|
49
|
-
Handler: function.handler
|
50
|
-
Role: !GetAtt MyLambdaRole
|
36
|
+
Handler: function.handler
|
37
|
+
Role: !GetAtt MyLambdaRole<%= index %>.Arn
|
51
38
|
Runtime: ruby2.7
|
52
|
-
|
53
|
-
|
54
|
-
|
39
|
+
Environment:
|
40
|
+
Variables:
|
41
|
+
<% resource.each do |k, v| %>
|
42
|
+
<%= k %>: <%= v %>
|
43
|
+
<% end %>
|
44
|
+
LogGroup<%= index %>:
|
45
|
+
Type: AWS::Logs::LogGroup
|
55
46
|
Properties:
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
RoleArn: !GetAtt StepFunctionsRole.Arn
|
60
|
-
StateMachineType: !Ref StateMachineType
|
61
|
-
MyLambdaRole:
|
47
|
+
LogGroupName: !Sub "/aws/lambda/${LambdaFunction<%= index %>}"
|
48
|
+
RetentionInDays: 7
|
49
|
+
MyLambdaRole<%= index %>:
|
62
50
|
Type: AWS::IAM::Role
|
63
|
-
Condition: CreateLambda
|
64
51
|
Properties:
|
65
52
|
AssumeRolePolicyDocument:
|
66
53
|
Version: "2012-10-17"
|
@@ -81,9 +68,20 @@ Resources:
|
|
81
68
|
- logs:CreateLogStream
|
82
69
|
- logs:PutLogEvents
|
83
70
|
Resource: arn:aws:logs:*:*:*
|
71
|
+
<% end %>
|
72
|
+
<% end %>
|
73
|
+
|
74
|
+
<% if resources[:state_machine] %>
|
75
|
+
StepFunctionsStateMachine:
|
76
|
+
Type: "AWS::StepFunctions::StateMachine"
|
77
|
+
Properties:
|
78
|
+
DefinitionS3Location:
|
79
|
+
Bucket: !Ref DeployBucket
|
80
|
+
Key: !Ref StepFunctionsS3
|
81
|
+
RoleArn: !GetAtt StepFunctionsRole.Arn
|
82
|
+
StateMachineType: !Ref StateMachineType
|
84
83
|
StepFunctionsRole:
|
85
84
|
Type: AWS::IAM::Role
|
86
|
-
Condition: CreateStepfunctions
|
87
85
|
Properties:
|
88
86
|
AssumeRolePolicyDocument:
|
89
87
|
Version: "2012-10-17"
|
@@ -106,22 +104,41 @@ Resources:
|
|
106
104
|
- Effect: Allow
|
107
105
|
Action:
|
108
106
|
- lambda:InvokeFunction
|
109
|
-
Resource:
|
107
|
+
Resource:
|
108
|
+
<% resources[:functions].each_with_index do |resource, index| %>
|
109
|
+
- !GetAtt LambdaFunction<%= index %>.Arn
|
110
|
+
<% end %>
|
111
|
+
<% end %>
|
112
|
+
|
110
113
|
Outputs:
|
111
114
|
DeployBucket:
|
112
115
|
Value: !Ref DeployBucket
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
Value: !
|
124
|
-
|
116
|
+
|
117
|
+
<% if resources[:functions] %>
|
118
|
+
LambdaCount:
|
119
|
+
Value: <%= resources[:functions].length %>
|
120
|
+
<% resources[:functions].each_with_index do |resource, index| %>
|
121
|
+
LambdaRoleARN<%= index %>:
|
122
|
+
Value: !GetAtt MyLambdaRole<%= index %>.Arn
|
123
|
+
LambdaFunctionARN<%= index %>:
|
124
|
+
Value: !GetAtt LambdaFunction<%= index %>.Arn
|
125
|
+
LambdaFunctionName<%= index %>:
|
126
|
+
Value: !Ref LambdaFunction<%= index %>
|
127
|
+
<% end %>
|
128
|
+
<% end %>
|
129
|
+
|
130
|
+
<% if resources[:state_machine] %>
|
125
131
|
StepFunctionsStateMachineARN:
|
126
132
|
Value: !GetAtt StepFunctionsStateMachine.Arn
|
127
|
-
|
133
|
+
StateMachineType:
|
134
|
+
Value: !Ref StateMachineType
|
135
|
+
StepFunctionsRoleARN:
|
136
|
+
Value: !GetAtt StepFunctionsRole.Arn
|
137
|
+
<% end %>
|
138
|
+
YAML
|
139
|
+
|
140
|
+
def self.cloudformation_yaml(resources)
|
141
|
+
template = ERB.new(CLOUDFORMATION_ERB_TEMPLATE)
|
142
|
+
template.result(binding)
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Simplerubysteps
|
4
|
+
$sm = StateMachine.new
|
5
|
+
$tasks = []
|
6
|
+
|
7
|
+
def kind(k)
|
8
|
+
$sm.kind = k
|
9
|
+
end
|
10
|
+
|
11
|
+
def task(name)
|
12
|
+
t = $sm.add Task.new(name)
|
13
|
+
|
14
|
+
$tasks.last.next = t if $tasks.last
|
15
|
+
|
16
|
+
$tasks.push t
|
17
|
+
yield if block_given?
|
18
|
+
$tasks.pop
|
19
|
+
end
|
20
|
+
|
21
|
+
def callback(name)
|
22
|
+
t = $sm.add Callback.new(name)
|
23
|
+
|
24
|
+
$tasks.last.next = t if $tasks.last
|
25
|
+
|
26
|
+
$tasks.push t
|
27
|
+
yield if block_given?
|
28
|
+
$tasks.pop
|
29
|
+
end
|
30
|
+
|
31
|
+
def action(&action_block)
|
32
|
+
$tasks.last.action &action_block
|
33
|
+
end
|
34
|
+
|
35
|
+
def transition(state)
|
36
|
+
$tasks.last.next = state
|
37
|
+
end
|
38
|
+
|
39
|
+
def transition_to(state, &condition_block)
|
40
|
+
choice = $tasks.last.implicit_choice
|
41
|
+
|
42
|
+
c = ChoiceItem.new({
|
43
|
+
:Variable => "$.#{choice.name}_#{state}",
|
44
|
+
:StringMatches => "yes",
|
45
|
+
})
|
46
|
+
c.next = state
|
47
|
+
c.implicit_condition_block = condition_block
|
48
|
+
|
49
|
+
choice.add c
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_transition_to(state)
|
53
|
+
choice = $tasks.last.implicit_choice
|
54
|
+
|
55
|
+
choice.default = state
|
56
|
+
end
|
57
|
+
|
58
|
+
def choice(name)
|
59
|
+
t = $sm.add Choice.new(name)
|
60
|
+
|
61
|
+
$tasks.last.next = t if $tasks.last
|
62
|
+
|
63
|
+
$tasks.push t
|
64
|
+
yield if block_given?
|
65
|
+
$tasks.pop
|
66
|
+
end
|
67
|
+
|
68
|
+
def string_matches(var, match)
|
69
|
+
c = ChoiceItem.new({
|
70
|
+
:Variable => var,
|
71
|
+
:StringMatches => match,
|
72
|
+
})
|
73
|
+
|
74
|
+
$tasks.last.add c
|
75
|
+
|
76
|
+
$tasks.push c
|
77
|
+
yield if block_given?
|
78
|
+
$tasks.pop
|
79
|
+
end
|
80
|
+
|
81
|
+
def default
|
82
|
+
$tasks.push $tasks.last
|
83
|
+
yield if block_given?
|
84
|
+
$tasks.pop
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
module Simplerubysteps
|
2
|
+
$LAMBDA_FUNCTION_ARNS = ENV["LAMBDA_FUNCTION_ARNS"] ? ENV["LAMBDA_FUNCTION_ARNS"].split(",") : nil
|
3
|
+
|
4
|
+
def pop_function_arn
|
5
|
+
return "unknown" unless $LAMBDA_FUNCTION_ARNS
|
6
|
+
arn = $LAMBDA_FUNCTION_ARNS.first
|
7
|
+
$LAMBDA_FUNCTION_ARNS.delete arn
|
8
|
+
arn
|
9
|
+
end
|
10
|
+
|
11
|
+
def pop_function_name
|
12
|
+
return "unknown" unless pop_function_arn =~ /.+\:function\:(.+)/
|
13
|
+
$1
|
14
|
+
end
|
15
|
+
|
16
|
+
class StateMachine
|
17
|
+
attr_reader :states
|
18
|
+
attr_reader :start_at
|
19
|
+
attr_accessor :kind
|
20
|
+
|
21
|
+
def initialize()
|
22
|
+
@states = {}
|
23
|
+
@kind = "STANDARD"
|
24
|
+
end
|
25
|
+
|
26
|
+
def add(state)
|
27
|
+
@start_at = state.name unless @start_at
|
28
|
+
|
29
|
+
@states[state.name] = state
|
30
|
+
state.state_machine = self
|
31
|
+
|
32
|
+
state
|
33
|
+
end
|
34
|
+
|
35
|
+
def cloudformation_config
|
36
|
+
data = []
|
37
|
+
states.each do |name, state|
|
38
|
+
if state.is_a? Task or state.is_a? Callback
|
39
|
+
data.push({
|
40
|
+
task: name,
|
41
|
+
})
|
42
|
+
end
|
43
|
+
end
|
44
|
+
data
|
45
|
+
end
|
46
|
+
|
47
|
+
def render
|
48
|
+
{
|
49
|
+
:StartAt => @start_at,
|
50
|
+
:States => @states.map { |name, state| [name, state.render] }.to_h,
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class State
|
56
|
+
attr_reader :name
|
57
|
+
attr_accessor :state_machine
|
58
|
+
|
59
|
+
def initialize(name)
|
60
|
+
@name = name
|
61
|
+
@dict = {}
|
62
|
+
end
|
63
|
+
|
64
|
+
def []=(key, value)
|
65
|
+
@dict[key] = value
|
66
|
+
end
|
67
|
+
|
68
|
+
def next=(state)
|
69
|
+
@dict[:Next] = (state.is_a? Symbol) ? state : state.name
|
70
|
+
end
|
71
|
+
|
72
|
+
def render
|
73
|
+
dict = @dict
|
74
|
+
dict[:End] = true unless dict[:Next]
|
75
|
+
dict
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Task < State
|
80
|
+
def initialize(name)
|
81
|
+
super
|
82
|
+
@dict[:Type] = "Task"
|
83
|
+
@dict[:Resource] = pop_function_arn
|
84
|
+
@dict[:Parameters] = {
|
85
|
+
:Task => name,
|
86
|
+
"Input.$" => "$",
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def action(&action_block)
|
91
|
+
@action_block = action_block
|
92
|
+
end
|
93
|
+
|
94
|
+
def perform_action(input)
|
95
|
+
output = input # default: pass through
|
96
|
+
|
97
|
+
output = @action_block.call(input) if @action_block
|
98
|
+
|
99
|
+
if @implicit_choice
|
100
|
+
output = {} unless output
|
101
|
+
@implicit_choice.perform_action(output)
|
102
|
+
end
|
103
|
+
|
104
|
+
output
|
105
|
+
end
|
106
|
+
|
107
|
+
def implicit_choice
|
108
|
+
unless @implicit_choice
|
109
|
+
@implicit_choice = Choice.new("#{name}_choice")
|
110
|
+
$sm.add @implicit_choice
|
111
|
+
self.next = @implicit_choice
|
112
|
+
end
|
113
|
+
@implicit_choice
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class Callback < State
|
118
|
+
def initialize(name)
|
119
|
+
super
|
120
|
+
@dict[:Type] = "Task"
|
121
|
+
@dict[:Resource] = "arn:aws:states:::lambda:invoke.waitForTaskToken"
|
122
|
+
@dict[:Parameters] = {
|
123
|
+
:FunctionName => pop_function_name,
|
124
|
+
:Payload => {
|
125
|
+
:Task => name,
|
126
|
+
"Input.$" => "$",
|
127
|
+
"Token.$" => "$$.Task.Token",
|
128
|
+
},
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
def action(&action_block)
|
133
|
+
@action_block = action_block
|
134
|
+
end
|
135
|
+
|
136
|
+
def perform_action(input, token)
|
137
|
+
@action_block.call(input, token) if @action_block
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class ChoiceItem
|
142
|
+
attr_accessor :implicit_condition_block
|
143
|
+
|
144
|
+
def initialize(dict = {}, state = nil)
|
145
|
+
@dict = dict
|
146
|
+
self.next = state if state
|
147
|
+
end
|
148
|
+
|
149
|
+
def next=(state)
|
150
|
+
@dict[:Next] = (state.is_a? Symbol) ? state : state.name
|
151
|
+
end
|
152
|
+
|
153
|
+
def render
|
154
|
+
@dict
|
155
|
+
end
|
156
|
+
|
157
|
+
def perform_action(choice_name, output)
|
158
|
+
if @implicit_condition_block
|
159
|
+
output["#{choice_name}_#{@dict[:Next]}"] = @implicit_condition_block.call(output) ? "yes" : "no"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class Choice < State
|
165
|
+
attr_reader :choices
|
166
|
+
|
167
|
+
def initialize(name)
|
168
|
+
super
|
169
|
+
@choices = []
|
170
|
+
@dict[:Type] = "Choice"
|
171
|
+
end
|
172
|
+
|
173
|
+
def add(item)
|
174
|
+
@choices.push item
|
175
|
+
end
|
176
|
+
|
177
|
+
def add_string_matches(var, match, state)
|
178
|
+
add ChoiceItem.new({
|
179
|
+
:Variable => var,
|
180
|
+
:StringMatches => match,
|
181
|
+
}, state)
|
182
|
+
end
|
183
|
+
|
184
|
+
def default=(state)
|
185
|
+
@dict[:Default] = (state.is_a? Symbol) ? state : state.name
|
186
|
+
end
|
187
|
+
|
188
|
+
def next=(state)
|
189
|
+
self.default = state
|
190
|
+
end
|
191
|
+
|
192
|
+
def render
|
193
|
+
dict = @dict.clone
|
194
|
+
dict[:Choices] = @choices.map { |item| item.render }
|
195
|
+
dict
|
196
|
+
end
|
197
|
+
|
198
|
+
def perform_action(output)
|
199
|
+
@choices.each do |choice|
|
200
|
+
choice.perform_action name, output
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|