apispec 0.0.3
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.
- data/LICENSE +21 -0
- data/README.md +1 -0
- data/Rakefile +9 -0
- data/apispec-0.0.2.gem +0 -0
- data/apispec.gemspec +37 -0
- data/bin/apispec +112 -0
- data/example/datagram/user.json +7 -0
- data/example/example.rb +59 -0
- data/example/user.rb +34 -0
- data/lib/apispec/example.rb +10 -0
- data/lib/apispec/field.rb +37 -0
- data/lib/apispec/generator.rb +104 -0
- data/lib/apispec/http.rb +53 -0
- data/lib/apispec/interface.rb +29 -0
- data/lib/apispec/message.rb +56 -0
- data/lib/apispec/namespace.rb +113 -0
- data/lib/apispec/node.rb +52 -0
- data/lib/apispec/object.rb +34 -0
- data/lib/apispec/resource.rb +43 -0
- data/lib/apispec/version.rb +6 -0
- data/lib/apispec.rb +15 -0
- data/resources/jquery.min.js +154 -0
- data/resources/links.css +42 -0
- data/resources/stripe.png +0 -0
- data/resources/style.css +260 -0
- data/spec/dsl/example_spec.rb +5 -0
- data/spec/dsl/field_spec.rb +5 -0
- data/spec/dsl/interface_spec.rb +5 -0
- data/spec/dsl/message_spec.rb +5 -0
- data/spec/dsl/object_spec.rb +5 -0
- data/spec/dsl/resource_spec.rb +5 -0
- data/spec/generator_spec.rb +28 -0
- data/spec/spec_helper.rb +1 -0
- data/templates/field.html.erb +24 -0
- data/templates/index.html.erb +11 -0
- data/templates/interface.html.erb +19 -0
- data/templates/links.html.erb +26 -0
- data/templates/message.html.erb +38 -0
- data/templates/object.html.erb +27 -0
- data/templates/resource.html.erb +26 -0
- metadata +160 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Vincent Landgraf
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
TODO
|
data/Rakefile
ADDED
data/apispec-0.0.2.gem
ADDED
Binary file
|
data/apispec.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
$:.push('lib')
|
2
|
+
require "apispec/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'apispec'
|
6
|
+
s.version = APISpec::VERSION.dup
|
7
|
+
s.date = Time.now
|
8
|
+
s.summary = "A ruby based http/rest documentation generator"
|
9
|
+
s.email = "vilandgr+github@googlemail.com"
|
10
|
+
s.homepage = "http://github.com/threez/apispec/"
|
11
|
+
s.authors = ['Vincent Landgraf']
|
12
|
+
s.description = "A documentation generator for http/rest"
|
13
|
+
|
14
|
+
dependencies = [
|
15
|
+
[:runtime, "RedCloth", "~> 4.2.7"],
|
16
|
+
[:runtime, "coderay", "~> 0.9.7"],
|
17
|
+
[:development, "rspec", "~> 2.1"],
|
18
|
+
]
|
19
|
+
|
20
|
+
s.files = Dir['**/*']
|
21
|
+
s.test_files = Dir['test/**/*'] + Dir['spec/**/*']
|
22
|
+
s.executables = Dir['bin/*'].map { |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
|
25
|
+
## Make sure you can build the gem on older versions of RubyGems too:
|
26
|
+
s.rubygems_version = APISpec::VERSION.dup
|
27
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
28
|
+
s.specification_version = 3 if s.respond_to? :specification_version
|
29
|
+
|
30
|
+
dependencies.each do |type, name, version|
|
31
|
+
if s.respond_to?("add_#{type}_dependency")
|
32
|
+
s.send("add_#{type}_dependency", name, version)
|
33
|
+
else
|
34
|
+
s.add_dependency(name, version)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/bin/apispec
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
# grab the project root for tempaltes and the lib
|
5
|
+
project_root = File.join(File.dirname(__FILE__), "..")
|
6
|
+
|
7
|
+
require File.expand_path(File.join(project_root, "lib", "apispec"))
|
8
|
+
|
9
|
+
# the default tempate dir
|
10
|
+
template_dir = File.join(project_root, "templates")
|
11
|
+
resource_dir = File.join(project_root, "resources")
|
12
|
+
|
13
|
+
class DirectoryError < StandardError; end
|
14
|
+
def require_directory!(dir)
|
15
|
+
dir = File.expand_path(dir)
|
16
|
+
raise DirectoryError.new("directory #{dir} doesn't exist!") unless File.exist? dir
|
17
|
+
dir
|
18
|
+
end
|
19
|
+
|
20
|
+
options = {
|
21
|
+
:workspace => File.expand_path("."),
|
22
|
+
:template => File.expand_path(template_dir),
|
23
|
+
:output => File.expand_path("apidoc"),
|
24
|
+
:resource => File.expand_path(resource_dir)
|
25
|
+
}
|
26
|
+
parser = nil
|
27
|
+
|
28
|
+
begin
|
29
|
+
parser = OptionParser.new do |opts|
|
30
|
+
opts.banner = "Usage: #{$0} [options]"
|
31
|
+
opts.version = APISpec::VERSION
|
32
|
+
|
33
|
+
opts.separator ""
|
34
|
+
opts.separator "No existing project options:"
|
35
|
+
|
36
|
+
opts.on("-c", "--create <name>", "create a new project") do |name|
|
37
|
+
options[:create] = name
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.separator ""
|
41
|
+
opts.separator "Project options:"
|
42
|
+
|
43
|
+
opts.on("-w", "--workspace <dir>",
|
44
|
+
"The directory containing the source files (default: .)") do |dir|
|
45
|
+
options[:workspace] = require_directory!(dir)
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on("-t", "--template <dir>",
|
49
|
+
"The template directory (default: bundled with apispec)") do |dir|
|
50
|
+
options[:template] = require_directory!(dir)
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("-r", "--rescoure <dir>",
|
54
|
+
"The resource directory (default: bundled with apispec)") do |dir|
|
55
|
+
options[:template] = require_directory!(dir)
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on("-p", "--print",
|
59
|
+
"The resource directory (default: bundled with apispec)") do
|
60
|
+
options[:print] = true
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on("-o", "--output <dir>",
|
64
|
+
"The output directory (default: apidoc)") do |dir|
|
65
|
+
options[:output] = File.expand_path(dir)
|
66
|
+
end
|
67
|
+
|
68
|
+
# opts.on("-s", "--server <port>", "Don't create a output folder start a server on given port") do |dir|
|
69
|
+
# options[:output] = dir
|
70
|
+
# end
|
71
|
+
|
72
|
+
opts.separator ""
|
73
|
+
opts.separator "Common options:"
|
74
|
+
|
75
|
+
opts.on("-v", "--verbose", "Run verbosely") do
|
76
|
+
options[:verbose] = true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
parser.parse!
|
80
|
+
rescue DirectoryError => ex
|
81
|
+
STDERR.puts "Invalid Argument: #{ex.message}"
|
82
|
+
STDERR.puts parser.help
|
83
|
+
exit 3
|
84
|
+
rescue OptionParser::ParseError => ex
|
85
|
+
STDERR.puts ex.message
|
86
|
+
STDERR.puts parser.help
|
87
|
+
exit 2
|
88
|
+
end
|
89
|
+
|
90
|
+
if options[:print]
|
91
|
+
# just print the structure
|
92
|
+
generator = APISpec::Generator.new(options)
|
93
|
+
generator.parse_files!
|
94
|
+
puts generator.namespace.print_tree()
|
95
|
+
elsif dir = options[:create]
|
96
|
+
# create a new project based on the example project
|
97
|
+
new_dir = File.expand_path(dir)
|
98
|
+
FileUtils.mkdir_p(new_dir)
|
99
|
+
FileUtils.cp_r(Dir[File.expand_path(File.join(project_root, "example", "*"))],
|
100
|
+
new_dir)
|
101
|
+
elsif Dir[File.join(options[:workspace], "**", "*.rb")].any?
|
102
|
+
begin
|
103
|
+
APISpec::Generator.new(options).start!
|
104
|
+
rescue APISpec::Namespace::ReferenceError => ex
|
105
|
+
STDERR.puts ex.message
|
106
|
+
exit 4
|
107
|
+
end
|
108
|
+
else
|
109
|
+
STDERR.puts "There are no files to generate a documentation from!"
|
110
|
+
STDERR.puts parser.help
|
111
|
+
exit 5
|
112
|
+
end
|
data/example/example.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
field "X-User", "UserID" do
|
2
|
+
desc "The users email address"
|
3
|
+
example "test@example.com"
|
4
|
+
end
|
5
|
+
|
6
|
+
field "folder_id", "FolderID" do
|
7
|
+
desc "unique id of folder for user"
|
8
|
+
end
|
9
|
+
|
10
|
+
object "Folder" do
|
11
|
+
fields "FolderID"
|
12
|
+
example :text, %q{
|
13
|
+
asd
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
interface "Folders" do
|
18
|
+
base_uri "http://example.com/mail-rest"
|
19
|
+
|
20
|
+
get 'folders' do
|
21
|
+
desc "returns the list of folders"
|
22
|
+
|
23
|
+
request do
|
24
|
+
headers "UserID"
|
25
|
+
end
|
26
|
+
|
27
|
+
response do
|
28
|
+
example :json, %q|
|
29
|
+
{
|
30
|
+
"Kreditkarte" : "Xema",
|
31
|
+
"Nummer" : "1234-5678-9012-3456",
|
32
|
+
"Inhaber" : {
|
33
|
+
"Name" : "Reich",
|
34
|
+
"Vorname" : "Rainer",
|
35
|
+
"Geschlecht" : "männlich",
|
36
|
+
"Vorlieben" : [ "Reiten", "Schwimmen", "Lesen" ],
|
37
|
+
"Alter" : null
|
38
|
+
},
|
39
|
+
"Deckung" : 2e+6,
|
40
|
+
"Währung" : "EURO"
|
41
|
+
}
|
42
|
+
|
|
43
|
+
array "Folder"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
get 'folder/:folder_id' do
|
48
|
+
desc "returns the mail header objects of the requested Folder"
|
49
|
+
|
50
|
+
request do
|
51
|
+
headers "UserID"
|
52
|
+
params "FolderID"
|
53
|
+
end
|
54
|
+
|
55
|
+
response do
|
56
|
+
object "Folder"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/example/user.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
namespace "Account" do
|
2
|
+
field("id") { type :integer }
|
3
|
+
field("firstname") { example "John" }
|
4
|
+
field("lastname") { example "Doe" }
|
5
|
+
field("login") { example "john.doe" }
|
6
|
+
field("password") { example "secretxxx" }
|
7
|
+
|
8
|
+
object "User" do
|
9
|
+
fields "Account.id", "Account.firstname", "Account.lastname",
|
10
|
+
"Account.login", "Account.password"
|
11
|
+
example_file :json, "datagram/user.json"
|
12
|
+
end
|
13
|
+
|
14
|
+
interface "UserRest" do
|
15
|
+
base_uri "/account"
|
16
|
+
|
17
|
+
get "users/" do
|
18
|
+
response do
|
19
|
+
array "Account.User"
|
20
|
+
example_file :json, "datagram/user.json"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
interface "SystemCheck" do
|
27
|
+
get "check" do
|
28
|
+
desc "returns true if the system is ok otherwise false"
|
29
|
+
|
30
|
+
response do
|
31
|
+
example :text, "OK"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class APISpec::Field < APISpec::Node
|
2
|
+
# default type is String and field is not optional
|
3
|
+
def initialize(name, &block)
|
4
|
+
@optional = false
|
5
|
+
@nullable = false
|
6
|
+
@type = :string
|
7
|
+
super(name, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def type(value)
|
11
|
+
@type = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def desc(value)
|
15
|
+
@desc = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def default(value)
|
19
|
+
@default = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def optional(value)
|
23
|
+
@optional = value
|
24
|
+
end
|
25
|
+
|
26
|
+
def nullable(value)
|
27
|
+
@nullable = value
|
28
|
+
end
|
29
|
+
|
30
|
+
def example(value)
|
31
|
+
@example = value
|
32
|
+
end
|
33
|
+
|
34
|
+
def resolve_references!(root_namespace)
|
35
|
+
@type = root_namespace.find_object(@type) if @type.is_a? String
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require "logger"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
class APISpec::Generator
|
5
|
+
attr_reader :namespace, :logger
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@options = options
|
9
|
+
# logging
|
10
|
+
@logger = Logger.new(STDOUT)
|
11
|
+
@logger.level = Logger::WARN unless options[:verbose]
|
12
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
13
|
+
"#{datetime.strftime("%Y-%m-%d %H:%M:%S")} [#{severity}]: #{msg}\n"
|
14
|
+
end
|
15
|
+
@namespace = APISpec::Namespace.new(nil)
|
16
|
+
end
|
17
|
+
|
18
|
+
# returns the dir with the root based in the working directory
|
19
|
+
def path(dir)
|
20
|
+
File.join(@options[:workspace], dir)
|
21
|
+
end
|
22
|
+
|
23
|
+
# read all ruby files that can be found in the working directory
|
24
|
+
def parse_files!
|
25
|
+
@logger.info "parse all files"
|
26
|
+
Dir[File.join(@options[:workspace], "**", "*.rb")].each do |path|
|
27
|
+
logger.info "read file #{path}..."
|
28
|
+
@namespace.read_file(path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# creates the output folder
|
33
|
+
def create_output_folder!
|
34
|
+
@logger.info "remove and create the output folder"
|
35
|
+
FileUtils.rm_rf "#{@options[:output]}"
|
36
|
+
FileUtils.mkdir_p @options[:output]
|
37
|
+
end
|
38
|
+
|
39
|
+
# create the index frame
|
40
|
+
def create_index!
|
41
|
+
@logger.info "create index frame"
|
42
|
+
File.open(File.join("#{@options[:output]}", "index.html"), "w") do |file|
|
43
|
+
file.write(template(binding, :index))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# create the links page (left part of frame)
|
48
|
+
def create_links!
|
49
|
+
@logger.info "create links page"
|
50
|
+
File.open(File.join("#{@options[:output]}", "links.html"), "w") do |file|
|
51
|
+
file.write(template(binding, :links))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# create the documentation files itself
|
56
|
+
def create_objects_and_interfaces!
|
57
|
+
@namespace.objects.each do |object|
|
58
|
+
create(object)
|
59
|
+
end
|
60
|
+
@namespace.interfaces.each do |interface|
|
61
|
+
create(interface)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# create the resources for all subfolders and main folder
|
66
|
+
def create_resources!
|
67
|
+
Dir[File.join(@options[:output], "**/*")].map do |path|
|
68
|
+
File.dirname(File.expand_path(path))
|
69
|
+
end.uniq.each do |path|
|
70
|
+
@logger.info "copy template resources to #{path}"
|
71
|
+
FileUtils.cp Dir[File.join(@options[:resource], "*")], path
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# start the generator
|
76
|
+
def start!
|
77
|
+
parse_files!
|
78
|
+
create_output_folder!
|
79
|
+
create_index!
|
80
|
+
create_links!
|
81
|
+
create_objects_and_interfaces!
|
82
|
+
create_resources!
|
83
|
+
end
|
84
|
+
|
85
|
+
# render a template with the passed object as binding
|
86
|
+
def template(object, template_name)
|
87
|
+
path = File.join(@options[:template], "#{template_name}.html.erb")
|
88
|
+
erb = ERB.new(File.read(path))
|
89
|
+
erb.filename = path
|
90
|
+
erb.result(object)
|
91
|
+
end
|
92
|
+
|
93
|
+
# create a doc file for the passed node
|
94
|
+
def create(node)
|
95
|
+
dir = File.dirname(node.to_path)
|
96
|
+
file_name = File.basename(node.to_path)
|
97
|
+
path = File.join("#{@options[:output]}", dir)
|
98
|
+
FileUtils.mkdir_p path
|
99
|
+
File.open(File.join(path, file_name), "w") do |file|
|
100
|
+
@logger.info "create page for #{node}"
|
101
|
+
file.write(node.to_html(self))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/apispec/http.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module APISpec
|
2
|
+
HTTP_STATUS_CODES = {
|
3
|
+
100 => "Continue",
|
4
|
+
101 => "Switching Protocols",
|
5
|
+
102 => "Processing",
|
6
|
+
200 => "OK",
|
7
|
+
201 => "Created",
|
8
|
+
202 => "Accepted",
|
9
|
+
203 => "Non-Authoritative Information",
|
10
|
+
204 => "No Content",
|
11
|
+
205 => "Reset Content",
|
12
|
+
206 => "Partial Content",
|
13
|
+
207 => "Multi-Status",
|
14
|
+
226 => "IM Used",
|
15
|
+
300 => "Multiple Choices",
|
16
|
+
301 => "Moved Permanently",
|
17
|
+
302 => "Found",
|
18
|
+
303 => "See Other",
|
19
|
+
304 => "Not Modified",
|
20
|
+
305 => "Use Proxy",
|
21
|
+
307 => "Temporary Redirect",
|
22
|
+
400 => "Bad Request",
|
23
|
+
401 => "Unauthorized",
|
24
|
+
402 => "Payment Required",
|
25
|
+
403 => "Forbidden",
|
26
|
+
404 => "Not Found",
|
27
|
+
405 => "Method Not Allowed",
|
28
|
+
406 => "Not Acceptable",
|
29
|
+
407 => "Proxy Authentication Required",
|
30
|
+
408 => "Request Timeout",
|
31
|
+
409 => "Conflict",
|
32
|
+
410 => "Gone",
|
33
|
+
411 => "Length Required",
|
34
|
+
412 => "Precondition Failed",
|
35
|
+
413 => "Request Entity Too Large",
|
36
|
+
414 => "Request-URI Too Long",
|
37
|
+
415 => "Unsupported Media Type",
|
38
|
+
416 => "Requested Range Not Satisfiable",
|
39
|
+
417 => "Expectation Failed",
|
40
|
+
422 => "Unprocessable Entity",
|
41
|
+
423 => "Locked",
|
42
|
+
424 => "Failed Dependency",
|
43
|
+
426 => "Upgrade Required",
|
44
|
+
500 => "Internal Server Error",
|
45
|
+
501 => "Not Implemented",
|
46
|
+
502 => "Bad Gateway",
|
47
|
+
503 => "Service Unavailable",
|
48
|
+
504 => "Gateway Timeout",
|
49
|
+
505 => "HTTP Version Not Supported",
|
50
|
+
507 => "Insufficient Storage",
|
51
|
+
510 => "Not Extended"
|
52
|
+
}
|
53
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class APISpec::Interface < APISpec::Node
|
2
|
+
def initialize(name, &block)
|
3
|
+
@resources = []
|
4
|
+
super(name, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def base_uri(value = nil)
|
8
|
+
@base_uri ||= value
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(path, &block); http(:get, path, &block); end
|
12
|
+
def put(path, &block); http(:put, path, &block); end
|
13
|
+
def post(path, &block); http(:post, path, &block); end
|
14
|
+
def delete(path, &block); http(:delete, path, &block); end
|
15
|
+
def options(path, &block); http(:options, path, &block); end
|
16
|
+
def head(path, &block); http(:head, path, &block); end
|
17
|
+
def trace(path, &block); http(:trace, path, &block); end
|
18
|
+
def connect(path, &block); http(:connect, path, &block); end
|
19
|
+
|
20
|
+
def http(method, path, &block)
|
21
|
+
@resources << APISpec::Resource.new(method, path, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def resolve_references!(root_namespace)
|
25
|
+
@resources.each do |resource|
|
26
|
+
resource.resolve_references!(root_namespace)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class APISpec::Message
|
2
|
+
def initialize(&block)
|
3
|
+
@headers = []
|
4
|
+
@parameters = []
|
5
|
+
instance_eval(&block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def headers(*value)
|
9
|
+
@headers = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def params(*value)
|
13
|
+
@parameters = value
|
14
|
+
end
|
15
|
+
|
16
|
+
def desc(value)
|
17
|
+
@desc = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def object(value)
|
21
|
+
@object = value
|
22
|
+
end
|
23
|
+
|
24
|
+
def array(value)
|
25
|
+
@array = value
|
26
|
+
end
|
27
|
+
|
28
|
+
def content_desc(value)
|
29
|
+
@content_desc = value
|
30
|
+
end
|
31
|
+
|
32
|
+
def example(format, value)
|
33
|
+
@example = APISpec::Example.new(format, value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def example_file(format, path)
|
37
|
+
@example = [format, path]
|
38
|
+
end
|
39
|
+
|
40
|
+
def resolve_references!(root_namespace)
|
41
|
+
@headers = @headers.map { |name| root_namespace.find_field(name) }.flatten
|
42
|
+
@parameters = @parameters.map { |name| root_namespace.find_field(name) }.flatten
|
43
|
+
@object = root_namespace.find_object(@object) if @object
|
44
|
+
@array = root_namespace.find_object(@array) if @array
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_html(generator, resource)
|
48
|
+
@generator = generator
|
49
|
+
@resource = resource
|
50
|
+
if @example.is_a? Array
|
51
|
+
format, path = @example
|
52
|
+
@example = APISpec::Example.new(format, File.read(@generator.path(path)))
|
53
|
+
end
|
54
|
+
generator.template(binding, :message)
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
class APISpec::Namespace < APISpec::Node
|
2
|
+
class AlreadyDefinedError < StandardError; end
|
3
|
+
class ReferenceError < StandardError; end
|
4
|
+
|
5
|
+
def initialize(name, &block)
|
6
|
+
@nodes = {}
|
7
|
+
super(name, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
# reads the passed file
|
11
|
+
def read_file(path)
|
12
|
+
eval(File.read(path), binding, path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def find_field(path)
|
16
|
+
result = find(path)
|
17
|
+
if result.is_a? Array
|
18
|
+
result.map { |path| find(path) }
|
19
|
+
else
|
20
|
+
[result]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_object(path)
|
25
|
+
find(path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def find(path)
|
29
|
+
path_parts = path.split(".")
|
30
|
+
node = self
|
31
|
+
while path_parts.any?
|
32
|
+
node = node.find_node(path_parts.shift)
|
33
|
+
raise ReferenceError.new("#{path} not found in #{self.to_s}") unless node
|
34
|
+
end
|
35
|
+
node
|
36
|
+
end
|
37
|
+
|
38
|
+
def all(method, type)
|
39
|
+
nodes = []
|
40
|
+
@nodes.keys.sort.each do |name|
|
41
|
+
node = @nodes[name]
|
42
|
+
if node.is_a? APISpec::Namespace
|
43
|
+
nodes << node.send(method)
|
44
|
+
elsif node.is_a? type
|
45
|
+
nodes << node
|
46
|
+
end
|
47
|
+
end
|
48
|
+
nodes.flatten
|
49
|
+
end
|
50
|
+
|
51
|
+
def interfaces
|
52
|
+
all(:interfaces, APISpec::Interface)
|
53
|
+
end
|
54
|
+
|
55
|
+
def objects
|
56
|
+
all(:objects, APISpec::Object)
|
57
|
+
end
|
58
|
+
|
59
|
+
def find_node(name)
|
60
|
+
@nodes[name]
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
"#{super} (Nodes: #{@nodes.size})"
|
65
|
+
end
|
66
|
+
|
67
|
+
# print the structure of all namespaces
|
68
|
+
def print_tree(indent = 0, lines = [])
|
69
|
+
lines << (" " * indent) + self.to_s
|
70
|
+
@nodes.keys.sort.each do |reference|
|
71
|
+
node = @nodes[reference]
|
72
|
+
if node.is_a? APISpec::Namespace
|
73
|
+
node.print_tree(indent + 1, lines)
|
74
|
+
else
|
75
|
+
lines << (" " * (indent + 1)) + "#{reference} => #{node.to_s}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
lines.join("\n")
|
79
|
+
end
|
80
|
+
|
81
|
+
def namespace(name, &block)
|
82
|
+
if node = @nodes[name]
|
83
|
+
node.instance_eval &block
|
84
|
+
else
|
85
|
+
define_node name, APISpec::Namespace.new(name, &block)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def define_node(name, node)
|
90
|
+
raise AlreadyDefinedError.new("#{name} is already defined!") if @nodes[name]
|
91
|
+
@nodes[name] = node
|
92
|
+
node.parent = self if node.respond_to? :parent
|
93
|
+
end
|
94
|
+
|
95
|
+
def interface(name, reference = nil, &block)
|
96
|
+
reference = reference || name
|
97
|
+
define_node name, APISpec::Interface.new(name, &block)
|
98
|
+
end
|
99
|
+
|
100
|
+
def object(name, reference = nil, &block)
|
101
|
+
reference = reference || name
|
102
|
+
define_node reference, APISpec::Object.new(name, &block)
|
103
|
+
end
|
104
|
+
|
105
|
+
def field(name, reference = nil, &block)
|
106
|
+
reference = reference || name
|
107
|
+
define_node reference, APISpec::Field.new(name, &block)
|
108
|
+
end
|
109
|
+
|
110
|
+
def field_set(reference = nil, *fields)
|
111
|
+
define_node reference, fields
|
112
|
+
end
|
113
|
+
end
|