states-dsl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,10 @@
1
+ module States
2
+ module Dsl
3
+ module ErrorSupport
4
+ protected
5
+ def ensure_errors_array(arr)
6
+ [arr].flatten.map { |exc| exc == :all ? "States.ALL" : exc }
7
+ end
8
+ end
9
+ end
10
+ 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