aws-sdk-code-generator 0.1.0.pre → 0.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +5 -5
  2. data/lib/aws-sdk-code-generator.rb +68 -75
  3. data/lib/aws-sdk-code-generator/api.rb +130 -0
  4. data/lib/aws-sdk-code-generator/apply_docs.rb +15 -2
  5. data/lib/aws-sdk-code-generator/client_constructor.rb +39 -0
  6. data/lib/aws-sdk-code-generator/client_operation_documentation.rb +268 -0
  7. data/lib/aws-sdk-code-generator/client_operation_list.rb +148 -0
  8. data/lib/aws-sdk-code-generator/client_response_structure_example.rb +108 -0
  9. data/lib/aws-sdk-code-generator/code_builder.rb +146 -133
  10. data/lib/aws-sdk-code-generator/crosslink.rb +42 -0
  11. data/lib/aws-sdk-code-generator/docstring.rb +199 -0
  12. data/lib/aws-sdk-code-generator/error_list.rb +77 -0
  13. data/lib/aws-sdk-code-generator/errors.rb +2 -0
  14. data/lib/aws-sdk-code-generator/eventstream_example.rb +220 -0
  15. data/lib/aws-sdk-code-generator/gem_builder.rb +19 -25
  16. data/lib/aws-sdk-code-generator/hash_formatter.rb +5 -2
  17. data/lib/aws-sdk-code-generator/helper.rb +77 -61
  18. data/lib/aws-sdk-code-generator/plugin_list.rb +146 -0
  19. data/lib/aws-sdk-code-generator/resource_action.rb +69 -0
  20. data/lib/aws-sdk-code-generator/resource_action_code.rb +57 -0
  21. data/lib/aws-sdk-code-generator/resource_association.rb +37 -0
  22. data/lib/aws-sdk-code-generator/resource_attribute.rb +76 -0
  23. data/lib/aws-sdk-code-generator/resource_batch_action.rb +56 -0
  24. data/lib/aws-sdk-code-generator/resource_batch_action_code.rb +136 -0
  25. data/lib/aws-sdk-code-generator/resource_batch_action_documentation.rb +108 -0
  26. data/lib/aws-sdk-code-generator/resource_batch_builder.rb +212 -0
  27. data/lib/aws-sdk-code-generator/resource_builder.rb +48 -0
  28. data/lib/aws-sdk-code-generator/resource_client_request.rb +62 -0
  29. data/lib/aws-sdk-code-generator/resource_client_request_documentation.rb +81 -0
  30. data/lib/aws-sdk-code-generator/resource_client_request_params.rb +86 -0
  31. data/lib/aws-sdk-code-generator/resource_data_method.rb +60 -0
  32. data/lib/aws-sdk-code-generator/resource_has_association.rb +117 -0
  33. data/lib/aws-sdk-code-generator/resource_has_many_association.rb +52 -0
  34. data/lib/aws-sdk-code-generator/resource_has_many_association_code.rb +76 -0
  35. data/lib/aws-sdk-code-generator/resource_identifier.rb +44 -0
  36. data/lib/aws-sdk-code-generator/resource_identifiers_method.rb +29 -0
  37. data/lib/aws-sdk-code-generator/resource_load_method.rb +68 -0
  38. data/lib/aws-sdk-code-generator/resource_method.rb +22 -0
  39. data/lib/aws-sdk-code-generator/resource_skip_params.rb +36 -0
  40. data/lib/aws-sdk-code-generator/resource_value_source.rb +68 -0
  41. data/lib/aws-sdk-code-generator/resource_waiter.rb +80 -0
  42. data/lib/aws-sdk-code-generator/service.rb +30 -7
  43. data/lib/aws-sdk-code-generator/shared_example.rb +131 -0
  44. data/lib/aws-sdk-code-generator/syntax_example.rb +60 -0
  45. data/lib/aws-sdk-code-generator/syntax_example_hash.rb +174 -0
  46. data/lib/aws-sdk-code-generator/underscore.rb +10 -5
  47. data/lib/aws-sdk-code-generator/view.rb +33 -0
  48. data/lib/aws-sdk-code-generator/views.rb +2 -0
  49. data/lib/aws-sdk-code-generator/views/apig_endpoint_class.rb +25 -0
  50. data/lib/aws-sdk-code-generator/views/apig_readme.rb +32 -0
  51. data/lib/aws-sdk-code-generator/views/async_client_class.rb +68 -0
  52. data/lib/aws-sdk-code-generator/views/authorizer_class.rb +17 -0
  53. data/lib/aws-sdk-code-generator/views/client_api_module.rb +583 -0
  54. data/lib/aws-sdk-code-generator/views/client_class.rb +93 -0
  55. data/lib/aws-sdk-code-generator/views/docstring.rb +27 -0
  56. data/lib/aws-sdk-code-generator/views/errors_module.rb +32 -0
  57. data/lib/aws-sdk-code-generator/views/event_streams_module.rb +149 -0
  58. data/lib/aws-sdk-code-generator/views/features/env.rb +9 -0
  59. data/lib/aws-sdk-code-generator/views/features/smoke.rb +51 -0
  60. data/lib/aws-sdk-code-generator/views/features/smoke_step_definitions.rb +26 -0
  61. data/lib/aws-sdk-code-generator/views/features/step_definitions.rb +2 -0
  62. data/lib/aws-sdk-code-generator/views/gemspec.rb +39 -5
  63. data/lib/aws-sdk-code-generator/views/resource_class.rb +122 -0
  64. data/lib/aws-sdk-code-generator/views/root_resource_class.rb +58 -0
  65. data/lib/aws-sdk-code-generator/views/service_module.rb +30 -14
  66. data/lib/aws-sdk-code-generator/views/spec/spec_helper.rb +9 -0
  67. data/lib/aws-sdk-code-generator/views/types_module.rb +294 -0
  68. data/lib/aws-sdk-code-generator/views/version.rb +2 -0
  69. data/lib/aws-sdk-code-generator/views/waiters_module.rb +37 -0
  70. data/lib/aws-sdk-code-generator/waiter.rb +95 -0
  71. data/lib/aws-sdk-code-generator/yard_option_tag.rb +43 -0
  72. metadata +61 -68
  73. data/lib/aws-sdk-code-generator/dsl/access_control_statement.rb +0 -23
  74. data/lib/aws-sdk-code-generator/dsl/attribute_accessor.rb +0 -43
  75. data/lib/aws-sdk-code-generator/dsl/attribute_reader.rb +0 -11
  76. data/lib/aws-sdk-code-generator/dsl/attribute_writer.rb +0 -11
  77. data/lib/aws-sdk-code-generator/dsl/autoload_statement.rb +0 -15
  78. data/lib/aws-sdk-code-generator/dsl/block_param.rb +0 -11
  79. data/lib/aws-sdk-code-generator/dsl/class.rb +0 -27
  80. data/lib/aws-sdk-code-generator/dsl/code_literal.rb +0 -66
  81. data/lib/aws-sdk-code-generator/dsl/code_object.rb +0 -33
  82. data/lib/aws-sdk-code-generator/dsl/docstring.rb +0 -36
  83. data/lib/aws-sdk-code-generator/dsl/eigenclass.rb +0 -15
  84. data/lib/aws-sdk-code-generator/dsl/extend_statement.rb +0 -12
  85. data/lib/aws-sdk-code-generator/dsl/formatter.rb +0 -25
  86. data/lib/aws-sdk-code-generator/dsl/include_statement.rb +0 -17
  87. data/lib/aws-sdk-code-generator/dsl/main.rb +0 -105
  88. data/lib/aws-sdk-code-generator/dsl/method.rb +0 -108
  89. data/lib/aws-sdk-code-generator/dsl/module.rb +0 -167
  90. data/lib/aws-sdk-code-generator/dsl/option_tag.rb +0 -36
  91. data/lib/aws-sdk-code-generator/dsl/param.rb +0 -43
  92. data/lib/aws-sdk-code-generator/dsl/param_list.rb +0 -38
  93. data/lib/aws-sdk-code-generator/dsl/return_tag.rb +0 -19
  94. data/lib/aws-sdk-code-generator/dsl/tag_default.rb +0 -20
  95. data/lib/aws-sdk-code-generator/dsl/tag_docstring.rb +0 -27
  96. data/lib/aws-sdk-code-generator/dsl/tag_type.rb +0 -18
  97. data/lib/aws-sdk-code-generator/generators/client_api_module.rb +0 -334
  98. data/lib/aws-sdk-code-generator/generators/client_class.rb +0 -389
  99. data/lib/aws-sdk-code-generator/generators/client_operation_documentation.rb +0 -166
  100. data/lib/aws-sdk-code-generator/generators/errors_module.rb +0 -25
  101. data/lib/aws-sdk-code-generator/generators/resource/action.rb +0 -88
  102. data/lib/aws-sdk-code-generator/generators/resource/batch_builder.rb +0 -211
  103. data/lib/aws-sdk-code-generator/generators/resource/builder.rb +0 -50
  104. data/lib/aws-sdk-code-generator/generators/resource/client_getter.rb +0 -15
  105. data/lib/aws-sdk-code-generator/generators/resource/client_request.rb +0 -49
  106. data/lib/aws-sdk-code-generator/generators/resource/client_request_docs.rb +0 -97
  107. data/lib/aws-sdk-code-generator/generators/resource/client_request_params.rb +0 -88
  108. data/lib/aws-sdk-code-generator/generators/resource/collection_class.rb +0 -180
  109. data/lib/aws-sdk-code-generator/generators/resource/data_attribute_getter.rb +0 -24
  110. data/lib/aws-sdk-code-generator/generators/resource/data_loaded_method.rb +0 -18
  111. data/lib/aws-sdk-code-generator/generators/resource/data_method.rb +0 -49
  112. data/lib/aws-sdk-code-generator/generators/resource/exists_method.rb +0 -29
  113. data/lib/aws-sdk-code-generator/generators/resource/extract_identifier_method.rb +0 -32
  114. data/lib/aws-sdk-code-generator/generators/resource/has_association.rb +0 -101
  115. data/lib/aws-sdk-code-generator/generators/resource/has_many_association.rb +0 -108
  116. data/lib/aws-sdk-code-generator/generators/resource/identifier_getter.rb +0 -26
  117. data/lib/aws-sdk-code-generator/generators/resource/identifiers_method.rb +0 -28
  118. data/lib/aws-sdk-code-generator/generators/resource/initialize_method.rb +0 -67
  119. data/lib/aws-sdk-code-generator/generators/resource/load_method.rb +0 -65
  120. data/lib/aws-sdk-code-generator/generators/resource/value_source.rb +0 -68
  121. data/lib/aws-sdk-code-generator/generators/resource/waiter_method.rb +0 -61
  122. data/lib/aws-sdk-code-generator/generators/resource_class.rb +0 -325
  123. data/lib/aws-sdk-code-generator/generators/response_structure_example.rb +0 -83
  124. data/lib/aws-sdk-code-generator/generators/root_resource_class.rb +0 -42
  125. data/lib/aws-sdk-code-generator/generators/service_documentation.rb +0 -64
  126. data/lib/aws-sdk-code-generator/generators/shared_example.rb +0 -132
  127. data/lib/aws-sdk-code-generator/generators/structure_type_class.rb +0 -95
  128. data/lib/aws-sdk-code-generator/generators/syntax_example.rb +0 -169
  129. data/lib/aws-sdk-code-generator/generators/types_module.rb +0 -52
  130. data/lib/aws-sdk-code-generator/generators/waiter_class.rb +0 -62
  131. data/lib/aws-sdk-code-generator/generators/waiters_module.rb +0 -20
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module AwsSdkCodeGenerator
6
+ class ResourceBatchBuilder
7
+
8
+ # @option options [required, Hash] :resource
9
+ # @option options [String] :resp_var_name ('resp')
10
+ def initialize(options)
11
+ @resource = options.fetch(:resource)
12
+ @resp_var_name = options.fetch(:resp_var_name, 'resp')
13
+ verify_resource!
14
+ @context = loop_context
15
+ @indent = options.fetch(:indent, '')
16
+ end
17
+
18
+ def build
19
+ code = ''
20
+ loops = self.send(:loops)
21
+ count = loops.size
22
+ loops.each.with_index do |loop_expression, n|
23
+ i = ' ' * n
24
+ code += "#{i}#{loop_expression}\n"
25
+ if n == count - 1
26
+ code += i + ' ' + Docstring.indent("batch << #{resource_class}.new(#{constructor_args})\n", i + ' ')
27
+ end
28
+ end
29
+ (count).times do |n|
30
+ i = ' ' * (count - n - 1)
31
+ code += "#{i}end\n"
32
+ end
33
+ code.rstrip
34
+ end
35
+
36
+ private
37
+
38
+ def resource_class
39
+ @resource['type']
40
+ end
41
+
42
+ def constructor_args
43
+ hash = {}
44
+ hash.update(identifiers)
45
+ hash[:data] = data_path if @resource['path']
46
+ hash[:client] = "@client"
47
+ HashFormatter.new(wrap:false, inline:true).format(hash)
48
+ end
49
+
50
+ def identifiers
51
+ (@resource['identifiers'] || []).inject({}) do |hash, identifier|
52
+ value = relative_identifier_path(identifier)
53
+ hash[underscore(identifier['target']).to_sym] = value
54
+ hash
55
+ end
56
+ end
57
+
58
+ def data_path
59
+ relative_identifier_path({
60
+ 'source' => 'data',
61
+ 'path' => @resource['path'],
62
+ })
63
+ end
64
+
65
+ def relative_identifier_path(identifier)
66
+ path = identifier['path']
67
+ if path && path.include?('[]')
68
+ prefix = loops.last.match(/\|(.+)\|/)[1]
69
+ suffix = underscore(path[common_prefix.length..-1])
70
+ unless @context == :response
71
+ suffix = suffix.gsub(/\.\w+/) { |word| "[:#{word[1..-1]}]" }
72
+ end
73
+ suffix.length == 0 ? prefix : prefix + suffix
74
+ else
75
+ ResourceValueSource.new(identifier).to_s
76
+ end
77
+ end
78
+
79
+ def common_prefix
80
+ paths = plural_paths
81
+ if paths.empty?
82
+ ''
83
+ elsif paths.size == 1
84
+ # grab everything upto and including the final []
85
+ paths.first.match(/(.+\[\]).*?$/)[1]
86
+ else
87
+ prefix = find_prefix(paths)
88
+ prefix = prefix.sub(/\[\].+?$/, '[]')
89
+ if prefix[-2..-1] != '[]'
90
+ msg = 'response paths must have a common prefix ending in [], got :'
91
+ msg << paths.inspect
92
+ raise ArgumentError, msg
93
+ else
94
+ prefix
95
+ end
96
+ end
97
+ end
98
+
99
+ def find_prefix(paths)
100
+ prefix = ''
101
+ loop.with_index do |_, n|
102
+ return prefix if paths.empty?
103
+ letter = paths[0][n]
104
+ paths.each do |path|
105
+ return prefix if path[n].nil?
106
+ return prefix if path[n] != letter
107
+ end
108
+ prefix += letter
109
+ end
110
+ end
111
+
112
+ def loops
113
+ loop_var =
114
+ case @context
115
+ when :data then 'data'
116
+ when :options then 'options'
117
+ when :response then "#{@resp_var_name}.data."
118
+ end
119
+
120
+ used_vars = Set.new
121
+ used_vars << loop_var
122
+
123
+ parts = common_prefix.split('[]')
124
+ parts = parts.map.with_index do |part,n|
125
+ part = underscore(part)
126
+ unless @context == :response
127
+ part = part.gsub(/\w+/) { |word| "[:#{word}]" }
128
+ part = part.gsub(/\./, '')
129
+ end
130
+ part = "#{loop_var}#{part}"
131
+ loop_var = loop_letter(part, used_vars)
132
+ part = part + ".each do |#{loop_var}|"
133
+ part
134
+ end
135
+ parts
136
+ end
137
+
138
+ def loop_letter(str, used_vars)
139
+ letter = if @context == :options
140
+ str.scan(/:\w/).last[1]
141
+ else
142
+ str.split('.').last[0]
143
+ end
144
+ n = 1
145
+ var = letter
146
+ while used_vars.include?(var)
147
+ n += 1
148
+ var = "#{letter}#{n}"
149
+ end
150
+ used_vars << var
151
+ var
152
+ end
153
+
154
+ def all_plural_paths
155
+ paths = {}
156
+ @resource['identifiers'].each do |i|
157
+ if i['path'] && i['path'].include?('[]')
158
+ paths[i['source']] ||= []
159
+ paths[i['source']] << i['path']
160
+ end
161
+ end
162
+ paths
163
+ end
164
+
165
+ def plural_paths
166
+ all_plural_paths.values.first
167
+ end
168
+
169
+ def verify_resource!
170
+ verify_plural_paths!
171
+ end
172
+
173
+ def verify_plural_paths!
174
+ paths = all_plural_paths
175
+ case paths.size
176
+ when 0
177
+ msg = 'expected at least one plural identifier path, got none'
178
+ raise ArgumentError, msg
179
+ when 1
180
+ case paths.keys.first
181
+ when 'requestParameter'
182
+ when 'response'
183
+ when 'data'
184
+ else
185
+ msg = "unsupported identifier source #{paths.keys.first.inspect}"
186
+ raise ArgumentError, msg
187
+ end
188
+ else
189
+ msg = 'mixing plural source types is not supported'
190
+ raise ArgumentError, msg
191
+ end
192
+ end
193
+
194
+ def loop_context
195
+ case all_plural_paths.keys
196
+ when ['response'] then :response
197
+ when ['data'] then :data
198
+ when ['requestParameter'] then :options
199
+ else
200
+ msg = "unable to determine loop context: #{@resource.inspect}"
201
+ raise msg
202
+ end
203
+ end
204
+
205
+ def underscore(str)
206
+ str.split('.').map do |part|
207
+ Underscore.underscore(part)
208
+ end.join('.')
209
+ end
210
+
211
+ end
212
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AwsSdkCodeGenerator
4
+ class ResourceBuilder
5
+
6
+ # @option options [required, Hash] :resource
7
+ # @option options [required, Boolean] :request_made
8
+ def initialize(options = {})
9
+ @resource = options.fetch(:resource)
10
+ @request_made = options.fetch(:request_made)
11
+ end
12
+
13
+ def build
14
+ "#{resource_class}.new(#{constructor_options})"
15
+ end
16
+
17
+ private
18
+
19
+ def resource_class
20
+ @resource['type']
21
+ end
22
+
23
+ def constructor_options
24
+ options = {}
25
+ options.update(identifiers)
26
+ options[:data] = data_path if @resource['path']
27
+ options[:client] = "@client"
28
+ HashFormatter.new(wrap:false).format(options)
29
+ end
30
+
31
+ def identifiers
32
+ (@resource['identifiers'] || []).inject({}) do |hash, identifier|
33
+ value = ResourceValueSource.new(identifier).to_s
34
+ hash[Underscore.underscore(identifier['target']).to_sym] = value
35
+ hash
36
+ end
37
+ end
38
+
39
+ def data_path
40
+ if @request_made
41
+ ResourceValueSource.new('source' => 'response', 'path' => @resource['path'])
42
+ else
43
+ ResourceValueSource.new('source' => 'data', 'path' => @resource['path'])
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AwsSdkCodeGenerator
4
+ class ResourceClientRequest
5
+ class << self
6
+
7
+ # @option options [required, Hash] :request
8
+ # @option options [Boolean] :resp (false)
9
+ # @option options [Boolean] :merge (true)
10
+ def build(options)
11
+ request = options.fetch(:request)
12
+ merge = options.fetch(:merge, true)
13
+ streaming = options.fetch(:streaming, false)
14
+ params = ResourceClientRequestParams.new(params: request['params'])
15
+ parts = []
16
+ parts << request_options(params) if merge
17
+ parts << assignment(options)
18
+ parts << "@client."
19
+ parts << operation_name(request)
20
+ parts << arguments(merge, params, streaming)
21
+ parts.join
22
+ end
23
+
24
+ private
25
+
26
+ def request_options(params)
27
+ if params.empty?
28
+ ''
29
+ elsif params.simple?
30
+ "options = options.merge(#{params})\n"
31
+ else
32
+ hash = params
33
+ hash = ' ' + hash unless hash[0] == "\n"
34
+ "options = Aws::Util.deep_merge(options,#{hash})\n"
35
+ end
36
+ end
37
+
38
+ def assignment(options)
39
+ if options.fetch(:resp, false)
40
+ 'resp = '
41
+ else
42
+ ''
43
+ end
44
+ end
45
+
46
+ def operation_name(request)
47
+ Underscore.underscore(request['operation'])
48
+ end
49
+
50
+ def arguments(merge, params, streaming)
51
+ if merge
52
+ streaming ? '(options, &block)' : '(options)'
53
+ elsif params.empty?
54
+ ''
55
+ else
56
+ streaming ? "(#{params}, &block)" : "(#{params})"
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module AwsSdkCodeGenerator
6
+ class ResourceClientReqeustDocumentation
7
+
8
+ def initialize(options)
9
+ @api = options.fetch(:api)
10
+ @method_name = options.fetch(:method_name)
11
+ @receiver = options.fetch(:receiver)
12
+ @resp_var = options.fetch(:resp_var, nil)
13
+ @request = options.fetch(:request)
14
+ @shape_ref = @api['operations'][@request['operation']]['input']
15
+ @returns = options.fetch(:returns, nil)
16
+ @skip = Set.new(options.fetch(:skip, []) + ResourceSkipParams.compute(input_shape, @request))
17
+ end
18
+
19
+ def build
20
+ Docstring.join_docstrings([
21
+ request_syntax_example,
22
+ param_tag,
23
+ option_tags,
24
+ return_tag,
25
+ ], block_comment: false, separator: false)
26
+ end
27
+
28
+ private
29
+
30
+ def request_syntax_example
31
+ if @shape_ref && Api.shape(@shape_ref, @api)['members'].count - @skip.count > 0
32
+ SyntaxExample.new(
33
+ api: @api,
34
+ shape: input_shape,
35
+ method_name: @method_name,
36
+ receiver: @receiver,
37
+ resp_var: @resp_var,
38
+ skip: @skip,
39
+ ).format
40
+ else
41
+ # TODO : remove these empty examples, not really needed
42
+ <<-DOCS.rstrip
43
+ # @example Request syntax with placeholder values
44
+ #
45
+ # #{@receiver}.#{@method_name}()
46
+ DOCS
47
+ end
48
+ end
49
+
50
+ def param_tag
51
+ '# @param [Hash] options ({})'
52
+ end
53
+
54
+ def option_tags
55
+ if input_shape
56
+ input_shape['members'].map do |member_name, member_ref|
57
+ next if @skip.include?(member_name)
58
+ YardOptionTag.new(
59
+ name: Underscore.underscore(member_name),
60
+ required: input_shape.fetch('required', []).include?(member_name),
61
+ ruby_type: Api.ruby_input_type(member_ref, @api),
62
+ docstring: Docstring.html_to_markdown(Api.docstring(member_ref, @api)),
63
+ ).to_str
64
+ end
65
+ end
66
+ end
67
+
68
+ def input_shape
69
+ Api.shape(@shape_ref, @api)
70
+ end
71
+
72
+ def return_tag
73
+ if @returns
74
+ "# @return [#{@returns}]"
75
+ else
76
+ nil
77
+ end
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AwsSdkCodeGenerator
4
+ class ResourceClientRequestParams < String
5
+
6
+ include Helper
7
+
8
+ # @option options [Array] :params ([])
9
+ def initialize(options = {})
10
+ @params = options.fetch(:params, nil) || []
11
+ super(format_params)
12
+ end
13
+
14
+ def simple?
15
+ @params.all? { |p| p['target'].match(/^\w+$/) }
16
+ end
17
+
18
+ def empty?
19
+ @params.empty?
20
+ end
21
+
22
+ def size
23
+ @params.size
24
+ end
25
+
26
+ private
27
+
28
+ def format_params
29
+ hash = {}
30
+ @params.each do |param|
31
+ ParamTarget.new(param).apply(hash, ResourceValueSource.new(param))
32
+ end
33
+ formatter = HashFormatter.new(wrap:false, inline: @params.count == 1)
34
+ params = formatter.format(hash)
35
+ @params.size == 1 ? params.strip : params
36
+ end
37
+
38
+ class ParamTarget
39
+
40
+ def initialize(param)
41
+ @target = param['target']
42
+ @steps = []
43
+ @target.scan(/\w+|\[\]|\[\*\]|\[[0-9]+\]/) do |step|
44
+ case step
45
+ when /\[\d+\]/ then @steps += [:array, step[1..-2].to_i]
46
+ when /\[\*\]/ then @steps += [:array, :n]
47
+ when '[]' then @steps += [:array, -1]
48
+ else @steps += [:hash, Underscore.underscore(step).to_sym]
49
+ end
50
+ end
51
+ @steps.shift
52
+ @final = @steps.pop
53
+ end
54
+
55
+ # @return [String] target
56
+ attr_reader :target
57
+
58
+ def apply(hash, value, options = {})
59
+ resource_index = options.fetch(:resource_index, 0)
60
+ if @final == -1
61
+ build_context(hash, resource_index) << value
62
+ else
63
+ build_context(hash, resource_index)[@final] = value
64
+ end
65
+ hash
66
+ end
67
+
68
+ private
69
+
70
+ def build_context(params, n)
71
+ @steps.each_slice(2).inject(params) do |context, (key, type)|
72
+ entry = type == :array ? [] : {}
73
+ if key == -1
74
+ context << entry
75
+ entry
76
+ elsif key == :n
77
+ context[n] ||= entry
78
+ else
79
+ context[key] ||= entry
80
+ end
81
+ end
82
+ end
83
+
84
+ end
85
+ end
86
+ end