swagger_yard 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +113 -0
- data/Rakefile +40 -0
- data/app/controllers/swagger_yard/application_controller.rb +4 -0
- data/app/controllers/swagger_yard/swagger_controller.rb +21 -0
- data/app/views/swagger_yard/swagger/doc.html.erb +80 -0
- data/config/routes.rb +6 -0
- data/lib/generators/swagger_yard/doc_generator.rb +11 -0
- data/lib/generators/swagger_yard/js_generator.rb +13 -0
- data/lib/swagger_yard.rb +113 -0
- data/lib/swagger_yard/api.rb +132 -0
- data/lib/swagger_yard/api_declaration.rb +42 -0
- data/lib/swagger_yard/cache.rb +50 -0
- data/lib/swagger_yard/engine.rb +18 -0
- data/lib/swagger_yard/local_dispatcher.rb +51 -0
- data/lib/swagger_yard/parameter.rb +9 -0
- data/lib/swagger_yard/parser.rb +35 -0
- data/lib/swagger_yard/resource_listing.rb +32 -0
- data/lib/swagger_yard/version.rb +3 -0
- data/public/swagger-ui/css/hightlight.default.css +135 -0
- data/public/swagger-ui/css/screen.css +1759 -0
- data/public/swagger-ui/images/logo_small.png +0 -0
- data/public/swagger-ui/images/pet_store_api.png +0 -0
- data/public/swagger-ui/images/throbber.gif +0 -0
- data/public/swagger-ui/images/wordnik_api.png +0 -0
- data/public/swagger-ui/lib/MD5.js +319 -0
- data/public/swagger-ui/lib/backbone-min.js +38 -0
- data/public/swagger-ui/lib/handlebars-1.0.rc.1.js +1920 -0
- data/public/swagger-ui/lib/highlight.7.3.pack.js +1 -0
- data/public/swagger-ui/lib/jquery-1.8.0.min.js +2 -0
- data/public/swagger-ui/lib/jquery.ba-bbq.min.js +18 -0
- data/public/swagger-ui/lib/jquery.slideto.min.js +1 -0
- data/public/swagger-ui/lib/jquery.wiggle.min.js +8 -0
- data/public/swagger-ui/lib/swagger.js +794 -0
- data/public/swagger-ui/lib/underscore-min.js +32 -0
- data/public/swagger-ui/swagger-ui.js +2090 -0
- data/public/swagger-ui/swagger-ui_org.js +2005 -0
- metadata +125 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
module SwaggerYard
|
2
|
+
class Api
|
3
|
+
attr_reader :nickname
|
4
|
+
attr_accessor :path, :parameters, :description, :http_method, :response_class, :summary, :notes, :error_responses
|
5
|
+
|
6
|
+
def initialize(yard_object)
|
7
|
+
@description = yard_object.docstring
|
8
|
+
@parameters = []
|
9
|
+
|
10
|
+
yard_object.tags.each do |tag|
|
11
|
+
value = tag.text
|
12
|
+
|
13
|
+
case tag.tag_name
|
14
|
+
when "path"
|
15
|
+
parse_path(value)
|
16
|
+
when "parameter"
|
17
|
+
@parameters << parse_parameter(value)
|
18
|
+
when "parameter_list"
|
19
|
+
@parameters << parse_parameter_list(value)
|
20
|
+
when "summary"
|
21
|
+
@summary = value
|
22
|
+
when "notes"
|
23
|
+
@notes = value.gsub("\n", "<br\>")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
@parameters.sort_by { |parameter| parameter["name"] }
|
28
|
+
@parameters << add_format_parameters
|
29
|
+
end
|
30
|
+
|
31
|
+
def nickname
|
32
|
+
@nickname ||= "#{http_method}".camelize
|
33
|
+
end
|
34
|
+
|
35
|
+
def operation
|
36
|
+
{
|
37
|
+
"httpMethod" => http_method,
|
38
|
+
"nickname" => path[1..-1].gsub(/[^a-zA-Z\d:]/, '-').squeeze("-") + http_method.downcase,
|
39
|
+
"responseClass" => response_class || "void",
|
40
|
+
"produces" => ["application/json", "application/xml"],
|
41
|
+
"parameters" => parameters,
|
42
|
+
"summary" => summary || description,
|
43
|
+
"notes" => notes,
|
44
|
+
"errorResponses" => error_responses
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_h
|
49
|
+
{
|
50
|
+
"path" => path,
|
51
|
+
"description" => description,
|
52
|
+
"operations" => [operation],
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def valid?
|
57
|
+
path.present?
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
##
|
62
|
+
# Example: [GET] /api/v2/ownerships.{format_type}
|
63
|
+
def parse_path(string)
|
64
|
+
@http_method, @path = string.match(/^\[(\w*)\]\s*(.*)$/).captures
|
65
|
+
|
66
|
+
path_params = @path.scan(/\{([^\}]+)\}/).flatten.reject { |value| value == "format_type" }
|
67
|
+
|
68
|
+
path_params.each do |path_param|
|
69
|
+
@parameters << {
|
70
|
+
"paramType" => "path",
|
71
|
+
"name" => path_param,
|
72
|
+
"description" => "Scope response to #{path_param}",
|
73
|
+
"dataType" => "string",
|
74
|
+
"required" => true,
|
75
|
+
"allowMultiple" => false
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Example: [Array] status Filter by status. (e.g. status[]=1&status[]=2&status[]=3)
|
82
|
+
# Example: [Array] status(required) Filter by status. (e.g. status[]=1&status[]=2&status[]=3)
|
83
|
+
# Example: [Integer] media[media_type_id] ID of the desired media type.
|
84
|
+
def parse_parameter(string)
|
85
|
+
data_type, name, required, description = string.match(/\A\[(\w*)\]\s*([\w\[\]]*)(\(required\))?\s*(.*)\Z/).captures
|
86
|
+
allow_multiple = name.gsub!("[]", "")
|
87
|
+
|
88
|
+
parameter = {
|
89
|
+
"paramType" => "query",
|
90
|
+
"name" => name,
|
91
|
+
"description" => description,
|
92
|
+
"dataType" => data_type.downcase,
|
93
|
+
"required" => required.present?,
|
94
|
+
"allowMultiple" => allow_multiple.present?
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Example: [String] sort_order Orders ownerships by fields. (e.g. sort_order=created_at)
|
100
|
+
# [List] id
|
101
|
+
# [List] begin_at
|
102
|
+
# [List] end_at
|
103
|
+
# [List] created_at
|
104
|
+
def parse_parameter_list(string)
|
105
|
+
data_type, name, required, description, set_string = string.match(/\A\[(\w*)\]\s*(\w*)(\(required\))?\s*(.*)\n([.\s\S]*)\Z/).captures
|
106
|
+
|
107
|
+
list_values = set_string.split("[List]").map(&:strip).reject { |string| string.empty? }
|
108
|
+
|
109
|
+
parameter = {
|
110
|
+
"paramType" => "query",
|
111
|
+
"name" => name,
|
112
|
+
"description" => description,
|
113
|
+
"dataType" => data_type.downcase,
|
114
|
+
"required" => required.present?,
|
115
|
+
"allowMultiple" => false,
|
116
|
+
"allowableValues" => {"valueType" => 'LIST', "values" => list_values}
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
def add_format_parameters
|
121
|
+
@add_format_parameters ||= {
|
122
|
+
"paramType" => "path",
|
123
|
+
"name" => "format_type",
|
124
|
+
"description" => "Response format either JSON or XML",
|
125
|
+
"dataType" => "string",
|
126
|
+
"required" => true,
|
127
|
+
"allowMultiple" => false,
|
128
|
+
"allowableValues" => {"valueType" => "LIST", "values" => ["json", "xml"]}
|
129
|
+
}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module SwaggerYard
|
2
|
+
class ApiDeclaration
|
3
|
+
attr_accessor :description, :resource_path
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@apis = {}
|
7
|
+
@models = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_listing_info(yard_object)
|
11
|
+
@description = yard_object.docstring if yard_object.docstring.present?
|
12
|
+
tag = yard_object.tags.find { |tag| tag.tag_name == "resource_path"}
|
13
|
+
@resource_path = tag.text.downcase if tag.present?
|
14
|
+
tag.present?
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_api(api)
|
18
|
+
if @apis.keys.include?(api.path)
|
19
|
+
same_api_path = @apis[api.path]
|
20
|
+
same_api_path["operations"] << api.operation
|
21
|
+
@apis[api.path] = same_api_path
|
22
|
+
else
|
23
|
+
@apis[api.path] = api.to_h
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def resource_name
|
28
|
+
@resource_path
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_h
|
32
|
+
{
|
33
|
+
"apiVersion" => SwaggerYard.api_version,
|
34
|
+
"swaggerVersion" => SwaggerYard.swagger_version,
|
35
|
+
"basePath" => SwaggerYard.api_base_path,
|
36
|
+
"resource_path" => @resource_path,
|
37
|
+
"apis" => @apis.values,
|
38
|
+
"models" => @models
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module SwaggerYard
|
2
|
+
# For now just a simple wrapper class for a Memcache client.
|
3
|
+
class Cache
|
4
|
+
|
5
|
+
attr_reader :prefix, :store
|
6
|
+
|
7
|
+
def initialize(store, prefix)
|
8
|
+
@store = store
|
9
|
+
@prefix = prefix
|
10
|
+
end
|
11
|
+
|
12
|
+
# Read from the Cache.
|
13
|
+
def [](resource_name)
|
14
|
+
case
|
15
|
+
when store.respond_to?(:read)
|
16
|
+
store.read key_for(resource_name)
|
17
|
+
when store.respond_to?(:[])
|
18
|
+
store[key_for(resource_name)]
|
19
|
+
when store.respond_to?(:get)
|
20
|
+
store.get key_for(resource_name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Write to the Cache.
|
25
|
+
def []=(resource_name, value)
|
26
|
+
case
|
27
|
+
when store.respond_to?(:write)
|
28
|
+
store.write key_for(resource_name), value
|
29
|
+
when store.respond_to?(:[]=)
|
30
|
+
store[key_for(resource_name)] = value
|
31
|
+
when store.respond_to?(:set)
|
32
|
+
store.set key_for(resource_name), value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def fetch(resource_name)
|
37
|
+
value = self[resource_name]
|
38
|
+
if value.nil? && block_given?
|
39
|
+
value = yield
|
40
|
+
self[resource_name] = yield
|
41
|
+
end
|
42
|
+
value
|
43
|
+
end
|
44
|
+
|
45
|
+
# Cache key for a given entry.
|
46
|
+
def key_for(resource_name)
|
47
|
+
[prefix, resource_name].join
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "swagger_yard/local_dispatcher"
|
2
|
+
|
3
|
+
module SwaggerYard
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
if SwaggerYard::LocalDispatcher.new.server?
|
6
|
+
isolate_namespace SwaggerYard
|
7
|
+
|
8
|
+
# NOTE: We should opt for asset pipeline instead of this.
|
9
|
+
initializer 'swagger_yard.load_static_assets' do |app|
|
10
|
+
app.middleware.use(::ActionDispatch::Static, "#{root}/public")
|
11
|
+
end
|
12
|
+
|
13
|
+
initializer "swagger_yard.finisher_hook" do |app|
|
14
|
+
SwaggerYard.generate!("#{app.root}/app/controllers/**/*.rb")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module SwaggerYard
|
2
|
+
# An instance of LocalEnvironment is responsible for determining the dispatcher.
|
3
|
+
# This is useful to determine whether or not to run SwaggerYard#generate!.
|
4
|
+
#
|
5
|
+
# Implementation heavily borrowed from NewRelic.
|
6
|
+
class LocalDispatcher
|
7
|
+
def discovered_dispatcher
|
8
|
+
discover_dispatcher unless @discovered_dispatcher
|
9
|
+
@discovered_dispatcher
|
10
|
+
end
|
11
|
+
|
12
|
+
def server?
|
13
|
+
[:thin, :unicorn].include?(discovered_dispatcher)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def discover_dispatcher
|
19
|
+
dispatchers = %w[sidekiq thin unicorn]
|
20
|
+
while dispatchers.any? && @discovered_dispatcher.nil?
|
21
|
+
send 'check_for_' + (dispatchers.shift)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_class_in_object_space(klass)
|
26
|
+
ObjectSpace.each_object(klass) do |x|
|
27
|
+
return x
|
28
|
+
end
|
29
|
+
return nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def check_for_unicorn
|
33
|
+
if defined?(::Unicorn) && defined?(::Unicorn::HttpServer)
|
34
|
+
v = find_class_in_object_space(::Unicorn::HttpServer)
|
35
|
+
@discovered_dispatcher = :unicorn if v
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_for_sidekiq
|
40
|
+
if defined?(::Sidekiq) && File.basename($0) == 'sidekiq'
|
41
|
+
@discovered_dispatcher = :sidekiq
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def check_for_thin
|
46
|
+
if defined?(::Thin) && defined?(::Thin::VERSION)
|
47
|
+
@discovered_dispatcher = :thin
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'api'
|
2
|
+
require_relative 'api_declaration'
|
3
|
+
require_relative 'resource_listing'
|
4
|
+
|
5
|
+
module SwaggerYard
|
6
|
+
class Parser
|
7
|
+
attr_reader :listing
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@listing = ResourceListing.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def run(yard_objects)
|
14
|
+
api_declaration = ApiDeclaration.new
|
15
|
+
retain_api = true
|
16
|
+
|
17
|
+
yard_objects.each do |yard_object|
|
18
|
+
case yard_object.type
|
19
|
+
when :class
|
20
|
+
break unless retain_api = api_declaration.add_listing_info(yard_object)
|
21
|
+
when :method
|
22
|
+
api = Api.new(yard_object)
|
23
|
+
api_declaration.add_api(api) if api.valid?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if retain_api
|
28
|
+
@listing.add(api_declaration)
|
29
|
+
api_declaration
|
30
|
+
else
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SwaggerYard
|
2
|
+
class ResourceListing
|
3
|
+
attr_reader :api_declarations
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@api_declarations = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def add(api_declaration)
|
10
|
+
@api_declarations << api_declaration
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_h
|
14
|
+
{
|
15
|
+
"apiVersion" => SwaggerYard.api_version,
|
16
|
+
"swaggerVersion" => SwaggerYard.swagger_version,
|
17
|
+
"basePath" => SwaggerYard.doc_base_path,
|
18
|
+
"apis" => list_api
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def list_api
|
24
|
+
@api_declarations.map do |api_declaration|
|
25
|
+
{
|
26
|
+
"path" => api_declaration.resource_path,
|
27
|
+
"description" => api_declaration.description
|
28
|
+
}
|
29
|
+
end.sort_by { |hsh| hsh["path"] }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
/*
|
2
|
+
|
3
|
+
Original style from softwaremaniacs.org (c) Ivan Sagalaev <Maniac@SoftwareManiacs.Org>
|
4
|
+
|
5
|
+
*/
|
6
|
+
|
7
|
+
pre code {
|
8
|
+
display: block; padding: 0.5em;
|
9
|
+
background: #F0F0F0;
|
10
|
+
}
|
11
|
+
|
12
|
+
pre code,
|
13
|
+
pre .subst,
|
14
|
+
pre .tag .title,
|
15
|
+
pre .lisp .title,
|
16
|
+
pre .clojure .built_in,
|
17
|
+
pre .nginx .title {
|
18
|
+
color: black;
|
19
|
+
}
|
20
|
+
|
21
|
+
pre .string,
|
22
|
+
pre .title,
|
23
|
+
pre .constant,
|
24
|
+
pre .parent,
|
25
|
+
pre .tag .value,
|
26
|
+
pre .rules .value,
|
27
|
+
pre .rules .value .number,
|
28
|
+
pre .preprocessor,
|
29
|
+
pre .ruby .symbol,
|
30
|
+
pre .ruby .symbol .string,
|
31
|
+
pre .aggregate,
|
32
|
+
pre .template_tag,
|
33
|
+
pre .django .variable,
|
34
|
+
pre .smalltalk .class,
|
35
|
+
pre .addition,
|
36
|
+
pre .flow,
|
37
|
+
pre .stream,
|
38
|
+
pre .bash .variable,
|
39
|
+
pre .apache .tag,
|
40
|
+
pre .apache .cbracket,
|
41
|
+
pre .tex .command,
|
42
|
+
pre .tex .special,
|
43
|
+
pre .erlang_repl .function_or_atom,
|
44
|
+
pre .markdown .header {
|
45
|
+
color: #800;
|
46
|
+
}
|
47
|
+
|
48
|
+
pre .comment,
|
49
|
+
pre .annotation,
|
50
|
+
pre .template_comment,
|
51
|
+
pre .diff .header,
|
52
|
+
pre .chunk,
|
53
|
+
pre .markdown .blockquote {
|
54
|
+
color: #888;
|
55
|
+
}
|
56
|
+
|
57
|
+
pre .number,
|
58
|
+
pre .date,
|
59
|
+
pre .regexp,
|
60
|
+
pre .literal,
|
61
|
+
pre .smalltalk .symbol,
|
62
|
+
pre .smalltalk .char,
|
63
|
+
pre .go .constant,
|
64
|
+
pre .change,
|
65
|
+
pre .markdown .bullet,
|
66
|
+
pre .markdown .link_url {
|
67
|
+
color: #080;
|
68
|
+
}
|
69
|
+
|
70
|
+
pre .label,
|
71
|
+
pre .javadoc,
|
72
|
+
pre .ruby .string,
|
73
|
+
pre .decorator,
|
74
|
+
pre .filter .argument,
|
75
|
+
pre .localvars,
|
76
|
+
pre .array,
|
77
|
+
pre .attr_selector,
|
78
|
+
pre .important,
|
79
|
+
pre .pseudo,
|
80
|
+
pre .pi,
|
81
|
+
pre .doctype,
|
82
|
+
pre .deletion,
|
83
|
+
pre .envvar,
|
84
|
+
pre .shebang,
|
85
|
+
pre .apache .sqbracket,
|
86
|
+
pre .nginx .built_in,
|
87
|
+
pre .tex .formula,
|
88
|
+
pre .erlang_repl .reserved,
|
89
|
+
pre .prompt,
|
90
|
+
pre .markdown .link_label,
|
91
|
+
pre .vhdl .attribute,
|
92
|
+
pre .clojure .attribute,
|
93
|
+
pre .coffeescript .property {
|
94
|
+
color: #88F
|
95
|
+
}
|
96
|
+
|
97
|
+
pre .keyword,
|
98
|
+
pre .id,
|
99
|
+
pre .phpdoc,
|
100
|
+
pre .title,
|
101
|
+
pre .built_in,
|
102
|
+
pre .aggregate,
|
103
|
+
pre .css .tag,
|
104
|
+
pre .javadoctag,
|
105
|
+
pre .phpdoc,
|
106
|
+
pre .yardoctag,
|
107
|
+
pre .smalltalk .class,
|
108
|
+
pre .winutils,
|
109
|
+
pre .bash .variable,
|
110
|
+
pre .apache .tag,
|
111
|
+
pre .go .typename,
|
112
|
+
pre .tex .command,
|
113
|
+
pre .markdown .strong,
|
114
|
+
pre .request,
|
115
|
+
pre .status {
|
116
|
+
font-weight: bold;
|
117
|
+
}
|
118
|
+
|
119
|
+
pre .markdown .emphasis {
|
120
|
+
font-style: italic;
|
121
|
+
}
|
122
|
+
|
123
|
+
pre .nginx .built_in {
|
124
|
+
font-weight: normal;
|
125
|
+
}
|
126
|
+
|
127
|
+
pre .coffeescript .javascript,
|
128
|
+
pre .javascript .xml,
|
129
|
+
pre .tex .formula,
|
130
|
+
pre .xml .javascript,
|
131
|
+
pre .xml .vbscript,
|
132
|
+
pre .xml .css,
|
133
|
+
pre .xml .cdata {
|
134
|
+
opacity: 0.5;
|
135
|
+
}
|