states-dsl 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/.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
|