spectacle 0.1.2

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.
@@ -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