forked from aws/aws-sdk-ruby
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdocumenter.rb
241 lines (209 loc) · 7.71 KB
/
documenter.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
require 'erb'
module Aws
module Api
class Documenter
def initialize(svc_module, docs_path)
@svc_module = svc_module
@svc_name = svc_module.name.split('::').last
@client_class = svc_module.const_get(:Client)
Aws::Api::Docstrings.apply(@client_class, docs_path)
@api = @client_class.api
@full_name = @api.metadata('serviceFullName')
@error_names = @api.operations.map {|_,o| o.errors.map(&:name) }
@error_names = @error_names.flatten.uniq.sort
@namespace = YARD::Registry['Aws']
end
def apply
document_service
document_client
document_errors
end
private
def document_service
yard_mod = YARD::CodeObjects::ModuleObject.new(@namespace, @svc_name)
yard_mod.docstring = service_docstring
yard_mod.docstring.add_tag(YARD::Tags::Tag.new(:service, @svc_name))
@namespace = yard_mod
end
def service_docstring
path = "doc-src/services/#{@svc_name}/service.md"
path = 'doc-src/services/default/service.md' unless File.exists?(path)
template = read(path)
svc_name = @svc_name
api = @api
full_name = @full_name
ERB.new(template).result(binding)
end
def document_errors
yard_mod = YARD::CodeObjects::ModuleObject.new(@namespace, 'Errors')
yard_mod.docstring = errors_docstring
base_error = YARD::CodeObjects::ClassObject.new(yard_mod, 'ServiceError')
base_error.docstring = "Base class for all Aws::#{@svc_name} errors."
base_error.superclass = YARD::Registry['Aws::Errors::ServiceError']
@error_names.each do |error_name|
error_klass = YARD::CodeObjects::ClassObject.new(yard_mod, error_name)
error_klass.superclass = base_error
end
end
def errors_docstring
path = "doc-src/services/#{@svc_name}/errors.md"
path = 'doc-src/services/default/errors.md' unless File.exists?(path)
template = read(path)
svc_name = @svc_name
api = @api
full_name = @full_name
known_errors = @error_names
ERB.new(template).result(binding)
end
def document_client
yard_class = YARD::CodeObjects::ClassObject.new(@namespace, 'Client')
yard_class.superclass = YARD::Registry['Seahorse::Client::Base']
yard_class.docstring = client_docstring
document_client_constructor(yard_class)
document_client_operations(yard_class)
document_client_waiters(yard_class)
end
def client_docstring
path = "doc-src/services/#{@svc_name}/client.md"
path = 'doc-src/services/default/client.md' unless File.exists?(path)
render(path)
end
def render(path)
svc_name = @svc_name
api = @api
full_name = @full_name
ERB.new(File.read(path)).result(binding)
end
def document_client_constructor(namespace)
constructor = YARD::CodeObjects::MethodObject.new(namespace, :initialize)
constructor.group = 'Constructor'
constructor.scope = :instance
constructor.parameters << ['options', '{}']
constructor.docstring = client_constructor_docstring
end
def client_constructor_docstring
<<-DOCS.strip
Constructs an API client.
#{client_constructor_options}
@return [#{@client_class.name}] Returns an API client.
DOCS
end
def client_constructor_options
options = {}
@client_class.plugins.each do |plugin|
if p = YARD::Registry[plugin.name]
p.tags.each do |tag|
if tag.tag_name == 'seahorse_client_option'
option_name = tag.text.match(/.+(:\w+)/)[1]
option_text = "@option options " + tag.text.split("\n").join("\n ")
options[option_name] = option_text +
" See {#{plugin.name}} for more details."
end
end
end
end
options.sort_by { |k,v| k }.map(&:last).join("\n")
end
def document_client_operations(namespace)
@api.operations.each do |method_name, operation|
document_client_operation(namespace, method_name, operation)
end
end
def document_client_operation(namespace, method_name, operation)
m = YARD::CodeObjects::MethodObject.new(namespace, method_name)
m.group = 'Service Operations'
m.scope = :instance
m.parameters << ['params', '{}']
m.docstring = operation_docstring(method_name, operation)
end
def operation_docstring(method_name, operation)
documentor = OperationDocumenter.new(
svc_var_name: @svc_name.downcase,
method_name: method_name,
operation: operation)
tabs = Tabulator.new.tap do |t|
t.tab(method_name, 'Formatting Example') do
"<pre><code>#{documentor.example}</code></pre>"
end
t.tab(method_name, 'Request Parameters') do
documentor.input
end
t.tab(method_name, 'Response Structure') do
documentor.output
end
end
errors = (operation.errors || []).map { |shape| shape.name }
errors = errors.map { |e| "@raise [Errors::#{e}]" }.join("\n")
docstring = <<-DOCSTRING.strip
<p>Calls the #{operation.name} operation.<p>
#{documentor.api_ref(operation)}
#{tabs}
@param [Hash] params ({})
@return [PageableResponse]
#{errors}
DOCSTRING
end
def document_client_waiters(yard_class)
m = YARD::CodeObjects::MethodObject.new(yard_class, :wait_until)
m.scope = :instance
m.parameters << ['waiter_name', nil]
m.parameters << ['params', '{}']
m.docstring = YARD::Registry['Aws::ClientWaiters#wait_until'].docstring
waiters = @client_class.waiters.waiter_names.sort.inject('') do |w,name|
waiter = @client_class.waiters.waiter(name)
operation = waiter.poller.operation_name
w << "<tr><td><tt>:#{name}</tt></td><td>{##{operation}}</td><td>#{waiter.delay}</td><td>#{waiter.max_attempts}</td></tr>"
end
docstring = <<-DOCSTRING
Returns the list of supported waiters. The following table lists the supported
waiters and the client method they call:
<table>
<thead>
<tr><th>Waiter Name</th><th>Client Method</th><th>Delay</th><th>Max Attempts</th></tr>
</thead>
<tbody>
#{waiters}
</tbody>
</table>
@return [Array<Symbol>] the list of supported waiters.
DOCSTRING
m = YARD::CodeObjects::MethodObject.new(yard_class, :waiter_names)
m.scope = :instance
m.docstring = docstring
end
class Tabulator
def initialize
@tabs = []
@tab_contents = []
end
def tab(method_name, tab_name, &block)
tab_class = tab_name.downcase.gsub(/[^a-z]+/i, '-')
tab_id = "#{method_name.to_s.gsub(/_/, '-')}-#{tab_class}"
class_names = ['tab-contents', tab_class]
@tabs << [tab_id, tab_name]
@tab_contents << "<div class=\"#{class_names.join(' ')}\" id=\"#{tab_id}\">"
@tab_contents << yield
@tab_contents << '</div>'
end
def to_html
lines = []
lines << '<div class="tab-box">'
lines << '<ul class="tabs">'
@tabs.each do |tab_id, tab_name|
lines << "<li data-tab-id=\"#{tab_id}\">#{tab_name}</li>"
end
lines << '</ul>'
lines.concat(@tab_contents)
lines << '</div>'
lines.join
end
alias inspect to_html
alias to_str to_html
alias to_s to_html
end
def read(path)
File.open(path, 'r', encoding: 'UTF-8') { |f| f.read }
end
end
end
end