states-dsl 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/states-dsl +5 -0
- data/examples/simple.rb +64 -0
- data/lib/states/dsl.rb +58 -0
- data/lib/states/dsl/catch.rb +30 -0
- data/lib/states/dsl/choice.rb +17 -0
- data/lib/states/dsl/choices.rb +44 -0
- data/lib/states/dsl/cli.rb +16 -0
- data/lib/states/dsl/condition_group.rb +22 -0
- data/lib/states/dsl/context.rb +47 -0
- data/lib/states/dsl/error_support.rb +10 -0
- data/lib/states/dsl/execution_context.rb +39 -0
- data/lib/states/dsl/fail.rb +24 -0
- data/lib/states/dsl/namespace.rb +22 -0
- data/lib/states/dsl/naming.rb +66 -0
- data/lib/states/dsl/parallel.rb +21 -0
- data/lib/states/dsl/resource_lookup.rb +30 -0
- data/lib/states/dsl/retry.rb +39 -0
- data/lib/states/dsl/state.rb +22 -0
- data/lib/states/dsl/state_like.rb +127 -0
- data/lib/states/dsl/state_machine.rb +31 -0
- data/lib/states/dsl/state_name.rb +22 -0
- data/lib/states/dsl/state_reference.rb +19 -0
- data/lib/states/dsl/trait.rb +10 -0
- data/lib/states/dsl/variable_choice.rb +16 -0
- data/lib/states/dsl/variable_condition.rb +59 -0
- data/lib/states/dsl/variable_condition_part.rb +15 -0
- data/lib/states/dsl/version.rb +5 -0
- data/lib/states/dsl/wait.rb +38 -0
- data/states-dsl.gemspec +28 -0
- metadata +140 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
module States
|
2
|
+
module Dsl
|
3
|
+
class ConditionGroup < Choice
|
4
|
+
def initialize(type, naming)
|
5
|
+
super(naming)
|
6
|
+
@type = type
|
7
|
+
@parts = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def variable(path, &block)
|
11
|
+
c = VariableConditionPart.new(path)
|
12
|
+
c.instance_eval(&block)
|
13
|
+
@parts << c
|
14
|
+
c
|
15
|
+
end
|
16
|
+
|
17
|
+
def serializable_hash
|
18
|
+
{ @type => @parts.map(&:serializable_hash) }.merge(super)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module States
|
2
|
+
module Dsl
|
3
|
+
class Context
|
4
|
+
attr_accessor :naming, :traits, :resource_defaults
|
5
|
+
|
6
|
+
def initialize(options={})
|
7
|
+
@traits = {}
|
8
|
+
@naming = Naming.new(nil)
|
9
|
+
@naming.converter = options[:name_converter]
|
10
|
+
@resource_defaults = options[:resource] || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_sub_context(options={})
|
14
|
+
sub = Context.new
|
15
|
+
sub.traits = traits
|
16
|
+
|
17
|
+
sub.resource_defaults = resource_defaults
|
18
|
+
if defaults = options[:resource]
|
19
|
+
sub.resource_defaults = defaults
|
20
|
+
end
|
21
|
+
|
22
|
+
sub.naming = Naming.new(naming)
|
23
|
+
if converter = options[:name_converter]
|
24
|
+
sub.naming.converter = converter
|
25
|
+
end
|
26
|
+
sub
|
27
|
+
end
|
28
|
+
|
29
|
+
def register_trait(name, trait)
|
30
|
+
trait.context = self
|
31
|
+
@traits[name] = trait
|
32
|
+
end
|
33
|
+
|
34
|
+
def lookup_traits(names)
|
35
|
+
names.map { |nm| traits[nm] }
|
36
|
+
end
|
37
|
+
|
38
|
+
def function_resource(name)
|
39
|
+
ResourceLookup.new(resource_defaults).function(name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def activity_resource(name)
|
43
|
+
ResourceLookup.new(resource_defaults).activity(name)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module States
|
2
|
+
module Dsl
|
3
|
+
class ExecutionContext
|
4
|
+
attr_reader :context
|
5
|
+
attr_reader :resource_defaults
|
6
|
+
|
7
|
+
def initialize(context, options)
|
8
|
+
@context = if context.nil?
|
9
|
+
Context.new(options)
|
10
|
+
else
|
11
|
+
context.create_sub_context
|
12
|
+
end
|
13
|
+
if start_at = options[:start_at]
|
14
|
+
@start_at = @context.naming.ref(start_at)
|
15
|
+
end
|
16
|
+
@states = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def state(name, options={}, &block)
|
20
|
+
state = State.new(name, @context, options)
|
21
|
+
state.instance_eval(&block) if block
|
22
|
+
@states << state
|
23
|
+
state
|
24
|
+
end
|
25
|
+
|
26
|
+
def start(name, options={}, &block)
|
27
|
+
@start_at = @context.naming.ref(name)
|
28
|
+
state(name, options, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def serializable_hash
|
32
|
+
{
|
33
|
+
"StartAt" => @start_at,
|
34
|
+
"States" => @states.reduce({}) { |m,s| m[s.name.to_s] = s.serializable_hash; m }
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module States
|
2
|
+
module Dsl
|
3
|
+
class Fail
|
4
|
+
def initialize(error=nil,cause=nil)
|
5
|
+
@error, @cause = error, cause
|
6
|
+
end
|
7
|
+
|
8
|
+
def error(err)
|
9
|
+
@error = err
|
10
|
+
end
|
11
|
+
|
12
|
+
def cause(cause)
|
13
|
+
@cause = cause
|
14
|
+
end
|
15
|
+
|
16
|
+
def serializable_hash
|
17
|
+
h = {}
|
18
|
+
h["Error"] = @error if @error
|
19
|
+
h["Cause"] = @cause if @cause
|
20
|
+
h
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module States
|
2
|
+
module Dsl
|
3
|
+
class Namespace
|
4
|
+
def initialize
|
5
|
+
@source = {}
|
6
|
+
@plurals = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def resolve(name)
|
10
|
+
if usages = @source[name.local_name]
|
11
|
+
if usages.length == 1
|
12
|
+
usages[0].index = 0
|
13
|
+
end
|
14
|
+
name.index = usages.length
|
15
|
+
usages << name
|
16
|
+
else
|
17
|
+
@source[name.local_name] = [name]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module States
|
2
|
+
module Dsl
|
3
|
+
class Naming
|
4
|
+
attr_accessor :converter
|
5
|
+
|
6
|
+
attr_reader :parent
|
7
|
+
attr_reader :namespace
|
8
|
+
attr_reader :children
|
9
|
+
attr_reader :names
|
10
|
+
|
11
|
+
def initialize(parent)
|
12
|
+
@parent = parent
|
13
|
+
@names = []
|
14
|
+
@references = []
|
15
|
+
@children = []
|
16
|
+
if parent
|
17
|
+
@namespace = parent.namespace
|
18
|
+
if parent.converter
|
19
|
+
self.converter = parent.converter
|
20
|
+
end
|
21
|
+
parent.children << self
|
22
|
+
else
|
23
|
+
@namespace = Namespace.new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def <<(name)
|
28
|
+
nm = StateName.new(name, self)
|
29
|
+
@names << nm
|
30
|
+
nm
|
31
|
+
end
|
32
|
+
|
33
|
+
def ref(name)
|
34
|
+
ref = StateReference.new(name, self)
|
35
|
+
@references << ref
|
36
|
+
ref
|
37
|
+
end
|
38
|
+
|
39
|
+
def convert(str)
|
40
|
+
if converter
|
41
|
+
converter.call(str)
|
42
|
+
else
|
43
|
+
str
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def resolve
|
48
|
+
return if @resolved
|
49
|
+
if parent
|
50
|
+
parent.resolve
|
51
|
+
else
|
52
|
+
resolve_children
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def resolve_children
|
57
|
+
@names.each { |name| @namespace.resolve(name) }
|
58
|
+
@references.each do |ref|
|
59
|
+
ref.resolved_to = @names.detect { |nm| nm.local_name == ref.name }
|
60
|
+
end
|
61
|
+
@children.each(&:resolve_children)
|
62
|
+
@resolved = true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module States
|
2
|
+
module Dsl
|
3
|
+
class Parallel
|
4
|
+
def initialize(context)
|
5
|
+
@context = context
|
6
|
+
@branches = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def branch(options={}, &block)
|
10
|
+
branch = ExecutionContext.new(@context, options)
|
11
|
+
branch.instance_eval(&block)
|
12
|
+
@branches << branch
|
13
|
+
branch
|
14
|
+
end
|
15
|
+
|
16
|
+
def serializable_hash
|
17
|
+
{ "Branches" => @branches.map(&:serializable_hash) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module States
|
2
|
+
module Dsl
|
3
|
+
class ResourceLookup
|
4
|
+
attr_reader :params
|
5
|
+
|
6
|
+
def initialize(defaults)
|
7
|
+
@defaults = defaults
|
8
|
+
@params = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def function(name)
|
12
|
+
resource(name, :function, :lambda)
|
13
|
+
end
|
14
|
+
|
15
|
+
def activity(name)
|
16
|
+
resource(name, :activity, :states)
|
17
|
+
end
|
18
|
+
|
19
|
+
def resource(name, type, service, options={})
|
20
|
+
@params = options.merge(name: name, type: type, service: service)
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
values = {partition: "aws"}.merge(@defaults).merge(params)
|
26
|
+
["arn", values[:partition], values[:service], values[:region], values[:account], values[:type], values[:name]].join(":")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module States
|
2
|
+
module Dsl
|
3
|
+
class Retry
|
4
|
+
include States::Dsl::ErrorSupport
|
5
|
+
|
6
|
+
def initialize(*arguments)
|
7
|
+
options = arguments.last.is_a?(Hash) ? arguments.pop : {}
|
8
|
+
@error_equals = if arguments.length > 0
|
9
|
+
ensure_errors_array(arguments.first)
|
10
|
+
else
|
11
|
+
ensure_errors_array(options[:error_equals])
|
12
|
+
end
|
13
|
+
@max_attempts = options[:max_attempts]
|
14
|
+
@backoff_rate = options[:backoff_rate]
|
15
|
+
@interval_seconds = options[:interval_seconds]
|
16
|
+
end
|
17
|
+
|
18
|
+
def max_attempts(n)
|
19
|
+
@max_attempts = n
|
20
|
+
end
|
21
|
+
|
22
|
+
def backoff_rate(r)
|
23
|
+
@backoff_rate = r
|
24
|
+
end
|
25
|
+
|
26
|
+
def interval_seconds(s)
|
27
|
+
@interval_seconds = s
|
28
|
+
end
|
29
|
+
|
30
|
+
def serializable_hash
|
31
|
+
h = { "ErrorEquals" => @error_equals }
|
32
|
+
h["MaxAttempts"] = @max_attempts if @max_attempts
|
33
|
+
h["BackoffRate"] = @backoff_rate if @backoff_rate
|
34
|
+
h["IntervalSeconds"] = @interval_seconds if @interval_seconds
|
35
|
+
h
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module States
|
2
|
+
module Dsl
|
3
|
+
class State < StateLike
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
def initialize(name, context, options={})
|
7
|
+
super(options)
|
8
|
+
@name = context.naming << name
|
9
|
+
self.context = context
|
10
|
+
end
|
11
|
+
|
12
|
+
def serializable_hash
|
13
|
+
json = super
|
14
|
+
json["Type"] ||= "Pass"
|
15
|
+
if json["Next"].nil? && !%w(Choice Fail).include?(json["Type"])
|
16
|
+
json["End"] = true
|
17
|
+
end
|
18
|
+
json
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module States
|
2
|
+
module Dsl
|
3
|
+
class StateLike
|
4
|
+
UNSET = Object.new
|
5
|
+
|
6
|
+
attr_accessor :context
|
7
|
+
|
8
|
+
def initialize(options={})
|
9
|
+
@traits = options[:traits] || []
|
10
|
+
@result_path = @output_path = @input_path = UNSET
|
11
|
+
@retries = []
|
12
|
+
@catches = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def function(name)
|
16
|
+
resource(context.function_resource(name))
|
17
|
+
end
|
18
|
+
|
19
|
+
def activity(name)
|
20
|
+
resource(context.activity_resource(name))
|
21
|
+
end
|
22
|
+
|
23
|
+
def resource(arn)
|
24
|
+
@resource = arn
|
25
|
+
end
|
26
|
+
|
27
|
+
def result_path(path)
|
28
|
+
@result_path = path
|
29
|
+
end
|
30
|
+
|
31
|
+
def output_path(path)
|
32
|
+
@output_path = path
|
33
|
+
end
|
34
|
+
|
35
|
+
def input_path(path)
|
36
|
+
@input_path = path
|
37
|
+
end
|
38
|
+
|
39
|
+
def next_state(state)
|
40
|
+
@next_state = context.naming.ref(state)
|
41
|
+
end
|
42
|
+
|
43
|
+
def parallel(&block)
|
44
|
+
@parallel = Parallel.new(@context)
|
45
|
+
@parallel.instance_eval(&block)
|
46
|
+
@parallel
|
47
|
+
end
|
48
|
+
|
49
|
+
def choices(&block)
|
50
|
+
@choices = Choices.new(@context.naming)
|
51
|
+
@choices.instance_eval(&block)
|
52
|
+
@choices
|
53
|
+
end
|
54
|
+
|
55
|
+
def wait(&block)
|
56
|
+
@wait = Wait.new(@context.naming)
|
57
|
+
@wait.instance_eval(&block)
|
58
|
+
@wait
|
59
|
+
end
|
60
|
+
|
61
|
+
def fail(&block)
|
62
|
+
@fail = Fail.new
|
63
|
+
@fail.instance_eval(&block)
|
64
|
+
@fail
|
65
|
+
end
|
66
|
+
|
67
|
+
def retry_if(*arguments, &block)
|
68
|
+
_retry = Retry.new(*arguments)
|
69
|
+
if block_given?
|
70
|
+
_retry.instance_eval(&block)
|
71
|
+
end
|
72
|
+
@retries << _retry
|
73
|
+
end
|
74
|
+
|
75
|
+
def catch_if(options={}, &block)
|
76
|
+
katch = Catch.new(context, options)
|
77
|
+
if block_given?
|
78
|
+
katch.instance_eval(&block)
|
79
|
+
end
|
80
|
+
@catches << katch
|
81
|
+
end
|
82
|
+
|
83
|
+
def serializable_hash
|
84
|
+
j = context.lookup_traits(@traits).reduce({}) { |m, t| m.merge(t.serializable_hash) }
|
85
|
+
if @resource
|
86
|
+
j["Type"] = "Task"
|
87
|
+
j["Resource"] = @resource.to_s
|
88
|
+
|
89
|
+
elsif @parallel
|
90
|
+
j["Type"] = "Parallel"
|
91
|
+
j.merge!(@parallel.serializable_hash)
|
92
|
+
|
93
|
+
elsif @choices
|
94
|
+
j["Type"] = "Choice"
|
95
|
+
j.merge!(@choices.serializable_hash)
|
96
|
+
|
97
|
+
elsif @wait
|
98
|
+
j["Type"] = "Wait"
|
99
|
+
j.merge!(@wait.serializable_hash)
|
100
|
+
|
101
|
+
elsif @fail
|
102
|
+
j["Type"] = "Fail"
|
103
|
+
j.merge!(@fail.serializable_hash)
|
104
|
+
end
|
105
|
+
|
106
|
+
[
|
107
|
+
[@result_path, "ResultPath"],
|
108
|
+
[@input_path , "InputPath" ],
|
109
|
+
[@output_path, "OutputPath"],
|
110
|
+
].each { |(k,v)| j[v] = k if k != UNSET }
|
111
|
+
|
112
|
+
unless @retries.empty?
|
113
|
+
j["Retry"] = @retries.map(&:serializable_hash)
|
114
|
+
end
|
115
|
+
|
116
|
+
unless @catches.empty?
|
117
|
+
j["Catch"] = @catches.map(&:serializable_hash)
|
118
|
+
end
|
119
|
+
|
120
|
+
if @next_state
|
121
|
+
j["Next"] = @next_state.to_s
|
122
|
+
end
|
123
|
+
j
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|