apipie-rails 0.2.6 → 0.3.0
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.
- data/CHANGELOG.md +33 -1
- data/README.rst +210 -137
- data/app/views/apipie/apipies/_disqus.html.erb +1 -1
- data/app/views/apipie/apipies/_params.html.erb +1 -1
- data/app/views/apipie/apipies/_params_plain.html.erb +1 -1
- data/lib/apipie/application.rb +44 -1
- data/lib/apipie/configuration.rb +22 -3
- data/lib/apipie/dsl_definition.rb +90 -47
- data/lib/apipie/errors.rb +6 -0
- data/lib/apipie/extractor.rb +1 -1
- data/lib/apipie/extractor/recorder.rb +35 -0
- data/lib/apipie/extractor/writer.rb +31 -9
- data/lib/apipie/method_description.rb +9 -3
- data/lib/apipie/routes_formatter.rb +33 -0
- data/lib/apipie/validator.rb +8 -0
- data/lib/apipie/version.rb +1 -1
- data/lib/tasks/apipie.rake +5 -3
- data/spec/controllers/users_controller_spec.rb +261 -193
- data/spec/dummy/app/controllers/application_controller.rb +8 -0
- data/spec/dummy/app/controllers/files_controller.rb +5 -0
- data/spec/dummy/app/controllers/users_controller.rb +10 -0
- data/spec/dummy/config/initializers/apipie.rb +1 -2
- data/spec/dummy/config/routes.rb +8 -1
- data/spec/lib/extractor/extractor_spec.rb +9 -0
- data/spec/lib/extractor/middleware_spec.rb +23 -0
- metadata +7 -2
@@ -3,7 +3,7 @@
|
|
3
3
|
var disqus_shortname = "<%= Apipie.configuration.disqus_shortname %>";
|
4
4
|
(function() {
|
5
5
|
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
6
|
-
dsq.src = '
|
6
|
+
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
|
7
7
|
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
|
8
8
|
})();
|
9
9
|
</script>
|
@@ -10,7 +10,7 @@
|
|
10
10
|
<%= param[:required] ? t('apipie.required') : t('apipie.optional') %>
|
11
11
|
<%= param[:allow_nil] ? ', '+t('apipie.nil_allowed') : '' %>
|
12
12
|
<% if param[:validator] %>
|
13
|
-
[ <%= param[:validator] %> ]
|
13
|
+
[ <%= Apipie.markup_to_html(param[:validator]).html_safe %> ]
|
14
14
|
<% end %>
|
15
15
|
</small>
|
16
16
|
<%= param[:description].html_safe %>
|
data/lib/apipie/application.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'apipie/static_dispatcher'
|
2
|
+
require 'apipie/routes_formatter'
|
2
3
|
require 'yaml'
|
3
4
|
require 'digest/md5'
|
4
5
|
require 'json'
|
@@ -6,7 +7,6 @@ require 'json'
|
|
6
7
|
module Apipie
|
7
8
|
|
8
9
|
class Application
|
9
|
-
|
10
10
|
# we need engine just for serving static assets
|
11
11
|
class Engine < Rails::Engine
|
12
12
|
initializer "static assets" do |app|
|
@@ -29,6 +29,49 @@ module Apipie
|
|
29
29
|
@controller_to_resource_id[controller] = resource_id
|
30
30
|
end
|
31
31
|
|
32
|
+
def rails_routes(route_set = nil)
|
33
|
+
if route_set.nil? && @rails_routes
|
34
|
+
return @rails_routes
|
35
|
+
end
|
36
|
+
route_set ||= Rails.application.routes
|
37
|
+
# ensure routes are loaded
|
38
|
+
Rails.application.reload_routes! unless Rails.application.routes.routes.any?
|
39
|
+
|
40
|
+
flatten_routes = []
|
41
|
+
|
42
|
+
route_set.routes.each do |route|
|
43
|
+
if route.app.respond_to?(:routes) && route.app.routes.is_a?(ActionDispatch::Routing::RouteSet)
|
44
|
+
# recursively go though the moutned engines
|
45
|
+
flatten_routes.concat(rails_routes(route.app.routes))
|
46
|
+
else
|
47
|
+
flatten_routes << route
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@rails_routes = flatten_routes
|
52
|
+
end
|
53
|
+
|
54
|
+
# the app might be nested when using contraints, namespaces etc.
|
55
|
+
# this method does in depth search for the route controller
|
56
|
+
def route_app_controller(app, route)
|
57
|
+
if app.respond_to?(:controller)
|
58
|
+
return app.controller(route.defaults)
|
59
|
+
elsif app.respond_to?(:app)
|
60
|
+
return route_app_controller(app.app, route)
|
61
|
+
end
|
62
|
+
rescue ActionController::RoutingError
|
63
|
+
# some errors in the routes will not stop us here: just ignoring
|
64
|
+
end
|
65
|
+
|
66
|
+
def routes_for_action(controller, method, args)
|
67
|
+
routes = rails_routes.select do |route|
|
68
|
+
controller == route_app_controller(route.app, route) &&
|
69
|
+
method.to_s == route.defaults[:action]
|
70
|
+
end
|
71
|
+
|
72
|
+
Apipie.configuration.routes_formatter.format_routes(routes, args)
|
73
|
+
end
|
74
|
+
|
32
75
|
# create new method api description
|
33
76
|
def define_method_description(controller, method_name, dsl_data)
|
34
77
|
return if ignored?(controller, method_name)
|
data/lib/apipie/configuration.rb
CHANGED
@@ -4,9 +4,10 @@ module Apipie
|
|
4
4
|
attr_accessor :app_name, :app_info, :copyright, :markup, :disqus_shortname,
|
5
5
|
:api_base_url, :doc_base_url, :required_by_default, :layout,
|
6
6
|
:default_version, :debug, :version_in_url, :namespaced_resources,
|
7
|
-
:validate, :validate_value, :validate_presence, :authenticate, :doc_path,
|
7
|
+
:validate, :validate_value, :validate_presence, :validate_key, :authenticate, :doc_path,
|
8
8
|
:show_all_examples, :process_params, :update_checksum, :checksum_path,
|
9
|
-
:link_extension, :record, :languages, :translate, :locale, :default_locale
|
9
|
+
:link_extension, :record, :languages, :translate, :locale, :default_locale,
|
10
|
+
:persist_show_in_doc
|
10
11
|
|
11
12
|
alias_method :validate?, :validate
|
12
13
|
alias_method :required_by_default?, :required_by_default
|
@@ -27,6 +28,12 @@ module Apipie
|
|
27
28
|
# Api::Engine.routes
|
28
29
|
attr_accessor :api_routes
|
29
30
|
|
31
|
+
# a object responsible for transforming the routes loaded from Rails to a form
|
32
|
+
# to be used in the documentation, when using the `api!` keyword. By default,
|
33
|
+
# it's Apipie::RoutesFormatter. To customize the behaviour, one can inherit from
|
34
|
+
# from this class and override the methods as needed.
|
35
|
+
attr_accessor :routes_formatter
|
36
|
+
|
30
37
|
def reload_controllers?
|
31
38
|
@reload_controllers = Rails.env.development? unless defined? @reload_controllers
|
32
39
|
return @reload_controllers && @api_controllers_matcher
|
@@ -42,6 +49,11 @@ module Apipie
|
|
42
49
|
end
|
43
50
|
alias_method :validate_presence?, :validate_presence
|
44
51
|
|
52
|
+
def validate_key
|
53
|
+
return (validate? && @validate_key)
|
54
|
+
end
|
55
|
+
alias_method :validate_key?, :validate_key
|
56
|
+
|
45
57
|
def process_value?
|
46
58
|
@process_params
|
47
59
|
end
|
@@ -86,6 +98,10 @@ module Apipie
|
|
86
98
|
@ignored.map(&:to_s)
|
87
99
|
end
|
88
100
|
|
101
|
+
# Persist the show_in_doc value in the examples if true. Use this if you
|
102
|
+
# cannot set the flag in the tests themselves (no rspec for example).
|
103
|
+
attr_writer :persist_show_in_doc
|
104
|
+
|
89
105
|
# comment to put before docs that was generated automatically. It's used to
|
90
106
|
# determine if the description should be overwritten next recording.
|
91
107
|
# If you want to keep the documentation (prevent from overriding), remove
|
@@ -124,9 +140,10 @@ module Apipie
|
|
124
140
|
@app_name = "Another API"
|
125
141
|
@app_info = HashWithIndifferentAccess.new
|
126
142
|
@copyright = nil
|
127
|
-
@validate =
|
143
|
+
@validate = :implicitly
|
128
144
|
@validate_value = true
|
129
145
|
@validate_presence = true
|
146
|
+
@validate_key = false
|
130
147
|
@required_by_default = false
|
131
148
|
@api_base_url = HashWithIndifferentAccess.new
|
132
149
|
@doc_base_url = "/apipie"
|
@@ -146,6 +163,8 @@ module Apipie
|
|
146
163
|
@default_locale = 'en'
|
147
164
|
@locale = lambda { |locale| @default_locale }
|
148
165
|
@translate = lambda { |str, locale| str }
|
166
|
+
@persist_show_in_doc = false
|
167
|
+
@routes_formatter = RoutesFormatter.new
|
149
168
|
end
|
150
169
|
end
|
151
170
|
end
|
@@ -20,7 +20,9 @@ module Apipie
|
|
20
20
|
|
21
21
|
def _apipie_dsl_data_init
|
22
22
|
@_apipie_dsl_data = {
|
23
|
+
:api => false,
|
23
24
|
:api_args => [],
|
25
|
+
:api_from_routes => nil,
|
24
26
|
:errors => [],
|
25
27
|
:params => [],
|
26
28
|
:resouce_id => nil,
|
@@ -72,16 +74,25 @@ module Apipie
|
|
72
74
|
Apipie.add_param_group(self, name, &block)
|
73
75
|
end
|
74
76
|
|
75
|
-
# Declare an api.
|
76
77
|
#
|
77
|
-
#
|
78
|
-
# api
|
78
|
+
# # load paths from routes and don't provide description
|
79
|
+
# api
|
79
80
|
#
|
80
81
|
def api(method, path, desc = nil, options={}) #:doc:
|
81
82
|
return unless Apipie.active_dsl?
|
83
|
+
_apipie_dsl_data[:api] = true
|
82
84
|
_apipie_dsl_data[:api_args] << [method, path, desc, options]
|
83
85
|
end
|
84
86
|
|
87
|
+
# # load paths from routes
|
88
|
+
# api! "short description",
|
89
|
+
#
|
90
|
+
def api!(desc = nil, options={}) #:doc:
|
91
|
+
return unless Apipie.active_dsl?
|
92
|
+
_apipie_dsl_data[:api] = true
|
93
|
+
_apipie_dsl_data[:api_from_routes] = { :desc => desc, :options =>options }
|
94
|
+
end
|
95
|
+
|
85
96
|
# Reference other similar method
|
86
97
|
#
|
87
98
|
# api :PUT, '/articles/:id'
|
@@ -189,44 +200,72 @@ module Apipie
|
|
189
200
|
end
|
190
201
|
|
191
202
|
def _apipie_define_validators(description)
|
192
|
-
# redefine method only if validation is turned on
|
193
|
-
if description && Apipie.configuration.validate == true
|
194
203
|
|
195
|
-
|
204
|
+
# [re]define method only if validation is turned on
|
205
|
+
if description && (Apipie.configuration.validate == true ||
|
206
|
+
Apipie.configuration.validate == :implicitly ||
|
207
|
+
Apipie.configuration.validate == :explicitly)
|
196
208
|
|
209
|
+
_apipie_save_method_params(description.method, description.params)
|
197
210
|
|
198
|
-
|
199
|
-
|
211
|
+
unless instance_methods.include?(:apipie_validations)
|
212
|
+
define_method(:apipie_validations) do
|
213
|
+
method_params = self.class._apipie_get_method_params(action_name)
|
200
214
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
215
|
+
if Apipie.configuration.validate_presence?
|
216
|
+
method_params.each do |_, param|
|
217
|
+
# check if required parameters are present
|
218
|
+
raise ParamMissing.new(param.name) if param.required && !params.has_key?(param.name)
|
219
|
+
end
|
205
220
|
end
|
206
|
-
end
|
207
221
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
222
|
+
if Apipie.configuration.validate_value?
|
223
|
+
method_params.each do |_, param|
|
224
|
+
# params validations
|
225
|
+
param.validate(params[:"#{param.name}"]) if params.has_key?(param.name)
|
226
|
+
end
|
212
227
|
end
|
213
|
-
end
|
214
228
|
|
215
|
-
|
216
|
-
|
229
|
+
# Only allow params passed in that are defined keys in the api
|
230
|
+
# Auto skip the default params (format, controller, action)
|
231
|
+
if Apipie.configuration.validate_key?
|
232
|
+
params.reject{|k,_| %w[format controller action].include?(k.to_s) }.each_key do |param|
|
233
|
+
# params allowed
|
234
|
+
raise UnknownParam.new(param) if method_params.select {|_,p| p.name.to_s == param.to_s}.empty?
|
235
|
+
end
|
236
|
+
end
|
217
237
|
|
218
|
-
|
219
|
-
|
220
|
-
|
238
|
+
if Apipie.configuration.process_value?
|
239
|
+
@api_params ||= {}
|
240
|
+
method_params.each do |_, param|
|
241
|
+
# params processing
|
242
|
+
@api_params[param.as] = param.process_value(params[:"#{param.name}"]) if params.has_key?(param.name)
|
243
|
+
end
|
221
244
|
end
|
222
245
|
end
|
246
|
+
end
|
247
|
+
|
248
|
+
if (Apipie.configuration.validate == :implicitly || Apipie.configuration.validate == true)
|
249
|
+
old_method = instance_method(description.method)
|
250
|
+
|
251
|
+
define_method(description.method) do |*args|
|
252
|
+
apipie_validations
|
223
253
|
|
224
|
-
|
225
|
-
|
254
|
+
# run the original method code
|
255
|
+
old_method.bind(self).call(*args)
|
256
|
+
end
|
226
257
|
end
|
227
258
|
|
228
259
|
end
|
260
|
+
end
|
229
261
|
|
262
|
+
def _apipie_save_method_params(method, params)
|
263
|
+
@method_params ||= {}
|
264
|
+
@method_params[method] = params
|
265
|
+
end
|
266
|
+
|
267
|
+
def _apipie_get_method_params(method)
|
268
|
+
@method_params[method]
|
230
269
|
end
|
231
270
|
|
232
271
|
end
|
@@ -335,22 +374,32 @@ module Apipie
|
|
335
374
|
# create method api and redefine newly added method
|
336
375
|
def method_added(method_name) #:doc:
|
337
376
|
super
|
377
|
+
return if !Apipie.active_dsl? || !_apipie_dsl_data[:api]
|
338
378
|
|
339
|
-
if
|
340
|
-
|
341
|
-
|
342
|
-
end
|
379
|
+
if _apipie_dsl_data[:api_from_routes]
|
380
|
+
desc = _apipie_dsl_data[:api_from_routes][:desc]
|
381
|
+
options = _apipie_dsl_data[:api_from_routes][:options]
|
343
382
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
383
|
+
api_from_routes = Apipie.routes_for_action(self, method_name, {:desc => desc, :options => options}).map do |route_info|
|
384
|
+
[route_info[:verb],
|
385
|
+
route_info[:path],
|
386
|
+
route_info[:desc],
|
387
|
+
(route_info[:options] || {}).merge(:from_routes => true)]
|
388
|
+
end
|
389
|
+
_apipie_dsl_data[:api_args].concat(api_from_routes)
|
350
390
|
end
|
351
391
|
|
392
|
+
return if _apipie_dsl_data[:api_args].blank?
|
393
|
+
|
394
|
+
# remove method description if exists and create new one
|
395
|
+
Apipie.remove_method_description(self, _apipie_dsl_data[:api_versions], method_name)
|
396
|
+
description = Apipie.define_method_description(self, method_name, _apipie_dsl_data)
|
397
|
+
|
398
|
+
_apipie_dsl_data_clear
|
352
399
|
_apipie_define_validators(description)
|
353
|
-
|
400
|
+
ensure
|
401
|
+
_apipie_dsl_data_clear
|
402
|
+
end
|
354
403
|
end
|
355
404
|
|
356
405
|
module Concern
|
@@ -381,18 +430,12 @@ module Apipie
|
|
381
430
|
def method_added(method_name) #:doc:
|
382
431
|
super
|
383
432
|
|
384
|
-
if ! Apipie.active_dsl? || _apipie_dsl_data[:
|
385
|
-
_apipie_dsl_data_clear
|
386
|
-
return
|
387
|
-
end
|
388
|
-
|
389
|
-
begin
|
390
|
-
_apipie_concern_data << [method_name, _apipie_dsl_data.merge(:from_concern => true)]
|
391
|
-
ensure
|
392
|
-
_apipie_dsl_data_clear
|
393
|
-
end
|
433
|
+
return if ! Apipie.active_dsl? || !_apipie_dsl_data[:api]
|
394
434
|
|
395
|
-
|
435
|
+
_apipie_concern_data << [method_name, _apipie_dsl_data.merge(:from_concern => true)]
|
436
|
+
ensure
|
437
|
+
_apipie_dsl_data_clear
|
438
|
+
end
|
396
439
|
|
397
440
|
end
|
398
441
|
|
data/lib/apipie/errors.rb
CHANGED
data/lib/apipie/extractor.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Apipie
|
2
2
|
module Extractor
|
3
3
|
class Recorder
|
4
|
+
MULTIPART_BOUNDARY = 'APIPIE_RECORDER_EXAMPLE_BOUNDARY'
|
5
|
+
|
4
6
|
def initialize
|
5
7
|
@ignored_params = [:controller, :action]
|
6
8
|
end
|
@@ -14,6 +16,8 @@ module Apipie
|
|
14
16
|
if data = parse_data(env["rack.input"].read)
|
15
17
|
@request_data = data
|
16
18
|
env["rack.input"].rewind
|
19
|
+
elsif form_hash = env["rack.request.form_hash"]
|
20
|
+
@request_data = reformat_multipart_data(form_hash)
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
@@ -50,6 +54,37 @@ module Apipie
|
|
50
54
|
data
|
51
55
|
end
|
52
56
|
|
57
|
+
def reformat_multipart_data(form)
|
58
|
+
form.empty? and return ''
|
59
|
+
lines = ["Content-Type: multipart/form-data; boundary=#{MULTIPART_BOUNDARY}",'']
|
60
|
+
boundary = "--#{MULTIPART_BOUNDARY}"
|
61
|
+
form.each do |key, attrs|
|
62
|
+
if attrs.is_a?(String)
|
63
|
+
lines << boundary << content_disposition(key) << "Content-Length: #{attrs.size}" << '' << attrs
|
64
|
+
else
|
65
|
+
reformat_hash(boundary, attrs, lines)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
lines << "#{boundary}--"
|
69
|
+
lines.join("\n")
|
70
|
+
end
|
71
|
+
|
72
|
+
def reformat_hash(boundary, attrs, lines)
|
73
|
+
if head = attrs[:head]
|
74
|
+
lines << boundary
|
75
|
+
lines.concat(head.split("\r\n"))
|
76
|
+
# To avoid large and/or binary file bodies, simply indicate the contents in the output.
|
77
|
+
lines << '' << %{... contents of "#{attrs[:name]}" ...}
|
78
|
+
else
|
79
|
+
# Look for subelements that contain a part.
|
80
|
+
attrs.each { |k,v| v.is_a?(Hash) and reformat_hash(boundary, v, lines) }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def content_disposition(name)
|
85
|
+
%{Content-Disposition: form-data; name="#{name}"}
|
86
|
+
end
|
87
|
+
|
53
88
|
def reformat_data(data)
|
54
89
|
parsed = parse_data(data)
|
55
90
|
case parsed
|
@@ -75,7 +75,7 @@ module Apipie
|
|
75
75
|
def ordered_call(call)
|
76
76
|
call = call.stringify_keys
|
77
77
|
ordered_call = OrderedHash.new
|
78
|
-
%w[verb path versions query request_data response_data code show_in_doc recorded].each do |k|
|
78
|
+
%w[title verb path versions query request_data response_data code show_in_doc recorded].each do |k|
|
79
79
|
next unless call.has_key?(k)
|
80
80
|
ordered_call[k] = case call[k]
|
81
81
|
when ActiveSupport::HashWithIndifferentAccess
|
@@ -97,15 +97,37 @@ module Apipie
|
|
97
97
|
merged_examples = []
|
98
98
|
(new_examples.keys + old_examples.keys).uniq.each do |key|
|
99
99
|
if new_examples.has_key?(key)
|
100
|
-
|
100
|
+
if old_examples.has_key?(key)
|
101
|
+
records = deep_merge_examples(new_examples[key], old_examples[key])
|
102
|
+
else
|
103
|
+
records = new_examples[key]
|
104
|
+
end
|
101
105
|
else
|
102
106
|
records = old_examples[key]
|
103
107
|
end
|
104
|
-
merged_examples << [key, records.map { |r|
|
108
|
+
merged_examples << [key, records.map { |r| ordered_call(r) } ]
|
105
109
|
end
|
106
110
|
return merged_examples
|
107
111
|
end
|
108
112
|
|
113
|
+
def deep_merge_examples(new_examples, old_examples)
|
114
|
+
new_examples.map do |new_example|
|
115
|
+
# Use ordered to get compareble output (mainly for the :query)
|
116
|
+
new_example_ordered = ordered_call(new_example.dup)
|
117
|
+
|
118
|
+
# Comparing verb, versions and query
|
119
|
+
if old_example = old_examples.find{ |old_example| old_example["verb"] == new_example_ordered["verb"] && old_example["versions"] == new_example_ordered["versions"] && old_example["query"] == new_example_ordered["query"]}
|
120
|
+
|
121
|
+
# Take the 'show in doc' attribute from the old example if it is present and the configuration requests the value to be persisted.
|
122
|
+
new_example[:show_in_doc] = old_example["show_in_doc"] if Apipie.configuration.persist_show_in_doc && old_example["show_in_doc"].to_i > 0
|
123
|
+
|
124
|
+
# Always take the title from the old example if it exists.
|
125
|
+
new_example[:title] ||= old_example["title"] if old_example["title"].present?
|
126
|
+
end
|
127
|
+
new_example
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
109
131
|
def load_new_examples
|
110
132
|
@collector.records.reduce({}) do |h, (method, calls)|
|
111
133
|
showed_in_versions = Set.new
|
@@ -272,8 +294,8 @@ module Apipie
|
|
272
294
|
"List #{name}"
|
273
295
|
end
|
274
296
|
|
275
|
-
code << "api :#{api[:method]},
|
276
|
-
code << ",
|
297
|
+
code << "api :#{api[:method]}, '#{api[:path]}'"
|
298
|
+
code << ", '#{desc}'" if desc
|
277
299
|
code << "\n"
|
278
300
|
end
|
279
301
|
return code
|
@@ -285,16 +307,16 @@ module Apipie
|
|
285
307
|
desc[:type] = (desc[:type] && desc[:type].first) || Object
|
286
308
|
code << "#{indent}param"
|
287
309
|
if name =~ /\W/
|
288
|
-
code << "
|
310
|
+
code << " :'#{name}'"
|
289
311
|
else
|
290
312
|
code << " :#{name}"
|
291
313
|
end
|
292
314
|
code << ", #{desc[:type].inspect}"
|
293
315
|
if desc[:allow_nil]
|
294
|
-
code << ", :
|
316
|
+
code << ", allow_nil: true"
|
295
317
|
end
|
296
318
|
if desc[:required]
|
297
|
-
code << ", :
|
319
|
+
code << ", required: true"
|
298
320
|
end
|
299
321
|
if desc[:nested]
|
300
322
|
code << " do\n"
|
@@ -310,7 +332,7 @@ module Apipie
|
|
310
332
|
def generate_errors_code(errors)
|
311
333
|
code = ""
|
312
334
|
errors.sort_by {|e| e[:code] }.each do |error|
|
313
|
-
code << "error :
|
335
|
+
code << "error code: #{error[:code]}\n"
|
314
336
|
end
|
315
337
|
code
|
316
338
|
end
|