Skip to content

Commit

Permalink
Merge pull request rkh#1 from sge/swagger-support
Browse files Browse the repository at this point in the history
Swagger support
  • Loading branch information
tim-sge committed May 10, 2013
2 parents 98a49f0 + 9862584 commit f401154
Show file tree
Hide file tree
Showing 75 changed files with 15,296 additions and 9 deletions.
10 changes: 8 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
PATH
remote: .
specs:
yard-sinatra (1.0.0)
yard-sinatra (1.0.1)
activesupport
yard (~> 0.7)

GEM
remote: http://rubygems.org/
specs:
activesupport (3.2.13)
i18n (= 0.6.1)
multi_json (~> 1.0)
diff-lcs (1.1.2)
i18n (0.6.1)
multi_json (1.7.3)
rake (0.9.2)
rspec (2.6.0)
rspec-core (~> 2.6.0)
Expand All @@ -17,7 +23,7 @@ GEM
rspec-expectations (2.6.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.6.0)
yard (0.7.2)
yard (0.8.6.1)

PLATFORMS
ruby
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
YARD::Sinatra
=============

This plugin adds [Sinatra](http://sinatrarb.com) routes to [YARD](http://yardoc.org/) output.
This plugin adds [Sinatra](http://sinatrarb.com) routes to [YARD](http://yardoc.org/) output. Additionally, there is a [Swagger](http://github.com/wordnik/swagger-core) template for generating interactive API documentation.

Usage
-----
Expand Down Expand Up @@ -43,6 +43,14 @@ The you're ready to go:

yardoc example_app.rb

Or, to generate spiffy [Swagger](http://github.com/wordnik/swagger-core) documentation, use:

yardoc -t swagger

By default, the Swagger documentation will include references to `http://127.0.0.1:9292/` as the base URL for making interactive API calls. You can modify this behavior by setting the `SWAGGER_BASE_PATH` environment variable, for example:

SWAGGER_BASE_PATH='http://api.domain.com/' yardoc -t swagger

Old versions of YARD (before 0.6.2) will automatically detect the yard-sinatra plugin and load it. In newer versions you must use the `--plugin yard-sinatra` parameter, or add it to a .yardopts file.

Other use cases
Expand Down
118 changes: 118 additions & 0 deletions templates/swagger/fulldoc/html/setup.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
require 'active_support/all'

def init

# copy over assets from swagger-ui
emit_swagger_ui_assets

# write out the actual swagger definition
options.serializer.serialize 'js/api.js', JSON.pretty_generate(swagger_api_definition)

# overwrite the swagger index.html distributable with a custom one
Templates::Engine.with_serializer('index.html', options.serializer) do
erb :swagger
end

end

def swagger_friendly_path(path)
path.split('/').map { |part| part.starts_with?(':') ? "{#{part.gsub(':','')}}" : part }.join('/')
end

def path_parameters(path)
path.split('/').select { |part| part.starts_with?(':') }.map { |var| var.gsub(':','') }
end

def swagger_api_definition
base_path = ENV['SWAGGER_BASE_PATH'].present? ? ENV['SWAGGER_BASE_PATH'] : 'http://127.0.0.1:9292/'

swagger_definition = {
apiVersion: '1.0',
swaggerVersion: '1.1',
basePath: base_path,
resourcePath: File.basename(File.expand_path('.')),
apis: [],
models: {}
}

# scan the routes for "resources" which (by convention) are the first
# part of the path, ala: /resources/:id

resources = {}
YARD::Sinatra.routes.each do |route|
resource = route.http_path.split('/')[1]
resources[resource] = {} unless resources[resource].present?
resources[resource][route.http_path] = [] unless resources[resource][route.http_path].present?
resources[resource][route.http_path] << route
end

# build the models section by inspecting the actual ActiveRecord models themselves
# this is slightly error-prone in that our "resource" names may not match one-to-one
# with models

resources.keys.each do |resource|
klass = "SleepyGiant::Model::Subscriptions::#{resource.singularize.classify}".constantize rescue next
swagger_definition[:models][resource.singularize.classify] = {
id: resource.singularize.classify,
properties: klass.columns.inject({}) { |m,i| m[i.name] = { type: i.type }; m }
}
end

# for each resource and path combination, assemble the API definitions
# for now this is lacking any kind of parameters (assumedly to come from
# the @param tags once I figure it out)

resources.keys.each do |resource|
resources[resource].keys.each do |path|
api = { path: swagger_friendly_path(path), operations: [], description: "Operations about #{resource}" }
resources[resource][path].each do |route|
parameters = []
path_parameters(route.http_path).each do |param|
parameters << {
name: param,
description: "Path-parameter #{param}",
paramType: 'path',
required: true,
dataType: 'string'
}
end
api[:operations] << {
httpMethod: route.http_verb,
summary: route.docstring,
notes: route.docstring,
nickname: "#{route.http_verb.titleize}#{route.http_path.gsub(':','').camelize.gsub('::','')}",
responseClass: resource.singularize.classify,
parameters: parameters,
errorResponses: []
}
end
swagger_definition[:apis] << api
end
end

swagger_definition
end

def javascripts
%w(js/swagger-ui.js)
end

def stylesheets
%w()
end

def emit_swagger_ui_assets
Dir[File.join(File.dirname(__FILE__),'swagger-ui','dist','**','*')].each do |file|
relative_path = file.gsub(File.dirname(__FILE__),'')[1..-1]
emitted_file_path = file.gsub(File.join(File.dirname(__FILE__),'swagger-ui','dist'),'')[1..-1]
asset emitted_file_path, file(relative_path, false) unless File.directory?(file)
end
end

def asset(path, content)
if options.serializer
log.capture("Generating asset #{path}") do
options.serializer.serialize(path, content)
end
end
end
21 changes: 21 additions & 0 deletions templates/swagger/fulldoc/html/swagger-ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
wordnik*.zip
.DS_STORE
*.ipr
*.iml
*.iws
lib/*.jar
META-INF/
web/
index
lib/*.zip
dk/
index_dict
logs
src/main/java/com/wordnik/env/Version.scala
lib/*.pom
version.properties
reports/*
.sass-cache
swagger-ui.sublime-workspace
.idea
.project
7 changes: 7 additions & 0 deletions templates/swagger/fulldoc/html/swagger-ui/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
*.sublime-*
example.html
*.tgz
.classpath
.project
.npmignore
node_modules
116 changes: 116 additions & 0 deletions templates/swagger/fulldoc/html/swagger-ui/Cakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
fs = require 'fs'
path = require 'path'
{exec} = require 'child_process'

sourceFiles = [
'SwaggerUi'
'view/HeaderView'
'view/MainView'
'view/ResourceView'
'view/OperationView'
'view/StatusCodeView'
'view/ParameterView'
'view/SignatureView'
'view/ContentTypeView'
]


task 'clean', 'Removes distribution', ->
console.log 'Clearing dist...'
exec 'rm -rf dist'

task 'dist', 'Build a distribution', ->
console.log "Build distribution in ./dist"
fs.mkdirSync('dist') if not path.existsSync('dist')
fs.mkdirSync('dist/lib') if not path.existsSync('dist/lib')

appContents = new Array remaining = sourceFiles.length
for file, index in sourceFiles then do (file, index) ->
console.log " : Reading src/main/coffeescript/#{file}.coffee"
fs.readFile "src/main/coffeescript/#{file}.coffee", 'utf8', (err, fileContents) ->
throw err if err
appContents[index] = fileContents
precompileTemplates() if --remaining is 0

precompileTemplates= ->
console.log ' : Precompiling templates...'
templateFiles = fs.readdirSync('src/main/template')
templateContents = new Array remaining = templateFiles.length
for file, index in templateFiles then do (file, index) ->
console.log " : Compiling src/main/template/#{file}"
exec "handlebars src/main/template/#{file} -f dist/_#{file}.js", (err, stdout, stderr) ->
throw err if err
fs.readFile 'dist/_' + file + '.js', 'utf8', (err, fileContents) ->
throw err if err
templateContents[index] = fileContents
fs.unlink 'dist/_' + file + '.js'
if --remaining is 0
templateContents.push '\n\n'
fs.writeFile 'dist/_swagger-ui-templates.js', templateContents.join('\n\n'), 'utf8', (err) ->
throw err if err
build()


build = ->
console.log ' : Collecting Coffeescript source...'

appContents.push '\n\n'
fs.writeFile 'dist/_swagger-ui.coffee', appContents.join('\n\n'), 'utf8', (err) ->
throw err if err
console.log ' : Compiling...'
exec 'coffee --compile dist/_swagger-ui.coffee', (err, stdout, stderr) ->
throw err if err
fs.unlink 'dist/_swagger-ui.coffee'
console.log ' : Combining with javascript...'
exec 'cat src/main/javascript/doc.js dist/_swagger-ui-templates.js dist/_swagger-ui.js > dist/swagger-ui.js', (err, stdout, stderr) ->
throw err if err
fs.unlink 'dist/_swagger-ui.js'
fs.unlink 'dist/_swagger-ui-templates.js'
console.log ' : Minifying all...'
exec 'java -jar "./bin/yuicompressor-2.4.7.jar" --type js -o ' + 'dist/swagger-ui.min.js ' + 'dist/swagger-ui.js', (err, stdout, stderr) ->
throw err if err
pack()

pack = ->
console.log ' : Packaging...'
exec 'cp -r lib dist'
exec 'cp -r node_modules/swagger-client/lib/swagger.js dist/lib'
exec 'cp -r src/main/html/* dist'
console.log ' !'

task 'spec', "Run the test suite", ->
exec "open spec.html", (err, stdout, stderr) ->
throw err if err

task 'watch', 'Watch source files for changes and autocompile', ->
# Function which watches all files in the passed directory
watchFiles = (dir) ->
files = fs.readdirSync(dir)
for file, index in files then do (file, index) ->
console.log " : " + dir + "/#{file}"
fs.watchFile dir + "/#{file}", (curr, prev) ->
if +curr.mtime isnt +prev.mtime
invoke 'dist'

notify "Watching source files for changes..."

# Watch specific source files
for file, index in sourceFiles then do (file, index) ->
console.log " : " + "src/main/coffeescript/#{file}.coffee"
fs.watchFile "src/main/coffeescript/#{file}.coffee", (curr, prev) ->
if +curr.mtime isnt +prev.mtime
invoke 'dist'

# watch all files in these folders
watchFiles("src/main/template")
watchFiles("src/main/javascript")
watchFiles("src/main/html")
watchFiles("src/test")

notify = (message) ->
return unless message?
console.log message
# options =
# title: 'CoffeeScript'
# image: 'bin/CoffeeScript.png'
# try require('growl') message, options
Loading

0 comments on commit f401154

Please sign in to comment.