swagger-doc-rails 1.0.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.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/Appraisals +7 -0
  4. data/CHANGELOG.md +120 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +448 -0
  8. data/Rakefile +10 -0
  9. data/certs/gem-public_cert.pem +21 -0
  10. data/lib/swagger/docs.rb +14 -0
  11. data/lib/swagger/docs/api_declaration_file.rb +125 -0
  12. data/lib/swagger/docs/api_declaration_file_metadata.rb +22 -0
  13. data/lib/swagger/docs/config.rb +63 -0
  14. data/lib/swagger/docs/dsl.rb +119 -0
  15. data/lib/swagger/docs/generator.rb +327 -0
  16. data/lib/swagger/docs/impotent_methods.rb +24 -0
  17. data/lib/swagger/docs/methods.rb +52 -0
  18. data/lib/swagger/docs/task.rb +9 -0
  19. data/lib/swagger/docs/version.rb +5 -0
  20. data/lib/tasks/swagger.rake +10 -0
  21. data/spec/fixtures/controllers/application_controller.rb +4 -0
  22. data/spec/fixtures/controllers/custom_resource_path_controller.rb +18 -0
  23. data/spec/fixtures/controllers/ignored_controller.rb +6 -0
  24. data/spec/fixtures/controllers/multiple_routes_controller.rb +13 -0
  25. data/spec/fixtures/controllers/nested_controller.rb +18 -0
  26. data/spec/fixtures/controllers/sample_controller.rb +81 -0
  27. data/spec/lib/swagger/docs/api_declaration_file_metadata_spec.rb +48 -0
  28. data/spec/lib/swagger/docs/api_declaration_file_spec.rb +203 -0
  29. data/spec/lib/swagger/docs/config_spec.rb +48 -0
  30. data/spec/lib/swagger/docs/dsl_spec.rb +17 -0
  31. data/spec/lib/swagger/docs/generator_spec.rb +451 -0
  32. data/spec/lib/swagger/docs/methods.rb +15 -0
  33. data/spec/spec_helper.rb +56 -0
  34. data/swagger-doc-rails.gemspec +31 -0
  35. metadata +175 -0
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "rubygems"
2
+ require 'appraisal'
3
+ require "bundler/setup"
4
+ require "bundler/gem_tasks"
5
+
6
+ require 'rspec/core/rake_task'
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+ task :default => :spec
10
+ task :test => :spec
@@ -0,0 +1,21 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRMwEQYDVQQDDApyaWNo
3
+ aG9sbGlzMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZFgNj
4
+ b20wHhcNMTMxMDIyMTMwMzI3WhcNMTQxMDIyMTMwMzI3WjBBMRMwEQYDVQQDDApy
5
+ aWNoaG9sbGlzMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
6
+ FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDppQTU++yinAuC
7
+ ydu87c/vDGTmE5Go9/zI48/T0kTco+JbUn4BPUaK0DWCEpZULvqwQAqVQm8JQnIU
8
+ 6Z3k1tAQbhtgbG2oWNIxyC7SyXMQw/ag5qoAhw6k3DFE+jGKrREzADFb7vG+nPYp
9
+ 4yinx27jCTIAv7/z2AVt6HoHOYh1s0HniJQWCebi7QgNXboMY8MpFxSwNkcFjl14
10
+ KMSf9SX7iOyiwqgcJmN0fN4be8pH5j/EdinUL1rWlwldcUo2+6LChBswRPmtdaZv
11
+ UyICuX5VfVJA0KrA/ihIMLaZVO5esFso+YrpP+QgbvhLwhn5e/sB5dr3a+y0+GJZ
12
+ zPGtm60bAgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
13
+ BBShIiKLL1E1JG++RUVAOSPO7rZV0TAfBgNVHREEGDAWgRRyaWNoaG9sbGlzQGdt
14
+ YWlsLmNvbTAfBgNVHRIEGDAWgRRyaWNoaG9sbGlzQGdtYWlsLmNvbTANBgkqhkiG
15
+ 9w0BAQUFAAOCAQEAe4P1TzJlVhUn60Wx/431wNnuHZS9K4gSzmNr4zuZU6lP3rxx
16
+ rMsSY1nJY1nTBqX9W62hO+KS14ncbZvNU59ao5YVXHDflEB3Yz20DP9E2Uws64Bx
17
+ ify0Dwuq4VV2PiQbczuTGhGupzQpkMttWNZqVdjDbH5k8sGx3MumNX7YUJwUerhZ
18
+ bTBme5soNyJzAeWBqCBPT9p98rC6vqhcBfAVF6RbERYL6MPyoBZWqGeuMR4H2X/v
19
+ RYcsqDfanYBx7QcftOnbeQq7/Ep7Zx+W9+Ph3TiJLMLdAr7bLkgN1SjvrjTL5mQR
20
+ FuQtYvE4LKiUQpG7vLTRB78dQBlSj9fnv2OM9w==
21
+ -----END CERTIFICATE-----
@@ -0,0 +1,14 @@
1
+ require "swagger/docs/config"
2
+ require "swagger/docs/dsl"
3
+ require "swagger/docs/api_declaration_file_metadata"
4
+ require "swagger/docs/api_declaration_file"
5
+ require "swagger/docs/generator"
6
+ require "swagger/docs/impotent_methods"
7
+ require "swagger/docs/methods"
8
+ require "swagger/docs/task"
9
+ require "swagger/docs/version"
10
+
11
+ module Swagger
12
+ module Docs
13
+ end
14
+ end
@@ -0,0 +1,125 @@
1
+ module Swagger
2
+ module Docs
3
+ class ApiDeclarationFile
4
+ attr_reader :metadata, :apis
5
+
6
+ def initialize(metadata, apis, models)
7
+ @metadata = metadata
8
+ @apis = camelize_keys_deep apis
9
+ @models = models
10
+ end
11
+
12
+ def generate_resource
13
+ resource = build_resource_root_hash
14
+ # Add the already-normalized models to the resource.
15
+ resource = resource.merge({:models => models}) if models.present?
16
+ resource
17
+ end
18
+
19
+ def base_path
20
+ metadata.base_path
21
+ end
22
+
23
+ def path
24
+ metadata.path
25
+ end
26
+
27
+ def swagger_version
28
+ metadata.swagger_version
29
+ end
30
+
31
+ def api_version
32
+ metadata.api_version
33
+ end
34
+
35
+ def controller_base_path
36
+ metadata.controller_base_path
37
+ end
38
+
39
+ def camelize_model_properties
40
+ metadata.camelize_model_properties
41
+ end
42
+
43
+ def resource_path
44
+ metadata.overridden_resource_path || demod
45
+ end
46
+
47
+ def resource_file_path
48
+ trim_leading_slash(debased_path.to_s.underscore)
49
+ end
50
+
51
+ def models
52
+ normalize_model_properties @models
53
+ end
54
+
55
+ def authorizations
56
+ metadata.authorizations
57
+ end
58
+
59
+ private
60
+
61
+ def build_resource_root_hash
62
+ {
63
+ "apiVersion" => api_version,
64
+ "swaggerVersion" => swagger_version,
65
+ "basePath" => base_path,
66
+ "resourcePath" => resource_path,
67
+ "apis" => apis,
68
+ "resourceFilePath" => resource_file_path,
69
+ "authorizations" => authorizations
70
+ }
71
+ end
72
+
73
+ def normalize_model_properties(models)
74
+ Hash[
75
+ models.map do |k, v|
76
+ if camelize_model_properties
77
+ [k.to_s, camelize_keys_deep(v)]
78
+ else
79
+ [k.to_s, stringify_keys_deep(v)]
80
+ end
81
+ end]
82
+ end
83
+
84
+ def demod
85
+ "#{debased_path.to_s.camelize}".demodulize.camelize.underscore
86
+ end
87
+
88
+ def debased_path
89
+ path.gsub("#{controller_base_path}", "")
90
+ end
91
+
92
+ def trim_leading_slash(str)
93
+ return str if !str
94
+ str.gsub(/\A\/+/, '')
95
+ end
96
+
97
+ def camelize_keys_deep(obj)
98
+ process_keys_deep(obj){|key| key.to_s.camelize(:lower)}
99
+ end
100
+
101
+ def stringify_keys_deep(obj)
102
+ process_keys_deep(obj){|key| key.to_s}
103
+ end
104
+
105
+ def process_keys_deep(obj, &block)
106
+ if obj.is_a? Hash
107
+ Hash[
108
+ obj.map do |k, v|
109
+ new_key = block.call(k)
110
+ new_value = process_keys_deep v, &block
111
+ [new_key, new_value]
112
+ end
113
+ ]
114
+ elsif obj.is_a? Array
115
+ new_value = obj.collect do |a|
116
+ process_keys_deep a, &block
117
+ end
118
+ else
119
+ obj
120
+ end
121
+ end
122
+
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,22 @@
1
+ module Swagger
2
+ module Docs
3
+ class ApiDeclarationFileMetadata
4
+ DEFAULT_SWAGGER_VERSION = "1.2"
5
+ DEFAULT_RESOURCE_PATH = nil
6
+
7
+ attr_reader :api_version, :path, :base_path, :controller_base_path, :swagger_version, :camelize_model_properties
8
+ attr_reader :authorizations, :overridden_resource_path
9
+
10
+ def initialize(api_version, path, base_path, controller_base_path, options={})
11
+ @api_version = api_version
12
+ @path = path
13
+ @base_path = base_path
14
+ @controller_base_path = controller_base_path
15
+ @swagger_version = options.fetch(:swagger_version, DEFAULT_SWAGGER_VERSION)
16
+ @camelize_model_properties = options.fetch(:camelize_model_properties, true)
17
+ @authorizations = options.fetch(:authorizations, {})
18
+ @overridden_resource_path = options.fetch(:resource_path, DEFAULT_RESOURCE_PATH)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,63 @@
1
+ module Swagger
2
+ module Docs
3
+ class Config
4
+ class << self
5
+ @@base_api_controller = nil
6
+
7
+ def base_api_controller
8
+ @@base_api_controller || ActionController::Base
9
+ end
10
+
11
+ def base_api_controllers
12
+ Array(base_api_controller)
13
+ end
14
+
15
+ def base_api_controller=(controller)
16
+ @@base_api_controller = controller
17
+ end
18
+
19
+ alias_method :base_api_controllers=, :base_api_controller=
20
+
21
+ def base_applications
22
+ Array(base_application)
23
+ end
24
+
25
+ def base_application
26
+ Rails.application
27
+ end
28
+
29
+ def register_apis(versions)
30
+ base_api_controllers.each do |controller|
31
+ controller.send(:include, ImpotentMethods)
32
+ end
33
+ @versions = versions
34
+ end
35
+
36
+ def registered_apis
37
+ @versions ||= {}
38
+ end
39
+
40
+ def transform_path(path, api_version)
41
+ # This is only for overriding, so don't perform any path transformations by default.
42
+ path
43
+ end
44
+
45
+ def log_exception
46
+ yield
47
+ rescue => e
48
+ write_log(:error, e)
49
+ raise
50
+ end
51
+
52
+ def log_env_name
53
+ 'SD_LOG_LEVEL'
54
+ end
55
+
56
+ def write_log(type, output)
57
+ $stderr.puts output if type == :error and ENV[log_env_name]=="1"
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,119 @@
1
+ module Swagger
2
+ module Docs
3
+ class SwaggerDSL
4
+ # http://stackoverflow.com/questions/5851127/change-the-context-binding-inside-a-block-in-ruby/5851325#5851325
5
+ def self.call(action, caller, &block)
6
+ # Create a new SwaggerDSL instance, and instance_eval the block to it
7
+ instance = new
8
+ instance.instance_eval(&block)
9
+ # Now return all of the set instance variables as a Hash
10
+ instance.instance_variables.inject({}) { |result_hash, instance_variable|
11
+ result_hash[instance_variable] = instance.instance_variable_get(instance_variable)
12
+ result_hash # Gotta have the block return the result_hash
13
+ }
14
+ end
15
+
16
+ def summary(text)
17
+ @summary = text
18
+ end
19
+
20
+ def notes(text)
21
+ @notes = text
22
+ end
23
+
24
+ def method(method)
25
+ @method = method
26
+ end
27
+
28
+ def type(type)
29
+ @type = type
30
+ end
31
+
32
+ def items(items)
33
+ @items = items
34
+ end
35
+
36
+ def consumes(mime_types)
37
+ @consumes = mime_types
38
+ end
39
+
40
+ def nickname(nickname)
41
+ @nickname = nickname
42
+ end
43
+
44
+ def parameters
45
+ @parameters ||= []
46
+ end
47
+
48
+ def param(param_type, name, type, required, description = nil, hash={})
49
+ parameters << {:param_type => param_type, :name => name, :type => type,
50
+ :description => description, :required => required == :required}.merge(hash)
51
+ end
52
+
53
+ # helper method to generate enums
54
+ def param_list(param_type, name, type, required, description = nil, allowed_values = [], hash = {})
55
+ hash.merge!({allowable_values: {value_type: "LIST", values: allowed_values}})
56
+ param(param_type, name, type, required, description, hash)
57
+ end
58
+
59
+ def response_messages
60
+ @response_messages ||= []
61
+ end
62
+
63
+ def response(status, text = nil, model = nil)
64
+ if status.is_a? Symbol
65
+ status = :ok if status == :success
66
+ status_code = Rack::Utils.status_code(status)
67
+ response_messages << {:code => status_code, :responseModel => model, :message => text || status.to_s.titleize}
68
+ else
69
+ response_messages << {:code => status, :responseModel => model, :message => text}
70
+ end
71
+ response_messages.sort_by!{|i| i[:code]}
72
+ end
73
+ end
74
+
75
+ class SwaggerModelDSL
76
+ attr_accessor :id
77
+
78
+ # http://stackoverflow.com/questions/5851127/change-the-context-binding-inside-a-block-in-ruby/5851325#5851325
79
+ def self.call(model_name, caller, &block)
80
+ # Create a new SwaggerModelDSL instance, and instance_eval the block to it
81
+ instance = new
82
+ instance.instance_eval(&block)
83
+ instance.id = model_name
84
+ # Now return all of the set instance variables as a Hash
85
+ instance.instance_variables.inject({}) { |result_hash, instance_var_name|
86
+ key = instance_var_name[1..-1].to_sym # Strip prefixed @ sign.
87
+ result_hash[key] = instance.instance_variable_get(instance_var_name)
88
+ result_hash # Gotta have the block return the result_hash
89
+ }
90
+ end
91
+
92
+ def properties
93
+ @properties ||= {}
94
+ end
95
+
96
+ def required
97
+ @required ||= []
98
+ end
99
+
100
+ def description(description)
101
+ @description = description
102
+ end
103
+
104
+ def property(name, type, required, description = nil, hash={})
105
+ properties[name] = {
106
+ type: type,
107
+ description: description,
108
+ }.merge!(hash)
109
+ self.required << name if required == :required
110
+ end
111
+
112
+ # helper method to generate enums
113
+ def property_list(name, type, required, description = nil, allowed_values = [], hash = {})
114
+ hash.merge!({allowable_values: {value_type: "LIST", values: allowed_values}})
115
+ property(name, type, required, description, hash)
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,327 @@
1
+ module Swagger
2
+ module Docs
3
+ class Generator
4
+
5
+ DEFAULT_VER = "1.0"
6
+ DEFAULT_CONFIG = {
7
+ :api_file_path => "public/",
8
+ :api_file_name => "api-docs.json",
9
+ :base_path => "/",
10
+ :clean_directory => false,
11
+ :formatting => :pretty
12
+ }
13
+
14
+ class << self
15
+
16
+ def set_real_methods
17
+ # replace impotent methods with live ones
18
+ Config.base_api_controllers.each do |controller|
19
+ controller.send(:include, Methods)
20
+ end
21
+ end
22
+
23
+ def write_docs(apis = nil)
24
+ results = generate_docs(apis)
25
+ results.each{|api_version, result| write_doc(result) }
26
+ end
27
+
28
+ def write_doc(result)
29
+ settings = result[:settings]
30
+ config = result[:config]
31
+ create_output_paths(settings[:api_file_path])
32
+ clean_output_paths(settings[:api_file_path]) if config[:clean_directory] || false
33
+ root = result[:root]
34
+ resources = root.delete 'resources'
35
+ root.merge!(config[:attributes] || {}) # merge custom user attributes like info
36
+ # write the api-docs file
37
+ write_to_file("#{settings[:api_file_path]}/#{config[:api_file_name]}", root, config)
38
+ # write the individual resource files
39
+ resources.each do |resource|
40
+ resource_file_path = resource.delete 'resourceFilePath'
41
+ write_to_file(File.join(settings[:api_file_path], "#{resource_file_path}.json"), resource, config)
42
+ end
43
+ result
44
+ end
45
+
46
+ def generate_docs(apis=nil)
47
+ apis ||= Config.registered_apis
48
+ results = {}
49
+ set_real_methods
50
+
51
+ apis[DEFAULT_VER] = DEFAULT_CONFIG if apis.empty?
52
+
53
+ apis.each do |api_version, config|
54
+ settings = get_settings(api_version, config)
55
+ config.reverse_merge!(DEFAULT_CONFIG)
56
+ results[api_version] = generate_doc(api_version, settings, config)
57
+ results[api_version][:settings] = settings
58
+ results[api_version][:config] = config
59
+ end
60
+ results
61
+ end
62
+
63
+ def generate_doc(api_version, settings, config)
64
+ root = {
65
+ "swagger" => "2.0",
66
+ # "info" => settings[:info],
67
+ "host" => settings[:host],
68
+ "basePath" => settings[:base_path],
69
+ "schemes" => [
70
+ "http"
71
+ ],
72
+
73
+ :paths => {},
74
+ }
75
+
76
+ results = {:processed => [], :skipped => []}
77
+ resources = []
78
+
79
+ get_route_paths(settings[:controller_base_path]).each do |path|
80
+ ret = process_path(path, root, config, settings)
81
+ results[ret[:action]] << ret
82
+
83
+ if ret[:action] == :processed
84
+ resources << generate_resource(ret[:path], ret[:apis], ret[:models], settings, root, config, ret[:klass].swagger_config)
85
+ debased_path = get_debased_path(ret[:path], settings[:controller_base_path])
86
+
87
+ ret[:apis] && ret[:apis].each do |rea|
88
+ name = rea[:path].gsub('.json', '')
89
+ name += '/' if root[:paths].has_key?(name)
90
+
91
+ api_content = rea[:operations][0]
92
+ description = ret[:klass].swagger_config[:description]
93
+
94
+ resource_api = {
95
+ # path: "/#{Config.transform_path(trim_leading_slash(debased_path), api_version)}.{format}",
96
+ "#{name}" => {
97
+ "#{api_content[:method]}" => {
98
+ "summary" => api_content[:summary],
99
+ "description" => description,
100
+ "parameters" => api_content[:parameters].map{|pr| {
101
+ "name" => pr[:name],
102
+ "in" => pr[:param_type],
103
+ "required" => pr[:required] ? true : false,
104
+ "description" => pr[:description],
105
+ "type" => pr[:type]
106
+ }
107
+ },
108
+ "responses" => {
109
+ "200" => {
110
+ "description" => description,
111
+ "schema" => {
112
+ # "type": "array",
113
+ # "items": {
114
+ # "properties": {
115
+ # "firstName": {
116
+ # "type": "string"
117
+ # },
118
+ # "lastName": {
119
+ # "type": "string"
120
+ # },
121
+ # "username": {
122
+ # "type": "string"
123
+ # }
124
+ # }
125
+ # }
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }.as_json
132
+ # root[:apis] << resource_api
133
+ root[:paths].merge!(resource_api)
134
+ end
135
+
136
+ end
137
+ end
138
+ root['resources'] = resources
139
+ results[:root] = root
140
+ results
141
+ end
142
+
143
+ private
144
+
145
+ def transform_spec_to_api_path(spec, controller_base_path, extension)
146
+ api_path = spec.to_s.dup
147
+ api_path.gsub!('(.:format)', extension ? ".#{extension}" : '')
148
+ api_path.gsub!(/:(\w+)/, '{\1}')
149
+ api_path.gsub!(controller_base_path, '')
150
+ "/" + trim_slashes(api_path)
151
+ end
152
+
153
+ def camelize_keys_deep!(h)
154
+ h.keys.each do |k|
155
+ ks = k.to_s.camelize(:lower)
156
+ h[ks] = h.delete k
157
+ camelize_keys_deep! h[ks] if h[ks].kind_of? Hash
158
+ if h[ks].kind_of? Array
159
+ h[ks].each do |a|
160
+ next unless a.kind_of? Hash
161
+ camelize_keys_deep! a
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ def trim_leading_slash(str)
168
+ return str if !str
169
+ str.gsub(/\A\/+/, '')
170
+ end
171
+
172
+ def trim_trailing_slash(str)
173
+ return str if !str
174
+ str.gsub(/\/+\z/, '')
175
+ end
176
+
177
+ def trim_slashes(str)
178
+ trim_leading_slash(trim_trailing_slash(str))
179
+ end
180
+
181
+ def get_debased_path(path, controller_base_path)
182
+ path.gsub("#{controller_base_path}", "")
183
+ end
184
+
185
+ def process_path(path, root, config, settings)
186
+ return {action: :skipped, reason: :empty_path} if path.empty?
187
+ klass = Config.log_exception { "#{path.to_s.camelize}Controller".constantize } rescue nil
188
+ return {action: :skipped, path: path, reason: :klass_not_present} if !klass
189
+ return {action: :skipped, path: path, reason: :not_swagger_resource} if !klass.methods.include?(:swagger_config) or !klass.swagger_config[:controller]
190
+ return {action: :skipped, path: path, reason: :not_kind_of_parent_controller} if config[:parent_controller] && !(klass < config[:parent_controller])
191
+ apis, models, defined_nicknames = [], {}, []
192
+ routes.select{|i| i.defaults[:controller] == path}.each do |route|
193
+ unless nickname_defined?(defined_nicknames, path, route) # only add once for each route once e.g. PATCH, PUT
194
+ ret = get_route_path_apis(path, route, klass, settings, config)
195
+ apis = apis + ret[:apis]
196
+ models.merge!(ret[:models])
197
+ defined_nicknames << ret[:nickname] if ret[:nickname].present?
198
+ end
199
+ end
200
+ {action: :processed, path: path, apis: apis, models: models, klass: klass}
201
+ end
202
+
203
+ def route_verbs(route)
204
+ if defined?(route.verb.source) then route.verb.source.to_s.delete('$'+'^').split('|') else [route.verb] end.collect{|verb| verb.downcase.to_sym}
205
+ end
206
+
207
+ def path_route_nickname(path, route)
208
+ action = route.defaults[:action]
209
+ "#{path.camelize}##{action}"
210
+ end
211
+
212
+ def nickname_defined?(defined_nicknames, path, route)
213
+ target_nickname = path_route_nickname(path, route)
214
+ defined_nicknames.each{|nickname| return true if nickname == target_nickname }
215
+ false
216
+ end
217
+
218
+ def generate_resource(path, apis, models, settings, root, config, swagger_config)
219
+ metadata = ApiDeclarationFileMetadata.new(
220
+ root["apiVersion"], path, root["basePath"],
221
+ settings[:controller_base_path],
222
+ camelize_model_properties: config.fetch(:camelize_model_properties, true),
223
+ swagger_version: root["swaggerVersion"],
224
+ authorizations: root[:authorizations],
225
+ resource_path: swagger_config[:resource_path]
226
+ )
227
+ declaration = ApiDeclarationFile.new(metadata, apis, models)
228
+ declaration.generate_resource
229
+ end
230
+
231
+ def routes
232
+ Config.base_applications.map{|app| app.routes.routes.to_a }.flatten
233
+ end
234
+
235
+ def get_route_path_apis(path, route, klass, settings, config)
236
+ models, apis = {}, []
237
+ action = route.defaults[:action]
238
+ verbs = route_verbs(route)
239
+ return {apis: apis, models: models, nickname: nil} if !operation = klass.swagger_actions[action.to_sym]
240
+ operation = Hash[operation.map {|k, v| [k.to_s.gsub("@","").to_sym, v.respond_to?(:deep_dup) ? v.deep_dup : v.dup] }] # rename :@instance hash keys
241
+ nickname = operation[:nickname] = path_route_nickname(path, route)
242
+
243
+ route_path = if defined?(route.path.spec) then route.path.spec else route.path end
244
+ api_path = transform_spec_to_api_path(route_path, settings[:controller_base_path], config[:api_extension_type])
245
+ operation[:parameters] = filter_path_params(api_path, operation[:parameters]) if operation[:parameters]
246
+ operations = verbs.collect{|verb|
247
+ op = operation.dup
248
+ op[:method] = verb
249
+ op
250
+ }
251
+ apis << {:path => api_path, :operations => operations}
252
+ models = get_klass_models(klass)
253
+
254
+ {apis: apis, models: models, nickname: nickname}
255
+ end
256
+
257
+ def get_klass_models(klass)
258
+ models = {}
259
+ # Add any declared models to the root of the resource.
260
+ klass.swagger_models.each do |model_name, model|
261
+ formatted_model = {
262
+ id: model[:id],
263
+ required: model[:required],
264
+ properties: model[:properties],
265
+ }
266
+ formatted_model[:description] = model[:description] if model[:description]
267
+ models[model[:id]] = formatted_model
268
+ end
269
+ models
270
+ end
271
+
272
+ def get_settings(api_version, config)
273
+ base_path = trim_trailing_slash(config[:base_path] || "")
274
+ controller_base_path = trim_leading_slash(config[:controller_base_path] || "")
275
+ base_path += "/#{controller_base_path}" unless controller_base_path.empty?
276
+ api_file_path = config[:api_file_path]
277
+ authorizations = config[:authorizations]
278
+
279
+ # new version
280
+ host = config[:host]
281
+ info = config[:attributes][:info]
282
+ settings = {
283
+ base_path: base_path,
284
+ controller_base_path: controller_base_path,
285
+ api_file_path: api_file_path,
286
+ authorizations: authorizations,
287
+
288
+ info: info,
289
+ host: host
290
+ }.freeze
291
+ end
292
+
293
+ def get_route_paths(controller_base_path)
294
+ paths = routes.map{|i| "#{i.defaults[:controller]}" }
295
+ paths.uniq.select{|i| i.start_with?(controller_base_path)}
296
+ end
297
+
298
+ def create_output_paths(api_file_path)
299
+ FileUtils.mkdir_p(api_file_path) # recursively create out output path
300
+ end
301
+
302
+ def clean_output_paths(api_file_path)
303
+ Dir.foreach(api_file_path) do |f|
304
+ fn = File.join(api_file_path, f)
305
+ File.delete(fn) if !File.directory?(fn) and File.extname(fn) == '.json'
306
+ end
307
+ end
308
+
309
+ def write_to_file(path, structure, config={})
310
+ content = case config[:formatting]
311
+ when :pretty; JSON.pretty_generate structure
312
+ else; structure.to_json
313
+ end
314
+ FileUtils.mkdir_p File.dirname(path)
315
+ File.open(path, 'w') { |file| file.write content }
316
+ end
317
+
318
+ def filter_path_params(path, params)
319
+ params.reject do |param|
320
+ param_as_variable = "{#{param[:name]}}"
321
+ param[:param_type] == :path && !path.include?(param_as_variable)
322
+ end
323
+ end
324
+ end
325
+ end
326
+ end
327
+ end