prmd 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +19 -0
- data/.travis.yml +2 -0
- data/CONTRIBUTORS.md +6 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +23 -0
- data/LICENSE.md +20 -0
- data/README.md +72 -0
- data/Rakefile +8 -0
- data/bin/prmd +104 -0
- data/docs/schemata.md +162 -0
- data/lib/prmd.rb +15 -0
- data/lib/prmd/commands/combine.rb +5 -0
- data/lib/prmd/commands/doc.rb +110 -0
- data/lib/prmd/commands/expand.rb +113 -0
- data/lib/prmd/commands/init.rb +101 -0
- data/lib/prmd/commands/verify.rb +66 -0
- data/lib/prmd/schema.rb +107 -0
- data/lib/prmd/version.rb +3 -0
- data/lib/prmd/views/endpoint.erb +107 -0
- data/lib/prmd/views/parameters.erb +16 -0
- data/prmd.gemspec +27 -0
- data/test/helpers.rb +10 -0
- data/test/schema_test.rb +13 -0
- data/test/schemas/input/user.json +102 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YWJmODRjMTMyNjBmNGE5N2JmZTk4ZmI2MzZiZTM3NDBkZmEyNTI5Yw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MjJiNjE4ZjA0ODUzNjlhNGZkZmVhZmY3MmRhMzY1MDFkYTM0M2QxMw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MThmMTQ3ODhhNDMwNTliZjk4NWY0N2JiOWU3MDk4ZjI1YzE2NDJhNjQ3YjIy
|
10
|
+
ZGU3OGVjYzBiNjZhNmVhNzljMjY5ZWE0YTJlMjg4NmMyN2JiOWQ1ZDM4N2E0
|
11
|
+
M2FjNDdjMmNmYzBlZjBiZjk1ZWQ4NWM3MDhiYmVmYTQ3MTYwMDA=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZDMzZWZjMzAyZDYxNjg5NTRmZjRjODJjMDNkOTZmNmNhM2QzNzRjN2M2OGVi
|
14
|
+
Yjg3NWE1ZWE2Njk5M2Y5ODg4NDQ1NzlmNjZmZTQxY2E4ZWY0YTJmMzI0NGUz
|
15
|
+
MjM5ZjljNDMxZGNjNGQ3MTI1MmQ3Mjk5MWYyMjM2OGRjMjJkMmI=
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CONTRIBUTORS.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
prmd (0.0.1)
|
5
|
+
diff-lcs
|
6
|
+
erubis
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
diff-lcs (1.2.5)
|
12
|
+
erubis (2.7.0)
|
13
|
+
minitest (4.7.5)
|
14
|
+
rake (10.1.0)
|
15
|
+
|
16
|
+
PLATFORMS
|
17
|
+
ruby
|
18
|
+
|
19
|
+
DEPENDENCIES
|
20
|
+
bundler (~> 1.3)
|
21
|
+
minitest
|
22
|
+
prmd!
|
23
|
+
rake
|
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013-2014 [CONTRIBUTORS.md](https://github.com/heroku/prmd/blob/master/CONTRIBUTORS.md)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
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, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# Prmd
|
2
|
+
|
3
|
+
schema to rule them all
|
4
|
+
|
5
|
+
## Introduction
|
6
|
+
|
7
|
+
JSON-Schema provides a great way to describe an API. prmd provides tools for bootstrapping a description like this, verifying it's completeness and generating documentation from the specification.
|
8
|
+
|
9
|
+
The expectations for json-schema usage that are expected by prmd are described in [/docs/schemata.md](/docs/schemata.md).
|
10
|
+
|
11
|
+
To learn more about json-schema in general, start with [this excellent guide](http://spacetelescope.github.io/understanding-json-schema/) and supplement with the [specification](http://json-schema.org/documentation.html).
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'prmd'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install prmd
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Combine takes the path to a directory of schemas and combines them onto stdout. If -m or --meta is supplied, it will override defaults/metadata.
|
30
|
+
|
31
|
+
```
|
32
|
+
prmd combine <directory>
|
33
|
+
```
|
34
|
+
|
35
|
+
Doc takes the path to a directory of schemas and outputs their documentation onto stdout. If -m or --meta is supplied, it will override defaults/metadata.
|
36
|
+
|
37
|
+
```
|
38
|
+
prmd doc <directory_or_schema>
|
39
|
+
```
|
40
|
+
|
41
|
+
Prepend file to the documentation output.
|
42
|
+
|
43
|
+
```
|
44
|
+
prmd doc -p header.md,overview.md <directory or schema>
|
45
|
+
```
|
46
|
+
|
47
|
+
Init optionally takes a resource as it's first argument and generates a new schema file to stdout (generically or using the resource name provided). If -m or --meta is supplied, it will override defaults/metadata.
|
48
|
+
|
49
|
+
```
|
50
|
+
prmd init
|
51
|
+
prmd init <resource_name>
|
52
|
+
```
|
53
|
+
|
54
|
+
Verify takes a path to a directory of schemas or a particular schema file and checks to see if it matches expectations.
|
55
|
+
|
56
|
+
```
|
57
|
+
prmd verify <directory_or_schema>
|
58
|
+
```
|
59
|
+
|
60
|
+
Combining commands works too.
|
61
|
+
|
62
|
+
```
|
63
|
+
prmd combine <directory> | prmd verify | prmd doc > schema.md
|
64
|
+
```
|
65
|
+
|
66
|
+
## Contributing
|
67
|
+
|
68
|
+
1. Fork it
|
69
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
70
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
71
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
72
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/prmd
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'prmd')))
|
4
|
+
|
5
|
+
options = {}
|
6
|
+
|
7
|
+
commands = {
|
8
|
+
combine: OptionParser.new do |opts|
|
9
|
+
opts.banner = "prmd combine [options] <directory>"
|
10
|
+
opts.on("-m", "--meta meta.json", "Set defaults for schemata") do |m|
|
11
|
+
options[:meta] = m
|
12
|
+
end
|
13
|
+
end,
|
14
|
+
doc: OptionParser.new do |opts|
|
15
|
+
opts.banner = "prmd doc [options] <schema>"
|
16
|
+
opts.on("-m", "--meta meta.json", "Set defaults for schemata") do |m|
|
17
|
+
options[:meta] = m
|
18
|
+
end
|
19
|
+
opts.on("-p", "--prepend header,overview", Array, "Prepend files to output") do |p|
|
20
|
+
options[:prepend] = p
|
21
|
+
end
|
22
|
+
opts.banner = "prmd doc [options] <directory>"
|
23
|
+
end,
|
24
|
+
init: OptionParser.new do |opts|
|
25
|
+
opts.banner = "prmd init [options] <directory> <resource>"
|
26
|
+
opts.on("-m", "--meta meta.json", "Set defaults for schemata") do |m|
|
27
|
+
options[:meta] = m
|
28
|
+
end
|
29
|
+
end,
|
30
|
+
verify: OptionParser.new do |opts|
|
31
|
+
opts.banner = "prmd verify [options] <directory or schema>"
|
32
|
+
end
|
33
|
+
}
|
34
|
+
|
35
|
+
help_text = commands.values.map do |command|
|
36
|
+
" #{command.banner}"
|
37
|
+
end.join("\n")
|
38
|
+
|
39
|
+
global = OptionParser.new do |opts|
|
40
|
+
opts.banner = "Usage: prmd [options] [command [options]]"
|
41
|
+
opts.separator "\nAvailable options:"
|
42
|
+
opts.on("--version", "Return version") do |opts|
|
43
|
+
puts "prmd #{Prmd::VERSION}"
|
44
|
+
exit(0)
|
45
|
+
end
|
46
|
+
opts.separator "\nAvailable commands:"
|
47
|
+
opts.separator help_text
|
48
|
+
end
|
49
|
+
|
50
|
+
if ARGV.empty?
|
51
|
+
puts global
|
52
|
+
exit(1)
|
53
|
+
end
|
54
|
+
global.order!
|
55
|
+
|
56
|
+
command = ARGV.shift.to_sym
|
57
|
+
option = commands[command]
|
58
|
+
if option.nil?
|
59
|
+
puts global
|
60
|
+
exit(1)
|
61
|
+
end
|
62
|
+
|
63
|
+
if ARGV.empty? && $stdin.tty?
|
64
|
+
puts option
|
65
|
+
exit(1)
|
66
|
+
end
|
67
|
+
option.order!
|
68
|
+
|
69
|
+
case command
|
70
|
+
when :combine
|
71
|
+
puts Prmd.combine(ARGV[0], options)
|
72
|
+
when :doc
|
73
|
+
if ARGV.empty?
|
74
|
+
data = JSON.parse($stdin.read)
|
75
|
+
schema = Prmd::Schema.new(data, options)
|
76
|
+
puts Prmd.doc(schema, options)
|
77
|
+
else
|
78
|
+
schema = Prmd::Schema.load(ARGV[0])
|
79
|
+
puts Prmd.doc(schema, options)
|
80
|
+
end
|
81
|
+
when :init
|
82
|
+
puts Prmd.init(ARGV[0], options)
|
83
|
+
when :verify
|
84
|
+
errors = []
|
85
|
+
if ARGV.empty?
|
86
|
+
data = $stdin.read
|
87
|
+
errors = Prmd.verify(JSON.parse(data))
|
88
|
+
puts data
|
89
|
+
elsif File.directory?(ARGV[0])
|
90
|
+
Dir.glob(File.join(ARGV[0], '**/*.json')).each do |path|
|
91
|
+
Prmd.verify(JSON.parse(File.read(path))).each do |error|
|
92
|
+
errors << "#{path}: #{error}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
else
|
96
|
+
Prmd.verify(JSON.parse(File.read(ARGV[0]))).each do |error|
|
97
|
+
errors << "#{ARGV[0]}: #{error}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
errors.each do |error|
|
101
|
+
$stderr.puts error
|
102
|
+
end
|
103
|
+
exit(1) unless errors.empty?
|
104
|
+
end
|
data/docs/schemata.md
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# schemata
|
2
|
+
This document seeks to explain JSON schema in practice as well as our usage and associated implications. Everything described must be followed unless otherwise noted (or it is a bug). Unless otherwise noted (as in meta-data) keys should be alphabetized for ease of modification/review/updating. A great example in-the-wild is available for the Heroku Platform API, see [Heroku Devcenter](https://devcenter.heroku.com/articles/platform-api-reference#schema) for details.
|
3
|
+
|
4
|
+
## json-schema
|
5
|
+
|
6
|
+
JSON Schema provides a way to describe the resources, attributes and links of an API using JSON. This document will contain many examples and explanation, but going to the source can also be useful. There are three relevant specs, which are additive. You can read through them, ideally in this order:
|
7
|
+
|
8
|
+
1. [JSON Schema Core](http://tools.ietf.org/html/draft-zyp-json-schema-04) - defines the basic foundation of JSON Schema - you probably will not need this often
|
9
|
+
2. [JSON Schema Validation](http://tools.ietf.org/html/draft-fge-json-schema-validation-00) - defines the validation keywords of JSON Schema - covers most attributes
|
10
|
+
3. [JSON Hyper-Schema](http://tools.ietf.org/html/draft-luff-json-hyper-schema-00) - defines the hyper-media keywords of JSON Schema - covers remaining links-specific attributes
|
11
|
+
|
12
|
+
## structure
|
13
|
+
|
14
|
+
We have opted to split apart the schema into individual resource schema and a root schema which references all of them. These individual schema are named based on the singular form of the resource in question.
|
15
|
+
|
16
|
+
### meta-data
|
17
|
+
|
18
|
+
Each schema MUST include some meta-data, which we cluster at the top of the file, including:
|
19
|
+
|
20
|
+
* `description` - a description of the resource described by the schema
|
21
|
+
* `id` - an id for this schema, it MUST be in the form `"schema/#{lower_case_singular_resource}"`
|
22
|
+
* `$schema` - defines what meta-schema is in use, it MUST be `http://json-schema.org/draft-04/hyper-schema`
|
23
|
+
* `title` - title for this resource, it MUST be in the form `"Heroku Platform API - #{title_case_plural_resource}"`
|
24
|
+
* `type` - the type(s) of this schema, it MUST be `["object"]`
|
25
|
+
|
26
|
+
### `definitions`
|
27
|
+
|
28
|
+
We make heavy usage of the `definitions` attribute in each resource to provide a centralized collection of attributes related to each resource. By doing so we are able to refer to the same attribute in links, properties and even as foreign keys.
|
29
|
+
|
30
|
+
The definitions object MUST include every attribute related directly to this resource, including:
|
31
|
+
|
32
|
+
* all properties that are present in the serialization of the object
|
33
|
+
* an `identity` property to provide an easy way to find what unique identifier(s) can be used with this object as well as what to use for foreign keys
|
34
|
+
* all transient properties which may be passed into links related to the object, even if they are not serialized
|
35
|
+
|
36
|
+
Each attribute MUST include the following properties:
|
37
|
+
|
38
|
+
* `description` - a description of the attribute and how it relates to the resource
|
39
|
+
* `example` - an example of the attributes value, useful for documentation and tests
|
40
|
+
* `type` - an array of type(s) for this attribute, values MUST be one of `["array", "boolean", "integer", "number", "null", "object", "string"]`
|
41
|
+
|
42
|
+
Each attribute MAY include the following properties:
|
43
|
+
|
44
|
+
* `pattern` - a regex encoded in a string that the valid values MUST match
|
45
|
+
* `readOnly` - boolean value defining if the attribute can be modified, assumes `false` if omitted
|
46
|
+
* `format` - format of the value. MUST be one of spec defined `["date-time", "email", "hostname", "ipv4", "ipv6", "uri"]` or defined by us `["uuid"]`
|
47
|
+
|
48
|
+
Examples:
|
49
|
+
|
50
|
+
```javascript
|
51
|
+
{
|
52
|
+
"definitions": {
|
53
|
+
"id": {
|
54
|
+
"description": "unique identifier of resource",
|
55
|
+
"example": "01234567-89ab-cdef-0123-456789abcdef",
|
56
|
+
"format": "uuid",
|
57
|
+
"readOnly": true,
|
58
|
+
"type": ["string"]
|
59
|
+
},
|
60
|
+
"url": {
|
61
|
+
"description": "URL of resource",
|
62
|
+
"example": "http://example.com",
|
63
|
+
"format": "uri",
|
64
|
+
"pattern": "^http://[a-z][a-z0-9-]{3,30}\\.com$",
|
65
|
+
"type": ["null", "string"]
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
```
|
70
|
+
|
71
|
+
### `links`
|
72
|
+
|
73
|
+
Links define the actions available on a given resource. They are listed as an array, which should be alphabetized by title.
|
74
|
+
|
75
|
+
The links array MUST include an object defining each action available. Each action MUST include the following attributes:
|
76
|
+
|
77
|
+
* `description` - a description of the action to perform
|
78
|
+
* `href` - the path associated with this action, use [URI templates](http://tools.ietf.org/html/rfc6570) as needed, CGI escaping any JSON pointer values used for identity
|
79
|
+
* `method` - the http method to be used with this actio
|
80
|
+
* `rel` - describes relation of link to resource, SHOULD be one of `["create", "destroy", "self", "instances", "update"]`
|
81
|
+
* `title` - title for the link
|
82
|
+
|
83
|
+
Links that expect a json-encoded body as input MUST also include the following attributes:
|
84
|
+
* `schema` - an object with a `properties` object that MUST include JSON pointers to the definitions for each associated attribute
|
85
|
+
|
86
|
+
Schema properties MAY also include a `required` boolean to indicate if an attribute must be present, which is assumed to be false when omitted.
|
87
|
+
|
88
|
+
```javascript
|
89
|
+
{
|
90
|
+
"links": [
|
91
|
+
{
|
92
|
+
"description": "Create a new resource.",
|
93
|
+
"href": "/resources",
|
94
|
+
"method": "POST",
|
95
|
+
"rel": "create",
|
96
|
+
"schema": {
|
97
|
+
"properties": {
|
98
|
+
"owner": { "$ref": "/schema/user#/definitions/identity" },
|
99
|
+
"url": { "$ref": "/schema/resource/definitions/url" }
|
100
|
+
}
|
101
|
+
},
|
102
|
+
"title": "Create"
|
103
|
+
},
|
104
|
+
{
|
105
|
+
"description": "Delete an existing resource.",
|
106
|
+
"href": "/resources/{(%2Fschema%2Fresources%23%2Fdefinitions%2Fidentity)}",
|
107
|
+
"method": "DELETE",
|
108
|
+
"rel": "destroy",
|
109
|
+
"title": "Delete"
|
110
|
+
},
|
111
|
+
{
|
112
|
+
"description": "Info for existing resource.",
|
113
|
+
"href": "/resources/{(%2Fschema%2Fresources%23%2Fdefinitions%2Fidentity)}",
|
114
|
+
"method": "GET",
|
115
|
+
"rel": "self",
|
116
|
+
"title": "Info"
|
117
|
+
},
|
118
|
+
{
|
119
|
+
"description": "List existing resources.",
|
120
|
+
"href": "/resources",
|
121
|
+
"method": "GET",
|
122
|
+
"rel": "instances",
|
123
|
+
"title": "List"
|
124
|
+
},
|
125
|
+
{
|
126
|
+
"description": "Update an existing resource.",
|
127
|
+
"href": "/resources/{(%2Fschema%2Fresource%23%2Fdefinitions%2Fidentity)}",
|
128
|
+
"method": "PATCH",
|
129
|
+
"rel": "update",
|
130
|
+
"schema": {
|
131
|
+
"properties": {
|
132
|
+
"url": { "$ref": "/schema/resource/definitions/url" }
|
133
|
+
}
|
134
|
+
},
|
135
|
+
"title": "Update"
|
136
|
+
}
|
137
|
+
}
|
138
|
+
```
|
139
|
+
|
140
|
+
### `properties`
|
141
|
+
|
142
|
+
Properties defines the attributes that exist in the serialization of the object.
|
143
|
+
|
144
|
+
The properties object MUST contain all the serialized attributes for the object. Each attribute MUST provide a [JSON pointer](http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07) to the attribute in appropriate `definitions`, we have opted to always use absolute pointers for consistency. Properties MUST also add any data which overrides the values in definitions and SHOULD add any additional, relevant data.
|
145
|
+
|
146
|
+
```javascript
|
147
|
+
{
|
148
|
+
"properties": {
|
149
|
+
"id": { "$ref": "/schema/resource#/definitions/id" },
|
150
|
+
"owner": {
|
151
|
+
"description": "unique identifier of the user who owns this resource",
|
152
|
+
"properties": {
|
153
|
+
"id": { "$ref": "/schema/user#/definitions/id" }
|
154
|
+
},
|
155
|
+
"type": ["object"]
|
156
|
+
},
|
157
|
+
"url": { "$ref": "/schema/resource#/definitions/url" }
|
158
|
+
}
|
159
|
+
}
|
160
|
+
```
|
161
|
+
|
162
|
+
Note: this assumes that schema/user will also be available and will have id defined in the definitions. If/when you need to refer to a foreign key, you MUST add a new schema and/or add the appropriate attribute to the foreign resource definitions unless it already exists.
|