aws-sdk-code-generator 0.1.0.pre
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/lib/aws-sdk-code-generator.rb +91 -0
- data/lib/aws-sdk-code-generator/apply_docs.rb +37 -0
- data/lib/aws-sdk-code-generator/code_builder.rb +201 -0
- data/lib/aws-sdk-code-generator/dsl/access_control_statement.rb +23 -0
- data/lib/aws-sdk-code-generator/dsl/attribute_accessor.rb +43 -0
- data/lib/aws-sdk-code-generator/dsl/attribute_reader.rb +11 -0
- data/lib/aws-sdk-code-generator/dsl/attribute_writer.rb +11 -0
- data/lib/aws-sdk-code-generator/dsl/autoload_statement.rb +15 -0
- data/lib/aws-sdk-code-generator/dsl/block_param.rb +11 -0
- data/lib/aws-sdk-code-generator/dsl/class.rb +27 -0
- data/lib/aws-sdk-code-generator/dsl/code_literal.rb +66 -0
- data/lib/aws-sdk-code-generator/dsl/code_object.rb +33 -0
- data/lib/aws-sdk-code-generator/dsl/docstring.rb +36 -0
- data/lib/aws-sdk-code-generator/dsl/eigenclass.rb +15 -0
- data/lib/aws-sdk-code-generator/dsl/extend_statement.rb +12 -0
- data/lib/aws-sdk-code-generator/dsl/formatter.rb +25 -0
- data/lib/aws-sdk-code-generator/dsl/include_statement.rb +17 -0
- data/lib/aws-sdk-code-generator/dsl/main.rb +105 -0
- data/lib/aws-sdk-code-generator/dsl/method.rb +108 -0
- data/lib/aws-sdk-code-generator/dsl/module.rb +167 -0
- data/lib/aws-sdk-code-generator/dsl/option_tag.rb +36 -0
- data/lib/aws-sdk-code-generator/dsl/param.rb +43 -0
- data/lib/aws-sdk-code-generator/dsl/param_list.rb +38 -0
- data/lib/aws-sdk-code-generator/dsl/return_tag.rb +19 -0
- data/lib/aws-sdk-code-generator/dsl/tag_default.rb +20 -0
- data/lib/aws-sdk-code-generator/dsl/tag_docstring.rb +27 -0
- data/lib/aws-sdk-code-generator/dsl/tag_type.rb +18 -0
- data/lib/aws-sdk-code-generator/errors.rb +30 -0
- data/lib/aws-sdk-code-generator/gem_builder.rb +71 -0
- data/lib/aws-sdk-code-generator/generators/client_api_module.rb +334 -0
- data/lib/aws-sdk-code-generator/generators/client_class.rb +389 -0
- data/lib/aws-sdk-code-generator/generators/client_operation_documentation.rb +166 -0
- data/lib/aws-sdk-code-generator/generators/errors_module.rb +25 -0
- data/lib/aws-sdk-code-generator/generators/resource/action.rb +88 -0
- data/lib/aws-sdk-code-generator/generators/resource/batch_builder.rb +211 -0
- data/lib/aws-sdk-code-generator/generators/resource/builder.rb +50 -0
- data/lib/aws-sdk-code-generator/generators/resource/client_getter.rb +15 -0
- data/lib/aws-sdk-code-generator/generators/resource/client_request.rb +49 -0
- data/lib/aws-sdk-code-generator/generators/resource/client_request_docs.rb +97 -0
- data/lib/aws-sdk-code-generator/generators/resource/client_request_params.rb +88 -0
- data/lib/aws-sdk-code-generator/generators/resource/collection_class.rb +180 -0
- data/lib/aws-sdk-code-generator/generators/resource/data_attribute_getter.rb +24 -0
- data/lib/aws-sdk-code-generator/generators/resource/data_loaded_method.rb +18 -0
- data/lib/aws-sdk-code-generator/generators/resource/data_method.rb +49 -0
- data/lib/aws-sdk-code-generator/generators/resource/exists_method.rb +29 -0
- data/lib/aws-sdk-code-generator/generators/resource/extract_identifier_method.rb +32 -0
- data/lib/aws-sdk-code-generator/generators/resource/has_association.rb +101 -0
- data/lib/aws-sdk-code-generator/generators/resource/has_many_association.rb +108 -0
- data/lib/aws-sdk-code-generator/generators/resource/identifier_getter.rb +26 -0
- data/lib/aws-sdk-code-generator/generators/resource/identifiers_method.rb +28 -0
- data/lib/aws-sdk-code-generator/generators/resource/initialize_method.rb +67 -0
- data/lib/aws-sdk-code-generator/generators/resource/load_method.rb +65 -0
- data/lib/aws-sdk-code-generator/generators/resource/value_source.rb +68 -0
- data/lib/aws-sdk-code-generator/generators/resource/waiter_method.rb +61 -0
- data/lib/aws-sdk-code-generator/generators/resource_class.rb +325 -0
- data/lib/aws-sdk-code-generator/generators/response_structure_example.rb +83 -0
- data/lib/aws-sdk-code-generator/generators/root_resource_class.rb +42 -0
- data/lib/aws-sdk-code-generator/generators/service_documentation.rb +64 -0
- data/lib/aws-sdk-code-generator/generators/shared_example.rb +132 -0
- data/lib/aws-sdk-code-generator/generators/structure_type_class.rb +95 -0
- data/lib/aws-sdk-code-generator/generators/syntax_example.rb +169 -0
- data/lib/aws-sdk-code-generator/generators/types_module.rb +52 -0
- data/lib/aws-sdk-code-generator/generators/waiter_class.rb +62 -0
- data/lib/aws-sdk-code-generator/generators/waiters_module.rb +20 -0
- data/lib/aws-sdk-code-generator/hash_formatter.rb +122 -0
- data/lib/aws-sdk-code-generator/helper.rb +215 -0
- data/lib/aws-sdk-code-generator/service.rb +126 -0
- data/lib/aws-sdk-code-generator/underscore.rb +45 -0
- data/lib/aws-sdk-code-generator/view.rb +23 -0
- data/lib/aws-sdk-code-generator/views.rb +3 -0
- data/lib/aws-sdk-code-generator/views/features/env.rb +24 -0
- data/lib/aws-sdk-code-generator/views/features/step_definitions.rb +20 -0
- data/lib/aws-sdk-code-generator/views/gemspec.rb +41 -0
- data/lib/aws-sdk-code-generator/views/service_module.rb +85 -0
- data/lib/aws-sdk-code-generator/views/spec/spec_helper.rb +24 -0
- data/lib/aws-sdk-code-generator/views/version.rb +16 -0
- metadata +120 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
module AwsSdkCodeGenerator
|
|
2
|
+
module Generators
|
|
3
|
+
class ClientOperationDocumentation
|
|
4
|
+
|
|
5
|
+
include Helper
|
|
6
|
+
|
|
7
|
+
def self.apply(options)
|
|
8
|
+
new(options).apply(options)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @option options [required, Hash] :api
|
|
12
|
+
# @option options [required, String] :service_identifier
|
|
13
|
+
# @option options [required, String] :operation_name
|
|
14
|
+
# @option options [required, Hash] :operation
|
|
15
|
+
# @option options [Hash] :examples
|
|
16
|
+
def initialize(options)
|
|
17
|
+
@api = options.fetch(:api)
|
|
18
|
+
@service_id = options.fetch(:service_identifier)
|
|
19
|
+
@operation_name = options.fetch(:operation_name)
|
|
20
|
+
@method_name = underscore(@operation_name)
|
|
21
|
+
@operation = options.fetch(:operation)
|
|
22
|
+
@examples = options.fetch(:examples, nil) || { 'examples' => {} }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def apply(options)
|
|
26
|
+
method = options.fetch(:method)
|
|
27
|
+
method.docstring do |docstring|
|
|
28
|
+
apply_operation_docs(docstring)
|
|
29
|
+
apply_option_tags(docstring)
|
|
30
|
+
apply_return_tags(docstring)
|
|
31
|
+
apply_shared_examples(docstring)
|
|
32
|
+
apply_examples_from_disk(docstring)
|
|
33
|
+
apply_request_syntax_example(docstring)
|
|
34
|
+
apply_response_struture_example(docstring)
|
|
35
|
+
apply_overload_tag(docstring)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def apply_operation_docs(docstring)
|
|
42
|
+
docstring.append(markdown(@operation['documentation']))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def apply_option_tags(docstring)
|
|
46
|
+
# document the `:response_target` option if the response is streaming
|
|
47
|
+
if output = shape(@operation['output'])
|
|
48
|
+
if output['payload'] && output['members'][output['payload']]['streaming']
|
|
49
|
+
docstring.lines.concat(Dsl::OptionTag.new(
|
|
50
|
+
name: 'response_target',
|
|
51
|
+
type: 'String, IO',
|
|
52
|
+
param: 'params',
|
|
53
|
+
required: false,
|
|
54
|
+
docstring: 'Where to write response data, file path, or IO object.'
|
|
55
|
+
).lines)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if input_shape = shape(@operation['input'])
|
|
60
|
+
required = input_shape['required'] || []
|
|
61
|
+
input_shape['members'].each_pair do |member_name, member_ref|
|
|
62
|
+
docstring.lines.concat(Dsl::OptionTag.new(
|
|
63
|
+
name: underscore(member_name),
|
|
64
|
+
type: ruby_input_type(member_ref),
|
|
65
|
+
param: 'params',
|
|
66
|
+
required: required.include?(member_name),
|
|
67
|
+
docstring: documentation(member_ref),
|
|
68
|
+
).lines)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def apply_return_tags(docstring)
|
|
74
|
+
output_shape = shape(@operation['output'])
|
|
75
|
+
resp = '{Seahorse::Client::Response response}'
|
|
76
|
+
if output_shape && output_shape['members'].size > 0
|
|
77
|
+
type = ruby_type(@operation['output'])
|
|
78
|
+
returns = "@return [#{type}] Returns a #{resp} object which responds to "
|
|
79
|
+
returns << "the following methods:\n\n"
|
|
80
|
+
output_shape['members'].each_pair do |mname, mref|
|
|
81
|
+
mtype = ruby_type(mref).gsub(/</, '<').gsub(/>/, '>')
|
|
82
|
+
returns << " * {#{type}##{underscore(mname)} ##{mname}} => #{mtype}\n"
|
|
83
|
+
end
|
|
84
|
+
docstring.append(returns)
|
|
85
|
+
else
|
|
86
|
+
docstring.append("@return [Struct] Returns an empty #{resp}.")
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def apply_shared_examples(docstring)
|
|
91
|
+
(@examples[@operation_name] || []).size.times do |n|
|
|
92
|
+
begin
|
|
93
|
+
# TODO : known issue with an ec2 shared example that
|
|
94
|
+
# attempts to document a member that is not
|
|
95
|
+
# present in the model any longer (intentionally
|
|
96
|
+
# removed in customizations) - raises runtime
|
|
97
|
+
# error. This should be cleaned up.
|
|
98
|
+
docstring.append(
|
|
99
|
+
SharedExample.new(
|
|
100
|
+
operation_name: @operation_name,
|
|
101
|
+
api: @api,
|
|
102
|
+
examples: @examples,
|
|
103
|
+
example: n
|
|
104
|
+
).to_s
|
|
105
|
+
)
|
|
106
|
+
rescue
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def apply_shared_example(docstring, example)
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
input_comments = json_ex['comments']['input']
|
|
115
|
+
input = SharedExample.new(json_ex['input'], method_name, operation, input_comments).to_str_input
|
|
116
|
+
parts = []
|
|
117
|
+
parts << "@example Example: #{json_ex['title']}\n\n"
|
|
118
|
+
parts << " # #{json_ex['description']}\n\n"
|
|
119
|
+
parts += input.lines.map { |line| " " + line }
|
|
120
|
+
if json_ex['output']
|
|
121
|
+
output_comments = json_ex['comments']['output']
|
|
122
|
+
output = SharedExample.new(json_ex['output'], method_name, operation, output_comments).to_str_output
|
|
123
|
+
parts << "\n\n # resp.to_h outputs the following:\n"
|
|
124
|
+
parts += output.lines.map { |line| " " + line }
|
|
125
|
+
end
|
|
126
|
+
tag(parts.join)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def apply_examples_from_disk(docstring)
|
|
130
|
+
examples = File.expand_path('../../../../../../doc-src/examples', __FILE__)
|
|
131
|
+
glob = "#{examples}/#{@service_id}/client/#{@method_name}/*.rb"
|
|
132
|
+
Dir.glob(glob).map do |path|
|
|
133
|
+
title = File.basename(path).split(/\./).first
|
|
134
|
+
title = title.sub(/^\d+_/, '').gsub(/_/, ' ')
|
|
135
|
+
title = title[0].upcase + title[1..-1]
|
|
136
|
+
docstring.append("\n@example #{title}")
|
|
137
|
+
docstring.append(" " + File.read(path).lines.join(' '))
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def apply_request_syntax_example(docstring)
|
|
142
|
+
if @operation['input']
|
|
143
|
+
syntax = SyntaxExample.new(
|
|
144
|
+
struct_shape: shape(@operation['input']),
|
|
145
|
+
api: @api,
|
|
146
|
+
indent: ' '
|
|
147
|
+
).format.strip
|
|
148
|
+
docstring.append("\n@example Request syntax with placeholder values")
|
|
149
|
+
docstring.append(" resp = client.#{@method_name}(#{syntax})")
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def apply_response_struture_example(docstring)
|
|
154
|
+
output = @operation['output']
|
|
155
|
+
if output && shape(output)['members'].size > 0
|
|
156
|
+
docstring.append(ResponseStructureExample.new(shape_ref:output, api:@api).to_s)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def apply_overload_tag(docstring)
|
|
161
|
+
docstring.append("@overload #{@method_name}(params = {})")
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module AwsSdkCodeGenerator
|
|
2
|
+
module Generators
|
|
3
|
+
class ErrorsModule < Dsl::Module
|
|
4
|
+
|
|
5
|
+
include Helper
|
|
6
|
+
|
|
7
|
+
def initialize(options)
|
|
8
|
+
@api = options.fetch(:api)
|
|
9
|
+
super('Errors')
|
|
10
|
+
self.extend('Aws::Errors::DynamicErrors')
|
|
11
|
+
self.class('ResourceNotLoadable', extends: 'RuntimeError') do |c|
|
|
12
|
+
c.docstring(<<-DOCSTRING)
|
|
13
|
+
Raised when calling #load or #data on a resource class that can not be
|
|
14
|
+
loaded. This can happen when:
|
|
15
|
+
|
|
16
|
+
* A resource class has identifiers, but no data attributes.
|
|
17
|
+
* Resource data is only available when making an API call that
|
|
18
|
+
enumerates all resources of that type.
|
|
19
|
+
DOCSTRING
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module AwsSdkCodeGenerator
|
|
2
|
+
module Generators
|
|
3
|
+
module Resource
|
|
4
|
+
class Action < Dsl::Method
|
|
5
|
+
|
|
6
|
+
include Helper
|
|
7
|
+
|
|
8
|
+
# @option options [required, String] :name
|
|
9
|
+
# @option options [required, Hash] :action
|
|
10
|
+
# @option options [required, Hash] :api
|
|
11
|
+
# @option options [String] :var_name ('')
|
|
12
|
+
def initialize(options = {})
|
|
13
|
+
@api = options.fetch(:api)
|
|
14
|
+
@request = options.fetch(:action).fetch('request')
|
|
15
|
+
@resource = options.fetch(:action).fetch('resource', nil)
|
|
16
|
+
@var_name = options.fetch(:var_name, '')
|
|
17
|
+
super(underscore(options.fetch(:name)))
|
|
18
|
+
param('options', type:Hash, default:{})
|
|
19
|
+
apply_client_request_docs
|
|
20
|
+
apply_response
|
|
21
|
+
apply_return_tag
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def apply_client_request_docs
|
|
27
|
+
ClientRequestDocs.new(
|
|
28
|
+
request: @request,
|
|
29
|
+
api: @api,
|
|
30
|
+
var_name: @var_name,
|
|
31
|
+
returns: @resource ? @resource['type'].downcase : nil
|
|
32
|
+
).apply(self)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def apply_response
|
|
36
|
+
if @resource && batch?(@resource)
|
|
37
|
+
code('batch = []')
|
|
38
|
+
add(client_request)
|
|
39
|
+
code(BatchBuilder.new(resource: @resource))
|
|
40
|
+
code("#{resource_type}::Collection.new([batch], size: batch.size)")
|
|
41
|
+
elsif @resource
|
|
42
|
+
add(client_request)
|
|
43
|
+
code(Builder.new(resource: @resource, request_made: true))
|
|
44
|
+
else
|
|
45
|
+
add(client_request)
|
|
46
|
+
code('resp.data')
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def client_request
|
|
51
|
+
ClientRequest.new(
|
|
52
|
+
request: @request,
|
|
53
|
+
resp: true
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def apply_return_tag
|
|
58
|
+
if @resource && batch?(@resource)
|
|
59
|
+
returns("#{resource_type}::Collection")
|
|
60
|
+
elsif @resource
|
|
61
|
+
returns(resource_type)
|
|
62
|
+
else
|
|
63
|
+
returns(request_return_type)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def resource_type
|
|
68
|
+
@resource['type']
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def request_return_type
|
|
72
|
+
operation = @api['operations'][@request['operation']]
|
|
73
|
+
if operation['output']
|
|
74
|
+
"Types::#{operation['output']['shape']}"
|
|
75
|
+
else
|
|
76
|
+
'EmptyStructure'
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def batch?(resource)
|
|
81
|
+
paths = (@resource['identifiers'] || []).map {|i| i['path'] }
|
|
82
|
+
paths << @resource['path']
|
|
83
|
+
paths.compact.any? { |path| path.match(/\[/) }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
module AwsSdkCodeGenerator
|
|
4
|
+
module Generators
|
|
5
|
+
module Resource
|
|
6
|
+
class BatchBuilder < Dsl::CodeLiteral
|
|
7
|
+
|
|
8
|
+
include Helper
|
|
9
|
+
|
|
10
|
+
# @option options [required, Hash] :resource
|
|
11
|
+
# @option options [String] :resp_var_name ('resp')
|
|
12
|
+
def initialize(options)
|
|
13
|
+
@resource = options.fetch(:resource)
|
|
14
|
+
@resp_var_name = options.fetch(:resp_var_name, 'resp')
|
|
15
|
+
verify_resource!
|
|
16
|
+
@context = loop_context
|
|
17
|
+
super()
|
|
18
|
+
|
|
19
|
+
# create nested blocks from each loop expression
|
|
20
|
+
stack = [self]
|
|
21
|
+
loops.each do |loop_expression|
|
|
22
|
+
stack.last.append(loop_expression)
|
|
23
|
+
stack << stack.last.indent
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# add resource to the batch
|
|
27
|
+
stack.last.append("batch << #{resource_class}.new(#{constructor_args})")
|
|
28
|
+
|
|
29
|
+
# add closing end tags
|
|
30
|
+
stack.pop
|
|
31
|
+
stack.each { |c| c.append('end') }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def resource_class
|
|
37
|
+
@resource['type']
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def constructor_args
|
|
41
|
+
hash = {}
|
|
42
|
+
hash.update(identifiers)
|
|
43
|
+
hash[:data] = data_path if @resource['path']
|
|
44
|
+
hash[:client] = "@client"
|
|
45
|
+
HashFormatter.new(wrap:false, inline:true).format(hash)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def identifiers
|
|
49
|
+
(@resource['identifiers'] || []).inject({}) do |hash, identifier|
|
|
50
|
+
value = relative_identifier_path(identifier)
|
|
51
|
+
hash[underscore(identifier['target']).to_sym] = value
|
|
52
|
+
hash
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def data_path
|
|
57
|
+
relative_identifier_path({
|
|
58
|
+
'source' => 'data',
|
|
59
|
+
'path' => @resource['path'],
|
|
60
|
+
})
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def relative_identifier_path(identifier)
|
|
64
|
+
path = identifier['path']
|
|
65
|
+
if path && path.include?('[]')
|
|
66
|
+
prefix = loops.last.match(/\|(.+)\|/)[1]
|
|
67
|
+
suffix = underscore(path[common_prefix.length..-1])
|
|
68
|
+
if @context == :options
|
|
69
|
+
suffix = suffix.gsub(/\.\w+/) { |word| "[:#{word[1..-1]}]" }
|
|
70
|
+
end
|
|
71
|
+
suffix.length == 0 ? prefix : prefix + suffix
|
|
72
|
+
else
|
|
73
|
+
ValueSource.new(identifier).to_s
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def common_prefix
|
|
78
|
+
paths = plural_paths
|
|
79
|
+
if paths.empty?
|
|
80
|
+
''
|
|
81
|
+
elsif paths.size == 1
|
|
82
|
+
# grab everything upto and including the final []
|
|
83
|
+
paths.first.match(/(.+\[\]).*?$/)[1]
|
|
84
|
+
else
|
|
85
|
+
prefix = find_prefix(paths)
|
|
86
|
+
prefix = prefix.sub(/\[\].+?$/, '[]')
|
|
87
|
+
if prefix[-2..-1] != '[]'
|
|
88
|
+
msg = 'response paths must have a common prefix ending in [], got :'
|
|
89
|
+
msg << paths.inspect
|
|
90
|
+
raise ArgumentError, msg
|
|
91
|
+
else
|
|
92
|
+
prefix
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def find_prefix(paths)
|
|
98
|
+
prefix = ''
|
|
99
|
+
loop.with_index do |_, n|
|
|
100
|
+
return prefix if paths.empty?
|
|
101
|
+
letter = paths[0][n]
|
|
102
|
+
paths.each do |path|
|
|
103
|
+
return prefix if path[n].nil?
|
|
104
|
+
return prefix if path[n] != letter
|
|
105
|
+
end
|
|
106
|
+
prefix += letter
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def loops
|
|
111
|
+
loop_var =
|
|
112
|
+
case @context
|
|
113
|
+
when :data then 'data.'
|
|
114
|
+
when :options then 'options'
|
|
115
|
+
when :response then "#{@resp_var_name}.data."
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
used_vars = Set.new
|
|
119
|
+
used_vars << loop_var
|
|
120
|
+
|
|
121
|
+
parts = common_prefix.split('[]')
|
|
122
|
+
parts = parts.map.with_index do |part,n|
|
|
123
|
+
part = underscore(part)
|
|
124
|
+
if @context == :options
|
|
125
|
+
part = part.gsub(/\w+/) { |word| "[:#{word}]" }
|
|
126
|
+
part = part.gsub(/\./, '')
|
|
127
|
+
end
|
|
128
|
+
part = "#{loop_var}#{part}"
|
|
129
|
+
loop_var = loop_letter(part, used_vars)
|
|
130
|
+
part = part + ".each do |#{loop_var}|"
|
|
131
|
+
part
|
|
132
|
+
end
|
|
133
|
+
parts
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def loop_letter(str, used_vars)
|
|
137
|
+
letter = if @context == :options
|
|
138
|
+
str.scan(/:\w/).last[1]
|
|
139
|
+
else
|
|
140
|
+
str.split('.').last[0]
|
|
141
|
+
end
|
|
142
|
+
n = 1
|
|
143
|
+
var = letter
|
|
144
|
+
while used_vars.include?(var)
|
|
145
|
+
n += 1
|
|
146
|
+
var = "#{letter}#{n}"
|
|
147
|
+
end
|
|
148
|
+
used_vars << var
|
|
149
|
+
var
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def all_plural_paths
|
|
153
|
+
paths = {}
|
|
154
|
+
@resource['identifiers'].each do |i|
|
|
155
|
+
if i['path'] && i['path'].include?('[]')
|
|
156
|
+
paths[i['source']] ||= []
|
|
157
|
+
paths[i['source']] << i['path']
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
#if @resource['path'] && @resource['path'].include?('[]')
|
|
161
|
+
# type = @context == :data ? 'data' : 'response'
|
|
162
|
+
# paths[type] ||= []
|
|
163
|
+
# paths[type] << @resource['path']
|
|
164
|
+
#end
|
|
165
|
+
paths
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def plural_paths
|
|
169
|
+
all_plural_paths.values.first
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def verify_resource!
|
|
173
|
+
verify_plural_paths!
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def verify_plural_paths!
|
|
177
|
+
paths = all_plural_paths
|
|
178
|
+
case paths.size
|
|
179
|
+
when 0
|
|
180
|
+
msg = 'expected at least one plural identifier path, got none'
|
|
181
|
+
raise ArgumentError, msg
|
|
182
|
+
when 1
|
|
183
|
+
case paths.keys.first
|
|
184
|
+
when 'requestParameter'
|
|
185
|
+
when 'response'
|
|
186
|
+
when 'data'
|
|
187
|
+
else
|
|
188
|
+
msg = "unsupported identifier source #{paths.keys.first.inspect}"
|
|
189
|
+
raise ArgumentError, msg
|
|
190
|
+
end
|
|
191
|
+
else
|
|
192
|
+
msg = 'mixing plural source types is not supported'
|
|
193
|
+
raise ArgumentError, msg
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def loop_context
|
|
198
|
+
case all_plural_paths.keys
|
|
199
|
+
when ['response'] then :response
|
|
200
|
+
when ['data'] then :data
|
|
201
|
+
when ['requestParameter'] then :options
|
|
202
|
+
else
|
|
203
|
+
msg = "unable to determine loop context: #{@resource.inspect}"
|
|
204
|
+
raise msg
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|