aws-codedeploy-agent 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/aws-codedeploy-agent.gemspec +5 -5
- data/certs/host-agent-deployment-signer-ca-chain.pem +30 -0
- data/conf/codedeployagent.yml +0 -1
- data/lib/instance_agent.rb +1 -13
- data/lib/instance_agent/agent/base.rb +38 -12
- data/lib/instance_agent/agent/plugin.rb +21 -0
- data/lib/instance_agent/config.rb +2 -1
- data/lib/instance_agent/platform/linux_util.rb +4 -0
- data/lib/instance_agent/plugins/codedeploy/application_specification/ace_info.rb +133 -0
- data/lib/instance_agent/plugins/codedeploy/application_specification/acl_info.rb +163 -0
- data/lib/instance_agent/plugins/codedeploy/application_specification/application_specification.rb +143 -0
- data/lib/instance_agent/plugins/codedeploy/application_specification/context_info.rb +23 -0
- data/lib/instance_agent/plugins/codedeploy/application_specification/file_info.rb +23 -0
- data/lib/instance_agent/plugins/codedeploy/application_specification/linux_permission_info.rb +121 -0
- data/lib/instance_agent/plugins/codedeploy/application_specification/mode_info.rb +66 -0
- data/lib/instance_agent/plugins/codedeploy/application_specification/range_info.rb +134 -0
- data/lib/instance_agent/plugins/codedeploy/application_specification/script_info.rb +27 -0
- data/lib/instance_agent/plugins/codedeploy/codedeploy_control.rb +100 -0
- data/lib/instance_agent/plugins/codedeploy/command_executor.rb +359 -0
- data/lib/instance_agent/plugins/codedeploy/command_poller.rb +178 -0
- data/lib/instance_agent/plugins/codedeploy/deployment_specification.rb +161 -0
- data/lib/instance_agent/plugins/codedeploy/hook_executor.rb +226 -0
- data/lib/instance_agent/plugins/codedeploy/install_instruction.rb +389 -0
- data/lib/instance_agent/plugins/codedeploy/installer.rb +147 -0
- data/lib/instance_agent/plugins/codedeploy/onpremise_config.rb +42 -0
- data/lib/instance_agent/plugins/codedeploy/register_plugin.rb +17 -0
- data/lib/instance_agent/runner/child.rb +20 -5
- data/lib/instance_agent/runner/master.rb +2 -15
- data/lib/instance_metadata.rb +2 -2
- data/test/certificate_helper.rb +1 -1
- data/test/helpers/instance_agent_helper.rb +1 -0
- data/test/instance_agent/agent/base_test.rb +16 -3
- data/test/instance_agent/config_test.rb +2 -1
- data/test/instance_agent/plugins/codedeploy/application_specification_test.rb +1713 -0
- data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/codedeploy_control_test.rb +1 -1
- data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/command_executor_test.rb +32 -9
- data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/command_poller_test.rb +13 -14
- data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/deployment_specification_test.rb +98 -25
- data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/hook_executor_test.rb +83 -15
- data/test/instance_agent/plugins/codedeploy/install_instruction_test.rb +568 -0
- data/test/instance_agent/{codedeploy_plugin → plugins/codedeploy}/installer_test.rb +12 -9
- data/test/instance_agent/plugins/codedeploy/onpremise_config_test.rb +72 -0
- data/test/instance_agent/runner/child_test.rb +1 -1
- data/vendor/gems/.codedeploy-commands-1.0.0.created.rid +1 -1
- data/vendor/gems/codedeploy-commands/lib/aws/plugins/deploy_control_endpoint.rb +4 -0
- data/vendor/gems/jmespath-1.0.1/lib/jmespath.rb +41 -0
- data/vendor/gems/jmespath-1.0.1/lib/jmespath/caching_parser.rb +30 -0
- data/vendor/gems/jmespath-1.0.1/lib/jmespath/errors.rb +17 -0
- data/vendor/gems/jmespath-1.0.1/lib/jmespath/expr_node.rb +15 -0
- data/vendor/gems/jmespath-1.0.1/lib/jmespath/lexer.rb +116 -0
- data/vendor/gems/jmespath-1.0.1/lib/jmespath/parser.rb +347 -0
- data/vendor/gems/jmespath-1.0.1/lib/jmespath/runtime.rb +71 -0
- data/vendor/gems/jmespath-1.0.1/lib/jmespath/token.rb +41 -0
- data/vendor/gems/jmespath-1.0.1/lib/jmespath/token_stream.rb +60 -0
- data/vendor/gems/jmespath-1.0.1/lib/jmespath/tree_interpreter.rb +523 -0
- data/vendor/gems/jmespath-1.0.1/lib/jmespath/version.rb +3 -0
- data/vendor/gems/process_manager/lib/process_manager/master.rb +16 -5
- data/vendor/specifications/{aws-sdk-core-2.0.5.gemspec → aws-sdk-core-2.0.42.gemspec} +9 -11
- data/vendor/specifications/builder-3.2.2.gemspec +1 -1
- data/vendor/specifications/codedeploy-commands-1.0.0.gemspec +7 -6
- data/vendor/specifications/gli-2.5.6.gemspec +1 -1
- data/vendor/specifications/jmespath-1.0.1.gemspec +29 -0
- data/vendor/specifications/little-plugger-1.1.3.gemspec +1 -1
- data/vendor/specifications/logging-1.8.1.gemspec +1 -1
- data/vendor/specifications/multi_json-1.7.7.gemspec +1 -1
- data/vendor/specifications/multi_json-1.8.4.gemspec +1 -1
- data/vendor/specifications/multi_xml-0.5.5.gemspec +1 -1
- data/vendor/specifications/process_manager-0.0.13.gemspec +1 -1
- data/vendor/specifications/simple_pid-0.2.1.gemspec +1 -1
- metadata +76 -63
- data/lib/instance_agent/codedeploy_plugin/application_specification/ace_info.rb +0 -133
- data/lib/instance_agent/codedeploy_plugin/application_specification/acl_info.rb +0 -163
- data/lib/instance_agent/codedeploy_plugin/application_specification/application_specification.rb +0 -142
- data/lib/instance_agent/codedeploy_plugin/application_specification/context_info.rb +0 -23
- data/lib/instance_agent/codedeploy_plugin/application_specification/file_info.rb +0 -23
- data/lib/instance_agent/codedeploy_plugin/application_specification/linux_permission_info.rb +0 -121
- data/lib/instance_agent/codedeploy_plugin/application_specification/mode_info.rb +0 -66
- data/lib/instance_agent/codedeploy_plugin/application_specification/range_info.rb +0 -134
- data/lib/instance_agent/codedeploy_plugin/application_specification/script_info.rb +0 -27
- data/lib/instance_agent/codedeploy_plugin/codedeploy_control.rb +0 -72
- data/lib/instance_agent/codedeploy_plugin/command_executor.rb +0 -357
- data/lib/instance_agent/codedeploy_plugin/command_poller.rb +0 -170
- data/lib/instance_agent/codedeploy_plugin/deployment_specification.rb +0 -150
- data/lib/instance_agent/codedeploy_plugin/hook_executor.rb +0 -206
- data/lib/instance_agent/codedeploy_plugin/install_instruction.rb +0 -374
- data/lib/instance_agent/codedeploy_plugin/installer.rb +0 -143
- data/lib/instance_agent/codedeploy_plugin/request_helper.rb +0 -28
- data/test/instance_agent/codedeploy_plugin/application_specification_test.rb +0 -1710
- data/test/instance_agent/codedeploy_plugin/install_instruction_test.rb +0 -566
- data/test/instance_agent/codedeploy_plugin/request_helper_test.rb +0 -37
- data/vendor/specifications/jamespath-0.5.1.gemspec +0 -35
@@ -0,0 +1,71 @@
|
|
1
|
+
module JMESPath
|
2
|
+
# @api private
|
3
|
+
class Runtime
|
4
|
+
|
5
|
+
# @api private
|
6
|
+
DEFAULT_PARSER = CachingParser.new
|
7
|
+
|
8
|
+
# Constructs a new runtime object for evaluating JMESPath expressions.
|
9
|
+
#
|
10
|
+
# runtime = JMESPath::Runtime.new
|
11
|
+
# runtime.search(expression, data)
|
12
|
+
# #=> ...
|
13
|
+
#
|
14
|
+
# ## Caching
|
15
|
+
#
|
16
|
+
# When constructing a {Runtime}, the default parser caches expressions.
|
17
|
+
# This significantly speeds up calls to {#search} multiple times
|
18
|
+
# with the same expression but different data. To disable caching, pass
|
19
|
+
# `:cache_expressions => false` to the constructor or pass a custom
|
20
|
+
# `:parser`.
|
21
|
+
#
|
22
|
+
# @example Re-use a Runtime, caching enabled by default
|
23
|
+
#
|
24
|
+
# runtime = JMESPath::Runtime.new
|
25
|
+
# runtime.parser
|
26
|
+
# #=> #<JMESPath::CachingParser ...>
|
27
|
+
#
|
28
|
+
# @example Disable caching
|
29
|
+
#
|
30
|
+
# runtime = JMESPath::Runtime.new(cache_expressions: false)
|
31
|
+
# runtime.parser
|
32
|
+
# #=> #<JMESPath::Parser ...>
|
33
|
+
#
|
34
|
+
# @option options [Boolean] :cache_expressions (true) When `false`, a non
|
35
|
+
# caching parser will be used. When `true`, a shared instance of
|
36
|
+
# {CachingParser} is used. Defaults to `true`.
|
37
|
+
#
|
38
|
+
# @option options [Parser,CachingParser] :parser
|
39
|
+
#
|
40
|
+
# @option options [Interpreter] :interpreter
|
41
|
+
#
|
42
|
+
def initialize(options = {})
|
43
|
+
@parser = options[:parser] || default_parser(options)
|
44
|
+
@interpreter = options[:interpreter] || TreeInterpreter.new
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Parser, CachingParser]
|
48
|
+
attr_reader :parser
|
49
|
+
|
50
|
+
# @return [Interpreter]
|
51
|
+
attr_reader :interpreter
|
52
|
+
|
53
|
+
# @param [String<JMESPath>] expression
|
54
|
+
# @param [Hash] data
|
55
|
+
# @return [Mixed,nil]
|
56
|
+
def search(expression, data)
|
57
|
+
@interpreter.visit(@parser.parse(expression), data)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def default_parser(options)
|
63
|
+
if options[:cache_expressions] == false
|
64
|
+
Parser.new(options)
|
65
|
+
else
|
66
|
+
DEFAULT_PARSER
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module JMESPath
|
2
|
+
# @api private
|
3
|
+
class Token < Struct.new(:type, :value, :position, :binding_power)
|
4
|
+
|
5
|
+
# @api private
|
6
|
+
NULL_TOKEN = Token.new(:eof, '', nil)
|
7
|
+
|
8
|
+
# binding power
|
9
|
+
# @api private
|
10
|
+
BINDING_POWER = {
|
11
|
+
:eof => 0,
|
12
|
+
:quoted_identifier => 0,
|
13
|
+
:identifier => 0,
|
14
|
+
:rbracket => 0,
|
15
|
+
:rparen => 0,
|
16
|
+
:comma => 0,
|
17
|
+
:rbrace => 0,
|
18
|
+
:number => 0,
|
19
|
+
:current => 0,
|
20
|
+
:expref => 0,
|
21
|
+
:pipe => 1,
|
22
|
+
:comparator => 2,
|
23
|
+
:or => 5,
|
24
|
+
:flatten => 6,
|
25
|
+
:star => 20,
|
26
|
+
:dot => 40,
|
27
|
+
:lbrace => 50,
|
28
|
+
:filter => 50,
|
29
|
+
:lbracket => 50,
|
30
|
+
:lparen => 60,
|
31
|
+
}
|
32
|
+
|
33
|
+
# @param [Symbol] type
|
34
|
+
# @param [Mixed] value
|
35
|
+
# @param [Integer] position
|
36
|
+
def initialize(type, value, position)
|
37
|
+
super(type, value, position, BINDING_POWER[type])
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module JMESPath
|
2
|
+
# @api private
|
3
|
+
class TokenStream
|
4
|
+
|
5
|
+
# @param [String<JMESPath>] expression
|
6
|
+
# @param [Array<Token>] tokens
|
7
|
+
def initialize(expression, tokens)
|
8
|
+
@expression = expression
|
9
|
+
@tokens = tokens
|
10
|
+
@token = nil
|
11
|
+
@position = -1
|
12
|
+
self.next
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [String<JMESPath>]
|
16
|
+
attr_reader :expression
|
17
|
+
|
18
|
+
# @return [Token]
|
19
|
+
attr_reader :token
|
20
|
+
|
21
|
+
# @return [Integer]
|
22
|
+
attr_reader :position
|
23
|
+
|
24
|
+
# @option options [Array<Symbol>] :match Requires the next token to be
|
25
|
+
# one of the given symbols or an error is raised.
|
26
|
+
def next(options = {})
|
27
|
+
validate_match(_next, options[:match])
|
28
|
+
end
|
29
|
+
|
30
|
+
def lookahead(count)
|
31
|
+
@tokens[@position + count] || Token::NULL_TOKEN
|
32
|
+
end
|
33
|
+
|
34
|
+
# @api private
|
35
|
+
def inspect
|
36
|
+
str = []
|
37
|
+
@tokens.each do |token|
|
38
|
+
str << "%3d %-15s %s" %
|
39
|
+
[token.position, token.type, token.value.inspect]
|
40
|
+
end
|
41
|
+
str.join("\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def _next
|
47
|
+
@position += 1
|
48
|
+
@token = @tokens[@position] || Token::NULL_TOKEN
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_match(token, match)
|
52
|
+
if match && !match.include?(token.type)
|
53
|
+
raise Errors::SyntaxError, "type missmatch"
|
54
|
+
else
|
55
|
+
token
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,523 @@
|
|
1
|
+
module JMESPath
|
2
|
+
# @api private
|
3
|
+
class TreeInterpreter
|
4
|
+
|
5
|
+
def visit(node, data)
|
6
|
+
dispatch(node, data)
|
7
|
+
end
|
8
|
+
|
9
|
+
# @api private
|
10
|
+
def method_missing(method_name, *args)
|
11
|
+
if matches = method_name.to_s.match(/^function_(.*)/)
|
12
|
+
raise Errors::UnknownFunctionError, "unknown function #{matches[1]}()"
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def dispatch(node, value)
|
21
|
+
case node[:type]
|
22
|
+
|
23
|
+
when :field
|
24
|
+
# hash_like?
|
25
|
+
key = node[:key]
|
26
|
+
case value
|
27
|
+
when Hash then value.key?(key) ? value[key] : value[key.to_sym]
|
28
|
+
when Struct then value.respond_to?(key) ? value[key] : nil
|
29
|
+
else nil
|
30
|
+
end
|
31
|
+
|
32
|
+
when :subexpression
|
33
|
+
dispatch(node[:children][1], dispatch(node[:children][0], value))
|
34
|
+
|
35
|
+
when :index
|
36
|
+
if Array === value
|
37
|
+
value[node[:index]]
|
38
|
+
else
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
when :projection
|
43
|
+
# Interprets a projection node, passing the values of the left
|
44
|
+
# child through the values of the right child and aggregating
|
45
|
+
# the non-null results into the return value.
|
46
|
+
left = dispatch(node[:children][0], value)
|
47
|
+
if node[:from] == :object && hash_like?(left)
|
48
|
+
projection(left.values, node)
|
49
|
+
elsif node[:from] == :object && left == []
|
50
|
+
projection(left, node)
|
51
|
+
elsif node[:from] == :array && Array === left
|
52
|
+
projection(left, node)
|
53
|
+
else
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
when :flatten
|
58
|
+
value = dispatch(node[:children][0], value)
|
59
|
+
if Array === value
|
60
|
+
value.inject([]) do |values, v|
|
61
|
+
values + (Array === v ? v : [v])
|
62
|
+
end
|
63
|
+
else
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
when :literal
|
68
|
+
node[:value]
|
69
|
+
|
70
|
+
when :current
|
71
|
+
value
|
72
|
+
|
73
|
+
when :or
|
74
|
+
result = dispatch(node[:children][0], value)
|
75
|
+
if result.nil? or result.empty?
|
76
|
+
dispatch(node[:children][1], value)
|
77
|
+
else
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
when :pipe
|
82
|
+
dispatch(node[:children][1], dispatch(node[:children][0], value))
|
83
|
+
|
84
|
+
when :multi_select_list
|
85
|
+
if value.nil?
|
86
|
+
value
|
87
|
+
else
|
88
|
+
node[:children].map { |n| dispatch(n, value) }
|
89
|
+
end
|
90
|
+
|
91
|
+
when :multi_select_hash
|
92
|
+
if value.nil?
|
93
|
+
nil
|
94
|
+
else
|
95
|
+
node[:children].each.with_object({}) do |child, hash|
|
96
|
+
hash[child[:key]] = dispatch(child[:children][0], value)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
when :comparator
|
102
|
+
left = dispatch(node[:children][0], value)
|
103
|
+
right = dispatch(node[:children][1], value)
|
104
|
+
case node[:relation]
|
105
|
+
when '==' then compare_values(left, right)
|
106
|
+
when '!=' then !compare_values(left, right)
|
107
|
+
when '>' then is_int(left) && is_int(right) && left > right
|
108
|
+
when '>=' then is_int(left) && is_int(right) && left >= right
|
109
|
+
when '<' then is_int(left) && is_int(right) && left < right
|
110
|
+
when '<=' then is_int(left) && is_int(right) && left <= right
|
111
|
+
end
|
112
|
+
|
113
|
+
when :condition
|
114
|
+
true == dispatch(node[:children][0], value) ?
|
115
|
+
dispatch(node[:children][1], value) :
|
116
|
+
nil
|
117
|
+
|
118
|
+
when :function
|
119
|
+
args = node[:children].map { |child| dispatch(child, value) }
|
120
|
+
send("function_#{node[:fn]}", *args)
|
121
|
+
|
122
|
+
when :slice
|
123
|
+
function_slice(value, *node[:args])
|
124
|
+
|
125
|
+
when :expression
|
126
|
+
ExprNode.new(self, node[:children][0])
|
127
|
+
|
128
|
+
else
|
129
|
+
raise NotImplementedError
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def hash_like?(value)
|
134
|
+
Hash === value || Struct === value
|
135
|
+
end
|
136
|
+
|
137
|
+
def projection(values, node)
|
138
|
+
values.inject([]) do |list, v|
|
139
|
+
list << dispatch(node[:children][1], v)
|
140
|
+
end.compact
|
141
|
+
end
|
142
|
+
|
143
|
+
def function_abs(*args)
|
144
|
+
if args.count == 1
|
145
|
+
value = args.first
|
146
|
+
else
|
147
|
+
raise Errors::InvalidArityError, "function abs() expects one argument"
|
148
|
+
end
|
149
|
+
if Numeric === value
|
150
|
+
value.abs
|
151
|
+
else
|
152
|
+
raise Errors::InvalidTypeError, "function abs() expects a number"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def function_avg(*args)
|
157
|
+
if args.count == 1
|
158
|
+
values = args.first
|
159
|
+
else
|
160
|
+
raise Errors::InvalidArityError, "function avg() expects one argument"
|
161
|
+
end
|
162
|
+
if Array === values
|
163
|
+
values.inject(0) do |total,n|
|
164
|
+
if Numeric === n
|
165
|
+
total + n
|
166
|
+
else
|
167
|
+
raise Errors::InvalidTypeError, "function avg() expects numeric values"
|
168
|
+
end
|
169
|
+
end / values.size.to_f
|
170
|
+
else
|
171
|
+
raise Errors::InvalidTypeError, "function avg() expects a number"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def function_ceil(*args)
|
176
|
+
if args.count == 1
|
177
|
+
value = args.first
|
178
|
+
else
|
179
|
+
raise Errors::InvalidArityError, "function ceil() expects one argument"
|
180
|
+
end
|
181
|
+
if Numeric === value
|
182
|
+
value.ceil
|
183
|
+
else
|
184
|
+
raise Errors::InvalidTypeError, "function ceil() expects a numeric value"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def function_contains(*args)
|
189
|
+
if args.count == 2
|
190
|
+
if String === args[0] || Array === args[0]
|
191
|
+
args[0].include?(args[1])
|
192
|
+
else
|
193
|
+
raise Errors::InvalidTypeError, "contains expects 2nd arg to be a list"
|
194
|
+
end
|
195
|
+
else
|
196
|
+
raise Errors::InvalidArityError, "function contains() expects 2 arguments"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def function_floor(*args)
|
201
|
+
if args.count == 1
|
202
|
+
value = args.first
|
203
|
+
else
|
204
|
+
raise Errors::InvalidArityError, "function floor() expects one argument"
|
205
|
+
end
|
206
|
+
if Numeric === value
|
207
|
+
value.floor
|
208
|
+
else
|
209
|
+
raise Errors::InvalidTypeError, "function floor() expects a numeric value"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def function_length(*args)
|
214
|
+
if args.count == 1
|
215
|
+
value = args.first
|
216
|
+
else
|
217
|
+
raise Errors::InvalidArityError, "function length() expects one argument"
|
218
|
+
end
|
219
|
+
case value
|
220
|
+
when Hash, Array, String then value.size
|
221
|
+
else raise Errors::InvalidTypeError, "function length() expects string, array or object"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def function_max(*args)
|
226
|
+
if args.count == 1
|
227
|
+
values = args.first
|
228
|
+
else
|
229
|
+
raise Errors::InvalidArityError, "function max() expects one argument"
|
230
|
+
end
|
231
|
+
if Array === values
|
232
|
+
values.inject(values.first) do |max, v|
|
233
|
+
if Numeric === v
|
234
|
+
v > max ? v : max
|
235
|
+
else
|
236
|
+
raise Errors::InvalidTypeError, "function max() expects numeric values"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
else
|
240
|
+
raise Errors::InvalidTypeError, "function max() expects an array"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def function_min(*args)
|
245
|
+
if args.count == 1
|
246
|
+
values = args.first
|
247
|
+
else
|
248
|
+
raise Errors::InvalidArityError, "function min() expects one argument"
|
249
|
+
end
|
250
|
+
if Array === values
|
251
|
+
values.inject(values.first) do |min, v|
|
252
|
+
if Numeric === v
|
253
|
+
v < min ? v : min
|
254
|
+
else
|
255
|
+
raise Errors::InvalidTypeError, "function min() expects numeric values"
|
256
|
+
end
|
257
|
+
end
|
258
|
+
else
|
259
|
+
raise Errors::InvalidTypeError, "function min() expects an array"
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def function_type(*args)
|
264
|
+
if args.count == 1
|
265
|
+
get_type(args.first)
|
266
|
+
else
|
267
|
+
raise Errors::InvalidArityError, "function type() expects one argument"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def function_keys(*args)
|
272
|
+
if args.count == 1
|
273
|
+
value = args.first
|
274
|
+
if hash_like?(value)
|
275
|
+
case value
|
276
|
+
when Hash then value.keys.map(&:to_s)
|
277
|
+
when Struct then value.members.map(&:to_s)
|
278
|
+
else raise NotImplementedError
|
279
|
+
end
|
280
|
+
else
|
281
|
+
raise Errors::InvalidTypeError, "function keys() expects a hash"
|
282
|
+
end
|
283
|
+
else
|
284
|
+
raise Errors::InvalidArityError, "function keys() expects one argument"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def function_values(*args)
|
289
|
+
if args.count == 1
|
290
|
+
value = args.first
|
291
|
+
if hash_like?(value)
|
292
|
+
value.values
|
293
|
+
elsif Array === value
|
294
|
+
value
|
295
|
+
else
|
296
|
+
raise Errors::InvalidTypeError, "function values() expects an array or a hash"
|
297
|
+
end
|
298
|
+
else
|
299
|
+
raise Errors::InvalidArityError, "function values() expects one argument"
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def function_join(*args)
|
304
|
+
if args.count == 2
|
305
|
+
glue = args[0]
|
306
|
+
values = args[1]
|
307
|
+
if !(String === glue)
|
308
|
+
raise Errors::InvalidTypeError, "function join() expects the first argument to be a string"
|
309
|
+
elsif Array === values && values.all? { |v| String === v }
|
310
|
+
values.join(glue)
|
311
|
+
else
|
312
|
+
raise Errors::InvalidTypeError, "function join() expects values to be an array of strings"
|
313
|
+
end
|
314
|
+
else
|
315
|
+
raise Errors::InvalidArityError, "function join() expects an array of strings"
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def function_to_string(*args)
|
320
|
+
if args.count == 1
|
321
|
+
value = args.first
|
322
|
+
String === value ? value : MultiJson.dump(value)
|
323
|
+
else
|
324
|
+
raise Errors::InvalidArityError, "function to_string() expects one argument"
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def function_to_number(*args)
|
329
|
+
if args.count == 1
|
330
|
+
begin
|
331
|
+
value = Float(args.first)
|
332
|
+
Integer(value) === value ? value.to_i : value
|
333
|
+
rescue
|
334
|
+
nil
|
335
|
+
end
|
336
|
+
else
|
337
|
+
raise Errors::InvalidArityError, "function to_number() expects one argument"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def function_sum(*args)
|
342
|
+
if args.count == 1 && Array === args.first
|
343
|
+
args.first.inject(0) do |sum,n|
|
344
|
+
if Numeric === n
|
345
|
+
sum + n
|
346
|
+
else
|
347
|
+
raise Errors::InvalidTypeError, "function sum() expects values to be numeric"
|
348
|
+
end
|
349
|
+
end
|
350
|
+
else
|
351
|
+
raise Errors::InvalidArityError, "function sum() expects one argument"
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def function_not_null(*args)
|
356
|
+
if args.count > 0
|
357
|
+
args.find { |value| !value.nil? }
|
358
|
+
else
|
359
|
+
raise Errors::InvalidArityError, "function not_null() expects one or more arguments"
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def function_sort(*args)
|
364
|
+
if args.count == 1
|
365
|
+
value = args.first
|
366
|
+
if Array === value
|
367
|
+
value.sort do |a, b|
|
368
|
+
a_type = get_type(a)
|
369
|
+
b_type = get_type(b)
|
370
|
+
if ['string', 'number'].include?(a_type) && a_type == b_type
|
371
|
+
a <=> b
|
372
|
+
else
|
373
|
+
raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
|
374
|
+
end
|
375
|
+
end
|
376
|
+
else
|
377
|
+
raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
|
378
|
+
end
|
379
|
+
else
|
380
|
+
raise Errors::InvalidArityError, "function sort() expects one argument"
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
def function_sort_by(*args)
|
385
|
+
if args.count == 2
|
386
|
+
if get_type(args[0]) == 'array' && get_type(args[1]) == 'expression'
|
387
|
+
values = args[0]
|
388
|
+
expression = args[1]
|
389
|
+
values.sort do |a,b|
|
390
|
+
a_value = expression.interpreter.visit(expression.node, a)
|
391
|
+
b_value = expression.interpreter.visit(expression.node, b)
|
392
|
+
a_type = get_type(a_value)
|
393
|
+
b_type = get_type(b_value)
|
394
|
+
if ['string', 'number'].include?(a_type) && a_type == b_type
|
395
|
+
a_value <=> b_value
|
396
|
+
else
|
397
|
+
raise Errors::InvalidTypeError, "function sort() expects values to be an array of numbers or integers"
|
398
|
+
end
|
399
|
+
end
|
400
|
+
else
|
401
|
+
raise Errors::InvalidTypeError, "function sort_by() expects an array and an expression"
|
402
|
+
end
|
403
|
+
else
|
404
|
+
raise Errors::InvalidArityError, "function sort_by() expects two arguments"
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
def function_max_by(*args)
|
409
|
+
number_compare(:max, *args)
|
410
|
+
end
|
411
|
+
|
412
|
+
def function_min_by(*args)
|
413
|
+
number_compare(:min, *args)
|
414
|
+
end
|
415
|
+
|
416
|
+
def number_compare(mode, *args)
|
417
|
+
if args.count == 2
|
418
|
+
if get_type(args[0]) == 'array' && get_type(args[1]) == 'expression'
|
419
|
+
values = args[0]
|
420
|
+
expression = args[1]
|
421
|
+
args[0].send("#{mode}_by") do |entry|
|
422
|
+
value = expression.interpreter.visit(expression.node, entry)
|
423
|
+
if get_type(value) == 'number'
|
424
|
+
value
|
425
|
+
else
|
426
|
+
raise Errors::InvalidTypeError, "function #{mode}_by() expects values to be an numbers"
|
427
|
+
end
|
428
|
+
end
|
429
|
+
else
|
430
|
+
raise Errors::InvalidTypeError, "function #{mode}_by() expects an array and an expression"
|
431
|
+
end
|
432
|
+
else
|
433
|
+
raise Errors::InvalidArityError, "function #{mode}_by() expects two arguments"
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
def function_slice(values, *args)
|
438
|
+
if String === values || Array === values
|
439
|
+
_slice(values, *args)
|
440
|
+
else
|
441
|
+
nil
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
def _slice(values, start, stop, step)
|
446
|
+
start, stop, step = _adjust_slice(values.size, start, stop, step)
|
447
|
+
result = []
|
448
|
+
if step > 0
|
449
|
+
i = start
|
450
|
+
while i < stop
|
451
|
+
result << values[i]
|
452
|
+
i += step
|
453
|
+
end
|
454
|
+
else
|
455
|
+
i = start
|
456
|
+
while i > stop
|
457
|
+
result << values[i]
|
458
|
+
i += step
|
459
|
+
end
|
460
|
+
end
|
461
|
+
String === values ? result.join : result
|
462
|
+
end
|
463
|
+
|
464
|
+
def _adjust_slice(length, start, stop, step)
|
465
|
+
if step.nil?
|
466
|
+
step = 1
|
467
|
+
elsif step == 0
|
468
|
+
raise Errors::RuntimeError, 'slice step cannot be 0'
|
469
|
+
end
|
470
|
+
|
471
|
+
if start.nil?
|
472
|
+
start = step < 0 ? length - 1 : 0
|
473
|
+
else
|
474
|
+
start = _adjust_endpoint(length, start, step)
|
475
|
+
end
|
476
|
+
|
477
|
+
if stop.nil?
|
478
|
+
stop = step < 0 ? -1 : length
|
479
|
+
else
|
480
|
+
stop = _adjust_endpoint(length, stop, step)
|
481
|
+
end
|
482
|
+
|
483
|
+
[start, stop, step]
|
484
|
+
end
|
485
|
+
|
486
|
+
def _adjust_endpoint(length, endpoint, step)
|
487
|
+
if endpoint < 0
|
488
|
+
endpoint += length
|
489
|
+
endpoint = 0 if endpoint < 0
|
490
|
+
endpoint
|
491
|
+
elsif endpoint >= length
|
492
|
+
step < 0 ? length - 1 : length
|
493
|
+
else
|
494
|
+
endpoint
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
def compare_values(a, b)
|
499
|
+
if a == b
|
500
|
+
true
|
501
|
+
else
|
502
|
+
false
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def is_int(value)
|
507
|
+
Integer === value
|
508
|
+
end
|
509
|
+
|
510
|
+
def get_type(value)
|
511
|
+
case
|
512
|
+
when ExprNode === value then 'expression'
|
513
|
+
when String === value then 'string'
|
514
|
+
when hash_like?(value) then 'object'
|
515
|
+
when Array === value then 'array'
|
516
|
+
when [true, false].include?(value) then 'boolean'
|
517
|
+
when value.nil? then 'null'
|
518
|
+
when Numeric === value then 'number'
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
end
|
523
|
+
end
|