spectacle 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b42ae059b23d5ff986e540814af430c8caaa0626
4
+ data.tar.gz: aa149c1772cdfe1d28e949a1d2db6e660e77450c
5
+ SHA512:
6
+ metadata.gz: 36053c1424daf39030a9ada1ebb67615153c5bc998f762119dd0719ba1e95a91f8ec5fda7d230d07a8c72029807a6311415c74b33e451f0d56961876f4bc7267
7
+ data.tar.gz: 08a660ac60c8932c97bdce2566e573b773f7f94a62481f3eeed7a78396ace18404b210a58b1a6eb84b356bf865ebe0a942231ba7d702c6923e3bf541a5f1ab18
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ public
19
+ bak
20
+ node_modules
21
+ app/views/*/*
22
+ vendor/assets/*/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in spectacle.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Rich Hollis
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,13 @@
1
+ # Spectacle
2
+
3
+ Generate beautiful static documentation for your Rails API from OpenAPI/Swagger 2.0 specifications.
4
+
5
+ ## Contributing
6
+
7
+ When raising a Pull Request please ensure that you have provided good test coverage for the request you are making.
8
+
9
+ 1. Fork it
10
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
11
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
12
+ 4. Push to the branch (`git push origin my-new-feature`)
13
+ 5. Create new Pull Request
@@ -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,3 @@
1
+ Spectacle::Engine.routes.draw do
2
+ root 'apidocs#show'
3
+ end
@@ -0,0 +1,5 @@
1
+ require "spectacle/config"
2
+ require "spectacle/engine"
3
+ require "spectacle/dsl"
4
+ require "spectacle/task"
5
+ require "spectacle/version"
@@ -0,0 +1,79 @@
1
+ module Spectacle
2
+ class Config
3
+ class << self
4
+
5
+ def target_dir= value
6
+ @versions = value
7
+ end
8
+
9
+ def target_dir
10
+ @target_dir ||= 'public/apidocs'
11
+ end
12
+
13
+ def spec_file= value
14
+ @spec_file = value
15
+ end
16
+
17
+ def spec_file
18
+ @spec_file ||= 'public/swagger.json'
19
+ end
20
+
21
+
22
+ # @@base_api_controller = nil
23
+ #
24
+ # def base_api_controller
25
+ # @@base_api_controller || ActionController::Base
26
+ # end
27
+ #
28
+ # def base_api_controllers
29
+ # Array(base_api_controller)
30
+ # end
31
+ #
32
+ # def base_api_controller=(controller)
33
+ # @@base_api_controller = controller
34
+ # end
35
+ #
36
+ # alias_method :base_api_controllers=, :base_api_controller=
37
+ #
38
+ # def base_applications
39
+ # Array(base_application)
40
+ # end
41
+ #
42
+ # def base_application
43
+ # Rails.application
44
+ # end
45
+ #
46
+ # def register_apis(versions)
47
+ # base_api_controllers.each do |controller|
48
+ # controller.send(:include, ImpotentMethods)
49
+ # end
50
+ # @versions = versions
51
+ # end
52
+ #
53
+ # def registered_apis
54
+ # @versions ||= {}
55
+ # end
56
+ #
57
+ # def transform_path(path, api_version)
58
+ # # This is only for overriding, so don't perform any path transformations by default.
59
+ # path
60
+ # end
61
+ #
62
+ # def log_exception
63
+ # yield
64
+ # rescue => e
65
+ # write_log(:error, e)
66
+ # raise
67
+ # end
68
+ #
69
+ # def log_env_name
70
+ # 'SD_LOG_LEVEL'
71
+ # end
72
+ #
73
+ # def write_log(type, output)
74
+ # $stderr.puts output if type == :error and ENV[log_env_name]=="1"
75
+ # end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,337 @@
1
+ require 'open3'
2
+
3
+ module Spectacle
4
+ class DSL
5
+
6
+ DEFAULT_CONFIG = {
7
+ spectacle_dir: 'node_modules/spectacle-docs' #,
8
+ # target_dir: 'public/apidocs',
9
+ # spec_path: 'public/swagger.json'#,
10
+ # :base_path: '/',
11
+ # :clean_directory: false,
12
+ # :formatting: :pretty
13
+ }
14
+
15
+ class << self
16
+
17
+ #
18
+ # Generate Spectacle static documentation
19
+ #
20
+ # @return success
21
+ #
22
+ def generate
23
+ res = run "-C -e #{Config.spec_file}"
24
+ p res
25
+ if res[2]
26
+ source_dir = "#{DEFAULT_CONFIG[:spectacle_dir]}/app"
27
+ build_dir = "#{DEFAULT_CONFIG[:spectacle_dir]}/public"
28
+ views_dir = './app/views/spectacle'
29
+ assets_dir = './vendor/assets'
30
+
31
+ FileUtils.mkdir_p views_dir
32
+ FileUtils.mkdir_p "#{assets_dir}/stylesheets"
33
+ FileUtils.mkdir_p "#{assets_dir}/javascripts"
34
+
35
+ FileUtils.cp "#{build_dir}/index.html",
36
+ "#{views_dir}/_docs.html.erb"
37
+ FileUtils.cp Dir.glob("#{source_dir}/stylesheets/*"),
38
+ "#{assets_dir}/stylesheets"
39
+ # FileUtils.copy "#{build_dir}/stylesheets/spectacle.css",
40
+ # "#{assets_dir}/stylesheets/spectacle.css"
41
+ FileUtils.cp "#{build_dir}/javascripts/spectacle.js",
42
+ "#{assets_dir}/javascripts/spectacle.js"
43
+ end
44
+ res
45
+ end
46
+
47
+ #
48
+ # Install Spectacle using `npm`
49
+ #
50
+ # @return success
51
+ #
52
+ def install
53
+ sh "npm install spectacle-docs"
54
+ end
55
+
56
+ #
57
+ # Run the Spectacle shell command
58
+ #
59
+ # @param args the command line arguments to pass to spectacle
60
+ #
61
+ # @return success
62
+ #
63
+ def run args
64
+ sh "cd #{DEFAULT_CONFIG[:spectacle_dir]} && node bin/cli #{args}"
65
+ end
66
+
67
+ #
68
+ # Identical to Open3.capture3, except that it rescues runtime errors
69
+ #
70
+ # @param env optional (as `Kernel.system')
71
+ # @param *cmd the command and its (auto-escaped) arguments
72
+ # @param opts optional a hash of options (as `Kernel.system')
73
+ #
74
+ # @return [stdout, stderr, success]
75
+ #
76
+ def sh(*cmd)
77
+ begin
78
+ stdout, stderr, status = Open3.capture3(*cmd)
79
+ [stdout, stderr, status.success?]
80
+ rescue
81
+ [$/, $/, nil]
82
+ end
83
+ end
84
+
85
+ # def set_real_methods
86
+ # # replace impotent methods with live ones
87
+ # Config.base_api_controllers.each do |controller|
88
+ # controller.send(:include, Methods)
89
+ # end
90
+ # end
91
+ #
92
+ # def write_docs(apis = nil)
93
+ # results = generate_docs(apis)
94
+ # results.each{|api_version, result| write_doc(result) }
95
+ # end
96
+ #
97
+ # def write_doc(result)
98
+ # settings = result[:settings]
99
+ # config = result[:config]
100
+ # create_output_paths(settings[:api_file_path])
101
+ # clean_output_paths(settings[:api_file_path]) if config[:clean_directory] || false
102
+ # root = result[:root]
103
+ # resources = root.delete 'resources'
104
+ # root.merge!(config[:attributes] || {}) # merge custom user attributes like info
105
+ # # write the api-docs file
106
+ # write_to_file('#{settings[:api_file_path]}/#{config[:api_file_name]}', root, config)
107
+ # # write the individual resource files
108
+ # resources.each do |resource|
109
+ # resource_file_path = resource.delete 'resourceFilePath'
110
+ # write_to_file(File.join(settings[:api_file_path], '#{resource_file_path}.json'), resource, config)
111
+ # end
112
+ # result
113
+ # end
114
+ #
115
+ # def generate_docs(apis=nil)
116
+ # apis ||= Config.registered_apis
117
+ # results = {}
118
+ # set_real_methods
119
+ #
120
+ # apis[DEFAULT_VER] = DEFAULT_CONFIG if apis.empty?
121
+ #
122
+ # apis.each do |api_version, config|
123
+ # settings = get_settings(api_version, config)
124
+ # config.reverse_merge!(DEFAULT_CONFIG)
125
+ # results[api_version] = generate_doc(api_version, settings, config)
126
+ # results[api_version][:settings] = settings
127
+ # results[api_version][:config] = config
128
+ # end
129
+ # results
130
+ # end
131
+ #
132
+ # def generate_doc(api_version, settings, config)
133
+ # root = {
134
+ # 'apiVersion': api_version,
135
+ # 'swaggerVersion': '1.2',
136
+ # 'basePath': settings[:base_path],
137
+ # :apis: [],
138
+ # :authorizations: settings[:authorizations]
139
+ # }
140
+ # results = {:processed: [], :skipped: []}
141
+ # resources = []
142
+ #
143
+ # get_route_paths(settings[:controller_base_path]).each do |path|
144
+ # ret = process_path(path, root, config, settings)
145
+ # results[ret[:action]] << ret
146
+ # if ret[:action] == :processed
147
+ # resources << generate_resource(ret[:path], ret[:apis], ret[:models], settings, root, config, ret[:klass].swagger_config)
148
+ # debased_path = get_debased_path(ret[:path], settings[:controller_base_path])
149
+ # resource_api = {
150
+ # path: '/#{Config.transform_path(trim_leading_slash(debased_path), api_version)}.{format}',
151
+ # description: ret[:klass].swagger_config[:description]
152
+ # }
153
+ # root[:apis] << resource_api
154
+ # end
155
+ # end
156
+ # root['resources'] = resources
157
+ # results[:root] = root
158
+ # results
159
+ # end
160
+ #
161
+ # private
162
+ #
163
+ # def transform_spec_to_api_path(spec, controller_base_path, extension)
164
+ # api_path = spec.to_s.dup
165
+ # api_path.gsub!('(.:format)', extension ? '.#{extension}' : '')
166
+ # api_path.gsub!(/:(\w+)/, '{\1}')
167
+ # api_path.gsub!(controller_base_path, '')
168
+ # '/' + trim_slashes(api_path)
169
+ # end
170
+ #
171
+ # def camelize_keys_deep!(h)
172
+ # h.keys.each do |k|
173
+ # ks = k.to_s.camelize(:lower)
174
+ # h[ks] = h.delete k
175
+ # camelize_keys_deep! h[ks] if h[ks].kind_of? Hash
176
+ # if h[ks].kind_of? Array
177
+ # h[ks].each do |a|
178
+ # next unless a.kind_of? Hash
179
+ # camelize_keys_deep! a
180
+ # end
181
+ # end
182
+ # end
183
+ # end
184
+ #
185
+ # def trim_leading_slash(str)
186
+ # return str if !str
187
+ # str.gsub(/\A\/+/, '')
188
+ # end
189
+ #
190
+ # def trim_trailing_slash(str)
191
+ # return str if !str
192
+ # str.gsub(/\/+\z/, '')
193
+ # end
194
+ #
195
+ # def trim_slashes(str)
196
+ # trim_leading_slash(trim_trailing_slash(str))
197
+ # end
198
+ #
199
+ # def get_debased_path(path, controller_base_path)
200
+ # path.gsub('#{controller_base_path}', '')
201
+ # end
202
+ #
203
+ # def process_path(path, root, config, settings)
204
+ # return {action: :skipped, reason: :empty_path} if path.empty?
205
+ # klass = Config.log_exception { '#{path.to_s.camelize}Controller'.constantize } rescue nil
206
+ # return {action: :skipped, path: path, reason: :klass_not_present} if !klass
207
+ # return {action: :skipped, path: path, reason: :not_swagger_resource} if !klass.methods.include?(:swagger_config) or !klass.swagger_config[:controller]
208
+ # return {action: :skipped, path: path, reason: :not_kind_of_parent_controller} if config[:parent_controller] && !(klass < config[:parent_controller])
209
+ # apis, models, defined_nicknames = [], {}, []
210
+ # routes.select{|i| i.defaults[:controller] == path}.each do |route|
211
+ # unless nickname_defined?(defined_nicknames, path, route) # only add once for each route once e.g. PATCH, PUT
212
+ # ret = get_route_path_apis(path, route, klass, settings, config)
213
+ # apis = apis + ret[:apis]
214
+ # models.merge!(ret[:models])
215
+ # defined_nicknames << ret[:nickname] if ret[:nickname].present?
216
+ # end
217
+ # end
218
+ # {action: :processed, path: path, apis: apis, models: models, klass: klass}
219
+ # end
220
+ #
221
+ # def route_verbs(route)
222
+ # if defined?(route.verb.source) then route.verb.source.to_s.delete('$'+'^').split('|') else [route.verb] end.collect{|verb| verb.downcase.to_sym}
223
+ # end
224
+ #
225
+ # def path_route_nickname(path, route)
226
+ # action = route.defaults[:action]
227
+ # '#{path.camelize}##{action}'
228
+ # end
229
+ #
230
+ # def nickname_defined?(defined_nicknames, path, route)
231
+ # target_nickname = path_route_nickname(path, route)
232
+ # defined_nicknames.each{|nickname| return true if nickname == target_nickname }
233
+ # false
234
+ # end
235
+ #
236
+ # def generate_resource(path, apis, models, settings, root, config, swagger_config)
237
+ # metadata = ApiDeclarationFileMetadata.new(
238
+ # root['apiVersion'], path, root['basePath'],
239
+ # settings[:controller_base_path],
240
+ # camelize_model_properties: config.fetch(:camelize_model_properties, true),
241
+ # swagger_version: root['swaggerVersion'],
242
+ # authorizations: root[:authorizations],
243
+ # resource_path: swagger_config[:resource_path]
244
+ # )
245
+ # declaration = ApiDeclarationFile.new(metadata, apis, models)
246
+ # declaration.generate_resource
247
+ # end
248
+ #
249
+ # def routes
250
+ # Config.base_applications.map{|app| app.routes.routes.to_a }.flatten
251
+ # end
252
+ #
253
+ # def get_route_path_apis(path, route, klass, settings, config)
254
+ # models, apis = {}, []
255
+ # action = route.defaults[:action]
256
+ # verbs = route_verbs(route)
257
+ # return {apis: apis, models: models, nickname: nil} if !operation = klass.swagger_actions[action.to_sym]
258
+ # 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
259
+ # nickname = operation[:nickname] = path_route_nickname(path, route)
260
+ #
261
+ # route_path = if defined?(route.path.spec) then route.path.spec else route.path end
262
+ # api_path = transform_spec_to_api_path(route_path, settings[:controller_base_path], config[:api_extension_type])
263
+ # operation[:parameters] = filter_path_params(api_path, operation[:parameters]) if operation[:parameters]
264
+ # operations = verbs.collect{|verb|
265
+ # op = operation.dup
266
+ # op[:method] = verb
267
+ # op
268
+ # }
269
+ # apis << {:path: api_path, :operations: operations}
270
+ # models = get_klass_models(klass)
271
+ #
272
+ # {apis: apis, models: models, nickname: nickname}
273
+ # end
274
+ #
275
+ # def get_klass_models(klass)
276
+ # models = {}
277
+ # # Add any declared models to the root of the resource.
278
+ # klass.swagger_models.each do |model_name, model|
279
+ # formatted_model = {
280
+ # id: model[:id],
281
+ # required: model[:required],
282
+ # properties: model[:properties],
283
+ # }
284
+ # formatted_model[:description] = model[:description] if model[:description]
285
+ # models[model[:id]] = formatted_model
286
+ # end
287
+ # models
288
+ # end
289
+ #
290
+ # def get_settings(api_version, config)
291
+ # base_path = trim_trailing_slash(config[:base_path] || '')
292
+ # controller_base_path = trim_leading_slash(config[:controller_base_path] || '')
293
+ # base_path += '/#{controller_base_path}' unless controller_base_path.empty?
294
+ # api_file_path = config[:api_file_path]
295
+ # authorizations = config[:authorizations]
296
+ # settings = {
297
+ # base_path: base_path,
298
+ # controller_base_path: controller_base_path,
299
+ # api_file_path: api_file_path,
300
+ # authorizations: authorizations
301
+ # }.freeze
302
+ # end
303
+ #
304
+ # def get_route_paths(controller_base_path)
305
+ # paths = routes.map{|i| '#{i.defaults[:controller]}' }
306
+ # paths.uniq.select{|i| i.start_with?(controller_base_path)}
307
+ # end
308
+ #
309
+ # def create_output_paths(api_file_path)
310
+ # FileUtils.mkdir_p(api_file_path) # recursively create out output path
311
+ # end
312
+ #
313
+ # def clean_output_paths(api_file_path)
314
+ # Dir.foreach(api_file_path) do |f|
315
+ # fn = File.join(api_file_path, f)
316
+ # File.delete(fn) if !File.directory?(fn) and File.extname(fn) == '.json'
317
+ # end
318
+ # end
319
+ #
320
+ # def write_to_file(path, structure, config={})
321
+ # content = case config[:formatting]
322
+ # when :pretty; JSON.pretty_generate structure
323
+ # else; structure.to_json
324
+ # end
325
+ # FileUtils.mkdir_p File.dirname(path)
326
+ # File.open(path, 'w') { |file| file.write content }
327
+ # end
328
+ #
329
+ # def filter_path_params(path, params)
330
+ # params.reject do |param|
331
+ # param_as_variable = '{#{param[:name]}}'
332
+ # param[:param_type] == :path && !path.include?(param_as_variable)
333
+ # end
334
+ # end
335
+ end
336
+ end
337
+ end