prmd 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +4 -0
- data/bin/prmd +25 -7
- data/docs/schemata.md +2 -2
- data/lib/prmd.rb +1 -1
- data/lib/prmd/commands/combine.rb +13 -23
- data/lib/prmd/commands/render.rb +16 -0
- data/lib/prmd/schema.rb +34 -0
- data/lib/prmd/{views/parameters.erb → templates/link_schema_properties.erb} +0 -0
- data/lib/prmd/templates/schema.erb +12 -0
- data/lib/prmd/templates/schemata.erb +171 -0
- data/lib/prmd/version.rb +1 -1
- data/test/schema_test.rb +13 -13
- metadata +6 -5
- data/lib/prmd/commands/doc.rb +0 -114
- data/lib/prmd/views/endpoint.erb +0 -128
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZTIwZDk1MjQwNDczNDkxOTkyZWE4NjVhNjgyZGI5MDBhOWY4ODNlZA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NGQxYTRlZjE5NmQ5YzcwZGM4OWZmMzFiYTU5OGU3N2FjYzkyYTMxOA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MmUwNWIxYjEwMDExY2FkYTM5MDliNDM0MGNlYjY2NzFhYzhhNzhlNWNhY2M2
|
10
|
+
YmFiOTFiOGFkMzdhZGFlMWU5ODk2OTY4ZWVkMDE3MjA5YzAyOTIyZDFlZjdi
|
11
|
+
MjEwNzVlZjUzMWUyNzI5NDc0NDk2OTgzMmZiZTc2NjNkYmI0N2E=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZDAxNWM4ZWJmZDJhN2YwZjIyMDNkZmJlY2E2ZGMzYzllOTI2Y2UxYzI5M2Nm
|
14
|
+
ZDU0YzYzNTI1YTI5NGM0NjA3NmRhZjI3MWFjZGY0OGNmMzliNThkNzg0NjBj
|
15
|
+
MjEzYWUwZDhlMDc3ZWU3MWZhMmVjMWRmNTNmZDk1ODQwNDk5YzI=
|
data/README.md
CHANGED
@@ -44,6 +44,7 @@ Prmd provides four main commands:
|
|
44
44
|
* `combine`: Combine schemata and metadata into single schema
|
45
45
|
* `verify`: Verify a schema
|
46
46
|
* `doc`: Generate documentation from a schema
|
47
|
+
* `render`: Render views from schema
|
47
48
|
|
48
49
|
Here's an example of using these commands in a typical workflow:
|
49
50
|
|
@@ -77,6 +78,9 @@ $ prmd verify schema.json
|
|
77
78
|
$ prmd doc schema.json > schema.md
|
78
79
|
```
|
79
80
|
|
81
|
+
# Render from schema
|
82
|
+
$ prmd render --template schemata.erb schema.json > schema.md
|
83
|
+
|
80
84
|
Typically you'll want to prepend header and overview information to
|
81
85
|
your API documentation. You can do this with the `--prepend` flag:
|
82
86
|
|
data/bin/prmd
CHANGED
@@ -23,6 +23,15 @@ commands = {
|
|
23
23
|
options[:yaml] = y
|
24
24
|
end
|
25
25
|
end,
|
26
|
+
render: OptionParser.new do |opts|
|
27
|
+
opts.banner = "prmd doc [options] <combined schema>"
|
28
|
+
opts.on("-p", "--prepend header,overview", Array, "Prepend files to output") do |p|
|
29
|
+
options[:prepend] = p
|
30
|
+
end
|
31
|
+
opts.on("-t", "--template schemata.erb", String, "Use alternate template") do |t|
|
32
|
+
options[:template] = t
|
33
|
+
end
|
34
|
+
end,
|
26
35
|
verify: OptionParser.new do |opts|
|
27
36
|
opts.banner = "prmd verify [options] <combined schema>"
|
28
37
|
end
|
@@ -66,17 +75,26 @@ case command
|
|
66
75
|
when :combine
|
67
76
|
puts Prmd.combine(ARGV[0], options).to_s
|
68
77
|
when :doc
|
69
|
-
unless $stdin.tty?
|
70
|
-
|
71
|
-
schema = Prmd::Schema.new(data)
|
72
|
-
puts Prmd.doc(schema, options)
|
78
|
+
data = unless $stdin.tty?
|
79
|
+
$stdin.read
|
73
80
|
else
|
74
|
-
|
75
|
-
schema = Prmd::Schema.new(data)
|
76
|
-
puts Prmd.doc(schema, options)
|
81
|
+
File.read(ARGV[0])
|
77
82
|
end
|
83
|
+
schema = Prmd::Schema.new(JSON.parse(data))
|
84
|
+
|
85
|
+
options[:template] = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'prmd', 'templates', 'schema.erb'))
|
86
|
+
|
87
|
+
puts Prmd.render(schema, options)
|
78
88
|
when :init
|
79
89
|
puts Prmd.init(ARGV[0], options)
|
90
|
+
when :render
|
91
|
+
data = unless $stdin.tty?
|
92
|
+
$stdin.read
|
93
|
+
else
|
94
|
+
File.read(ARGV[0])
|
95
|
+
end
|
96
|
+
schema = Prmd::Schema.new(JSON.parse(data))
|
97
|
+
puts Prmd.render(schema, options)
|
80
98
|
when :verify
|
81
99
|
data, errors = '', []
|
82
100
|
unless $stdin.tty?
|
data/docs/schemata.md
CHANGED
@@ -20,7 +20,7 @@ Each schema MUST include some meta-data, which we cluster at the top of the file
|
|
20
20
|
* `description` - a description of the resource described by the schema
|
21
21
|
* `id` - an id for this schema, it MUST be in the form `"schema/#{lower_case_singular_resource}"`
|
22
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 `"
|
23
|
+
* `title` - title for this resource, it MUST be in the form `"#{title_case_API_name} - #{title_case_plural_resource}"`
|
24
24
|
* `type` - the type(s) of this schema, it MUST be `["object"]`
|
25
25
|
|
26
26
|
### `definitions`
|
@@ -41,7 +41,7 @@ Each attribute MUST include the following properties:
|
|
41
41
|
|
42
42
|
Each attribute MAY include the following properties:
|
43
43
|
|
44
|
-
* `pattern` - a regex encoded in a string that the valid values MUST match
|
44
|
+
* `pattern` - a javascript regex encoded in a string that the valid values MUST match
|
45
45
|
* `format` - format of the value. MUST be one of spec defined `["date-time", "email", "hostname", "ipv4", "ipv6", "uri"]` or defined by us `["uuid"]`
|
46
46
|
|
47
47
|
Examples:
|
data/lib/prmd.rb
CHANGED
@@ -5,9 +5,9 @@ require "yaml"
|
|
5
5
|
|
6
6
|
dir = File.dirname(__FILE__)
|
7
7
|
require File.join(dir, 'prmd', 'commands', 'combine')
|
8
|
-
require File.join(dir, 'prmd', 'commands', 'doc')
|
9
8
|
require File.join(dir, 'prmd', 'commands', 'expand')
|
10
9
|
require File.join(dir, 'prmd', 'commands', 'init')
|
10
|
+
require File.join(dir, 'prmd', 'commands', 'render')
|
11
11
|
require File.join(dir, 'prmd', 'commands', 'verify')
|
12
12
|
require File.join(dir, 'prmd', 'schema')
|
13
13
|
require File.join(dir, 'prmd', 'version')
|
@@ -8,7 +8,7 @@ module Prmd
|
|
8
8
|
[path]
|
9
9
|
end
|
10
10
|
# sort for stable loading on any platform
|
11
|
-
|
11
|
+
schemata = files.sort.map { |file| [file, YAML.load(File.read(file))] }
|
12
12
|
|
13
13
|
data = {
|
14
14
|
'$schema' => 'http://json-schema.org/draft-04/hyper-schema',
|
@@ -18,51 +18,41 @@ module Prmd
|
|
18
18
|
}
|
19
19
|
|
20
20
|
# tracks which entities where defined in which file
|
21
|
-
|
21
|
+
schemata_map = {}
|
22
22
|
|
23
23
|
if options[:meta] && File.exists?(options[:meta])
|
24
24
|
data.merge!(YAML.load(File.read(options[:meta])))
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
id =
|
29
|
-
schema_data['id'].split('/').last
|
30
|
-
end
|
31
|
-
next if id.nil? || id[0..0] == '_' # FIXME: remove this exception?
|
27
|
+
schemata.each do |schema_file, schema_data|
|
28
|
+
id_prefix, id = schema_data['id'].split('/')
|
32
29
|
|
33
|
-
if file =
|
34
|
-
$stderr.puts "`#{id}` (from #{schema_file}) was already defined in `#{file}` and will overwrite the first definition"
|
30
|
+
if file = schemata_map[schema_data['id']]
|
31
|
+
$stderr.puts "`#{schema_data['id']}` (from #{schema_file}) was already defined in `#{file}` and will overwrite the first definition"
|
35
32
|
end
|
36
|
-
|
33
|
+
schemata_map[schema_data['id']] = schema_file
|
37
34
|
|
38
|
-
data['definitions'][
|
35
|
+
data['definitions'][id_prefix] ||= {}
|
36
|
+
data['definitions'][id_prefix][id] = schema_data
|
39
37
|
reference_localizer = lambda do |datum|
|
40
38
|
case datum
|
41
39
|
when Array
|
42
40
|
datum.map {|element| reference_localizer.call(element)}
|
43
41
|
when Hash
|
44
42
|
if datum.has_key?('$ref')
|
45
|
-
|
46
|
-
$stderr.puts("`#{schema_data['id']}` `/schema/` prefixed refs are deprecated, use `/schemata/` prefixes")
|
47
|
-
datum['$ref'] = datum['$ref'].gsub(%r{/schema/([^#]*)#}, '#/definitions/\1')
|
48
|
-
end
|
49
|
-
datum['$ref'] = datum['$ref'].gsub(%r{/schemata/([^#]*)#}, '#/definitions/\1')
|
43
|
+
datum['$ref'] = '#/definitions' + datum['$ref'].gsub('#', '')
|
50
44
|
end
|
51
45
|
if datum.has_key?('href')
|
52
|
-
|
53
|
-
$stderr.puts("`#{id}` `%2Fschema%2F` prefixed hrefs are deprecated, use `%2Fschemata%2F` prefixes")
|
54
|
-
datum['href'] = datum['href'].gsub(%r{%2Fschema%2F([^%]*)%23%2F}, '%23%2Fdefinitions%2F\1%2F')
|
55
|
-
end
|
56
|
-
datum['href'] = datum['href'].gsub(%r{%2Fschemata%2F([^%]*)%23%2F}, '%23%2Fdefinitions%2F\1%2F')
|
46
|
+
datum['href'] = datum['href'].gsub('%23', '').gsub(%r{(%2Fschemata%2F[^%]*%2F)}, '%23%2Fdefinitions\1')
|
57
47
|
end
|
58
48
|
datum.each { |k,v| datum[k] = reference_localizer.call(v) }
|
59
49
|
else
|
60
50
|
datum
|
61
51
|
end
|
62
52
|
end
|
63
|
-
reference_localizer.call(data['definitions'][id])
|
53
|
+
reference_localizer.call(data['definitions'][id_prefix][id])
|
64
54
|
|
65
|
-
data['properties'][id] = { '$ref' => "#/definitions/#{id}" }
|
55
|
+
data['properties'][id] = { '$ref' => "#/definitions/#{id_prefix}/#{id}" }
|
66
56
|
end
|
67
57
|
|
68
58
|
Prmd::Schema.new(data)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Prmd
|
2
|
+
def self.render(schema, options={})
|
3
|
+
doc = ''
|
4
|
+
|
5
|
+
if options[:prepend]
|
6
|
+
doc << options[:prepend].map {|path| File.read(path)}.join("\n") << "\n"
|
7
|
+
end
|
8
|
+
|
9
|
+
doc << Erubis::Eruby.new(File.read(options[:template])).result({
|
10
|
+
options: options,
|
11
|
+
schema: schema
|
12
|
+
})
|
13
|
+
|
14
|
+
doc
|
15
|
+
end
|
16
|
+
end
|
data/lib/prmd/schema.rb
CHANGED
@@ -24,6 +24,7 @@ module Prmd
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
@data = convert_type_to_array.call(new_data)
|
27
|
+
@schemata_examples = {}
|
27
28
|
end
|
28
29
|
|
29
30
|
def dereference(reference)
|
@@ -55,6 +56,39 @@ module Prmd
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
59
|
+
def schema_example(schema)
|
60
|
+
example = {}
|
61
|
+
|
62
|
+
if schema.has_key?('example')
|
63
|
+
example.merge!(schema['example'])
|
64
|
+
elsif schema.has_key?('properties')
|
65
|
+
schema['properties'].each do |key, value|
|
66
|
+
_, value = dereference(value)
|
67
|
+
if value.has_key?('properties') # nested properties
|
68
|
+
example[key] = {}
|
69
|
+
value['properties'].each do |k,v|
|
70
|
+
example[key][k] = dereference(v).last['example']
|
71
|
+
end
|
72
|
+
else
|
73
|
+
example[key] = value['example']
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
example
|
79
|
+
end
|
80
|
+
|
81
|
+
def schemata_example(schemata_id)
|
82
|
+
_, schema = dereference("#/definitions/#{schemata_id}")
|
83
|
+
@schemata_examples[schemata_id] ||= begin
|
84
|
+
schema_example(schema)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def href
|
89
|
+
(@data['links'].detect { |link| link['rel'] == 'self' } || {})['href']
|
90
|
+
end
|
91
|
+
|
58
92
|
def to_json
|
59
93
|
new_json = JSON.pretty_generate(@data)
|
60
94
|
# nuke empty lines
|
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%=
|
2
|
+
schemata_template = File.read(File.join(File.dirname(options[:template]), 'schemata.erb'))
|
3
|
+
|
4
|
+
schema['properties'].map do |_, property|
|
5
|
+
_, schemata = schema.dereference(property)
|
6
|
+
Erubis::Eruby.new(schemata_template).result({
|
7
|
+
options: options,
|
8
|
+
schema: schema,
|
9
|
+
schemata: schemata
|
10
|
+
})
|
11
|
+
end.join("\n") << "\n"
|
12
|
+
%>
|
@@ -0,0 +1,171 @@
|
|
1
|
+
<%-
|
2
|
+
return unless schemata.has_key?('links') && !schemata['links'].empty?
|
3
|
+
|
4
|
+
link_schema_properties_template = File.read(File.join(File.dirname(options[:template]), 'link_schema_properties.erb'))
|
5
|
+
resource = schemata['id'].split('/').last
|
6
|
+
title = schemata['title'].split(' - ', 2).last
|
7
|
+
|
8
|
+
def extract_attributes(schema, properties)
|
9
|
+
attributes = []
|
10
|
+
properties = properties.sort_by {|k,v| k} # ensure consistent ordering
|
11
|
+
|
12
|
+
properties.each do |key, value|
|
13
|
+
# found a reference to another element:
|
14
|
+
_, value = schema.dereference(value)
|
15
|
+
if value.has_key?('anyOf')
|
16
|
+
descriptions = []
|
17
|
+
examples = []
|
18
|
+
|
19
|
+
# sort anyOf! always show unique identifier first
|
20
|
+
anyof = value['anyOf'].sort_by do |property|
|
21
|
+
property['$ref'].split('/').last.gsub('id', 'a')
|
22
|
+
end
|
23
|
+
|
24
|
+
anyof.each do |ref|
|
25
|
+
_, nested_field = schema.dereference(ref)
|
26
|
+
descriptions << nested_field['description']
|
27
|
+
examples << nested_field['example']
|
28
|
+
end
|
29
|
+
|
30
|
+
# avoid repetition :}
|
31
|
+
if descriptions.size > 1
|
32
|
+
descriptions.first.gsub!(/ of (this )?.*/, "")
|
33
|
+
descriptions[1..-1].map { |d| d.gsub!(/unique /, "") }
|
34
|
+
end
|
35
|
+
|
36
|
+
last = descriptions.pop
|
37
|
+
description = [descriptions.join(", "), last].join(" or ")
|
38
|
+
|
39
|
+
example = [*examples].map { |e| "<code>#{e.to_json}</code>" }.join(" or ")
|
40
|
+
attributes << [key, "string", description, example]
|
41
|
+
|
42
|
+
# found a nested object
|
43
|
+
elsif value['type'] == ['object'] && value['properties']
|
44
|
+
nested = extract_attributes(schema, value['properties'])
|
45
|
+
nested.each do |attribute|
|
46
|
+
attribute[0] = "#{key}:#{attribute[0]}"
|
47
|
+
end
|
48
|
+
attributes.concat(nested)
|
49
|
+
# just a regular attribute
|
50
|
+
else
|
51
|
+
description = value['description']
|
52
|
+
if value['enum']
|
53
|
+
description += '<br/><b>one of:</b>' + [*value['enum']].map { |e| "<code>#{e.to_json}</code>" }.join(" or ")
|
54
|
+
end
|
55
|
+
example = [*value['example']].map { |e| "<code>#{e.to_json}</code>" }.join(" or ")
|
56
|
+
type = if value['type'].include?('null')
|
57
|
+
'nullable '
|
58
|
+
else
|
59
|
+
''
|
60
|
+
end
|
61
|
+
type += (value['format'] || (value['type'] - ['null']).first)
|
62
|
+
attributes << [key, type, description, example]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
return attributes
|
66
|
+
end
|
67
|
+
%>
|
68
|
+
## <%= title %>
|
69
|
+
<%= schemata['description'] %>
|
70
|
+
|
71
|
+
<%- if schemata['properties'] %>
|
72
|
+
### Attributes
|
73
|
+
<table>
|
74
|
+
<tr>
|
75
|
+
<th>Name</th>
|
76
|
+
<th>Type</th>
|
77
|
+
<th>Description</th>
|
78
|
+
<th>Example</th>
|
79
|
+
</tr>
|
80
|
+
<%- extract_attributes(schema, schemata['properties']).each do |(key, type, description, example)| %>
|
81
|
+
<tr>
|
82
|
+
<td><strong><%= key %></strong></td>
|
83
|
+
<td><em><%= type %></em></td>
|
84
|
+
<td><%= description %></td>
|
85
|
+
<td><%= example %></td>
|
86
|
+
</tr>
|
87
|
+
<%- end %>
|
88
|
+
</table>
|
89
|
+
|
90
|
+
<%- end %>
|
91
|
+
<%- schemata['links'].each do |link, datum| %>
|
92
|
+
<%-
|
93
|
+
path = link['href'].gsub(%r|(\{\([^\)]+\)\})|) do |ref|
|
94
|
+
ref = ref.gsub('%2F', '/').gsub('%23', '#').gsub(%r|[\{\(\)\}]|, '')
|
95
|
+
ref_resource = ref.split('#/definitions/schemata/').last.split('/').first.gsub('-','_')
|
96
|
+
identity_key, identity_value = schema.dereference(ref)
|
97
|
+
if identity_value.has_key?('anyOf')
|
98
|
+
'{' + ref_resource + '_' + identity_value['anyOf'].map {|r| r['$ref'].split('/').last}.join('_or_') + '}'
|
99
|
+
else
|
100
|
+
'{' + ref_resource + '_' + identity_key.split('/').last + '}'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
-%>
|
104
|
+
### <%= title %> <%= link['title'] %>
|
105
|
+
<%= link['description'] %>
|
106
|
+
|
107
|
+
```
|
108
|
+
<%= link['method'] %> <%= path %>
|
109
|
+
```
|
110
|
+
|
111
|
+
<%- if link.has_key?('schema') && link['schema'].has_key?('properties') %>
|
112
|
+
<%-
|
113
|
+
required, optional = link['schema']['properties'].partition do |k, v|
|
114
|
+
(link['schema']['required'] || []).include?(k)
|
115
|
+
end.map { |partition| Hash[partition] }
|
116
|
+
%>
|
117
|
+
<%- unless required.empty? %>
|
118
|
+
#### Required Parameters
|
119
|
+
<%= Erubis::Eruby.new(link_schema_properties_template).result(params: required, schema: schema) %>
|
120
|
+
|
121
|
+
<%- end %>
|
122
|
+
<%- unless optional.empty? %>
|
123
|
+
#### Optional Parameters
|
124
|
+
<%= Erubis::Eruby.new(link_schema_properties_template).result(params: optional, schema: schema) %>
|
125
|
+
<%- end %>
|
126
|
+
<%- end %>
|
127
|
+
|
128
|
+
#### Curl Example
|
129
|
+
```term
|
130
|
+
<%-
|
131
|
+
data = {}
|
132
|
+
path = path.gsub(/{([^}]*)}/) {|match| '$' + match.gsub(/[{}]/, '').upcase}
|
133
|
+
|
134
|
+
if link.has_key?('schema')
|
135
|
+
data.merge!(schema.schema_example(link['schema']))
|
136
|
+
|
137
|
+
if link['method'].upcase == 'GET' && !data.empty?
|
138
|
+
path << '?'
|
139
|
+
data.sort_by {|k,_| k.to_s }.each do |key, values|
|
140
|
+
[values].flatten.each do |value|
|
141
|
+
path << [key.to_s, CGI.escape(value.to_s)].join('=') << '&'
|
142
|
+
end
|
143
|
+
end
|
144
|
+
path.chop! # remove trailing '&'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
%>
|
148
|
+
$ curl -n -X <%= link['method'] %> <%= schema.href %><%= path %>
|
149
|
+
<%- unless data.empty? || link['method'].upcase == 'GET' %>
|
150
|
+
-H "Content-Type: application/json" \
|
151
|
+
-d '<%= data.to_json %>'
|
152
|
+
<%- end %>
|
153
|
+
```
|
154
|
+
|
155
|
+
#### Response Example
|
156
|
+
```
|
157
|
+
HTTP/1.1 <%= case link['rel']
|
158
|
+
when 'create'
|
159
|
+
'201 Created'
|
160
|
+
else
|
161
|
+
'200 OK'
|
162
|
+
end %>
|
163
|
+
```
|
164
|
+
```javascript```
|
165
|
+
<%- if link['rel'] == 'instances' %>
|
166
|
+
<%= JSON.pretty_generate([schema.schemata_example(schemata['id'])]) %>
|
167
|
+
<%- else %>
|
168
|
+
<%= JSON.pretty_generate(schema.schemata_example(schemata['id'])) %>
|
169
|
+
<%- end %>
|
170
|
+
```
|
171
|
+
<%- end -%>
|
data/lib/prmd/version.rb
CHANGED
data/test/schema_test.rb
CHANGED
@@ -1,34 +1,34 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), 'helpers'))
|
2
2
|
|
3
|
-
class SchemaTest < Minitest::
|
3
|
+
class SchemaTest < Minitest::Test
|
4
4
|
def test_dereference_with_ref
|
5
5
|
key, value = user_input_schema.dereference({
|
6
|
-
'$ref' => '#/definitions/user/definitions/id'
|
6
|
+
'$ref' => '#/definitions/schemata/user/definitions/id'
|
7
7
|
})
|
8
|
-
assert_equal(key, '#/definitions/user/definitions/id')
|
9
|
-
assert_equal(value, user_input_schema['definitions']['user']['definitions']['id'])
|
8
|
+
assert_equal(key, '#/definitions/schemata/user/definitions/id')
|
9
|
+
assert_equal(value, user_input_schema['definitions']['schemata']['user']['definitions']['id'])
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_dereference_without_ref
|
13
|
-
key, value = user_input_schema.dereference('#/definitions/user/definitions/id')
|
14
|
-
assert_equal(key, '#/definitions/user/definitions/id')
|
15
|
-
assert_equal(value, user_input_schema['definitions']['user']['definitions']['id'])
|
13
|
+
key, value = user_input_schema.dereference('#/definitions/schemata/user/definitions/id')
|
14
|
+
assert_equal(key, '#/definitions/schemata/user/definitions/id')
|
15
|
+
assert_equal(value, user_input_schema['definitions']['schemata']['user']['definitions']['id'])
|
16
16
|
end
|
17
17
|
|
18
18
|
def test_dereference_with_nested_ref
|
19
19
|
key, value = user_input_schema.dereference({
|
20
|
-
'$ref' => '#/definitions/user/definitions/identity'
|
20
|
+
'$ref' => '#/definitions/schemata/user/definitions/identity'
|
21
21
|
})
|
22
|
-
assert_equal(key, '#/definitions/user/definitions/id')
|
23
|
-
assert_equal(value, user_input_schema['definitions']['user']['definitions']['id'])
|
22
|
+
assert_equal(key, '#/definitions/schemata/user/definitions/id')
|
23
|
+
assert_equal(value, user_input_schema['definitions']['schemata']['user']['definitions']['id'])
|
24
24
|
end
|
25
25
|
|
26
26
|
def test_dereference_with_local_context
|
27
27
|
key, value = user_input_schema.dereference({
|
28
|
-
'$ref' => '#/definitions/user/properties/id',
|
28
|
+
'$ref' => '#/definitions/schemata/user/properties/id',
|
29
29
|
'override' => true
|
30
30
|
})
|
31
|
-
assert_equal(key, '#/definitions/user/definitions/id')
|
32
|
-
assert_equal(value, {'override' => true}.merge(user_input_schema['definitions']['user']['definitions']['id']))
|
31
|
+
assert_equal(key, '#/definitions/schemata/user/definitions/id')
|
32
|
+
assert_equal(value, {'override' => true}.merge(user_input_schema['definitions']['schemata']['user']['definitions']['id']))
|
33
33
|
end
|
34
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prmd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- geemus
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-05-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: erubis
|
@@ -87,14 +87,15 @@ files:
|
|
87
87
|
- docs/schemata.md
|
88
88
|
- lib/prmd.rb
|
89
89
|
- lib/prmd/commands/combine.rb
|
90
|
-
- lib/prmd/commands/doc.rb
|
91
90
|
- lib/prmd/commands/expand.rb
|
92
91
|
- lib/prmd/commands/init.rb
|
92
|
+
- lib/prmd/commands/render.rb
|
93
93
|
- lib/prmd/commands/verify.rb
|
94
94
|
- lib/prmd/schema.rb
|
95
|
+
- lib/prmd/templates/link_schema_properties.erb
|
96
|
+
- lib/prmd/templates/schema.erb
|
97
|
+
- lib/prmd/templates/schemata.erb
|
95
98
|
- lib/prmd/version.rb
|
96
|
-
- lib/prmd/views/endpoint.erb
|
97
|
-
- lib/prmd/views/parameters.erb
|
98
99
|
- prmd.gemspec
|
99
100
|
- test/helpers.rb
|
100
101
|
- test/schema_test.rb
|
data/lib/prmd/commands/doc.rb
DELETED
@@ -1,114 +0,0 @@
|
|
1
|
-
def extract_attributes(schema, properties)
|
2
|
-
attributes = []
|
3
|
-
properties.each do |key, value|
|
4
|
-
# found a reference to another element:
|
5
|
-
_, value = schema.dereference(value)
|
6
|
-
if value.has_key?('anyOf')
|
7
|
-
descriptions = []
|
8
|
-
examples = []
|
9
|
-
|
10
|
-
# sort anyOf! always show unique identifier first
|
11
|
-
anyof = value['anyOf'].sort_by do |property|
|
12
|
-
property['$ref'].split('/').last.gsub('id', 'a')
|
13
|
-
end
|
14
|
-
|
15
|
-
anyof.each do |ref|
|
16
|
-
_, nested_field = schema.dereference(ref)
|
17
|
-
descriptions << nested_field['description']
|
18
|
-
examples << nested_field['example']
|
19
|
-
end
|
20
|
-
|
21
|
-
# avoid repetition :}
|
22
|
-
if descriptions.size > 1
|
23
|
-
descriptions.first.gsub!(/ of (this )?.*/, "")
|
24
|
-
descriptions[1..-1].map { |d| d.gsub!(/unique /, "") }
|
25
|
-
end
|
26
|
-
|
27
|
-
last = descriptions.pop
|
28
|
-
description = [descriptions.join(", "), last].join(" or ")
|
29
|
-
|
30
|
-
example = doc_example(*examples)
|
31
|
-
attributes << [key, "string", description, example]
|
32
|
-
|
33
|
-
# found a nested object
|
34
|
-
elsif value['type'] == ['object'] && value['properties']
|
35
|
-
nested = extract_attributes(schema, value['properties'])
|
36
|
-
nested.each do |attribute|
|
37
|
-
attribute[0] = "#{key}:#{attribute[0]}"
|
38
|
-
end
|
39
|
-
attributes.concat(nested)
|
40
|
-
# just a regular attribute
|
41
|
-
else
|
42
|
-
description = value['description']
|
43
|
-
if value['enum']
|
44
|
-
description += '<br/><b>one of:</b>' + doc_example(*value['enum'])
|
45
|
-
end
|
46
|
-
example = doc_example(value['example'])
|
47
|
-
attributes << [key, doc_type(value), description, example]
|
48
|
-
end
|
49
|
-
end
|
50
|
-
return attributes
|
51
|
-
end
|
52
|
-
|
53
|
-
def doc_type(property)
|
54
|
-
schema_type = property["type"].dup
|
55
|
-
type = "nullable " if schema_type.delete("null")
|
56
|
-
type.to_s + (property["format"] || schema_type.first)
|
57
|
-
end
|
58
|
-
|
59
|
-
def doc_example(*examples)
|
60
|
-
examples.map { |e| "<code>#{e.to_json}</code>" }.join(" or ")
|
61
|
-
end
|
62
|
-
|
63
|
-
module Prmd
|
64
|
-
def self.doc(schema, options={})
|
65
|
-
root_url = schema['links'].find{|l| l['rel'] == 'self'}['href']
|
66
|
-
|
67
|
-
doc = (options[:prepend] || []).map do |path|
|
68
|
-
File.open(path, 'r').read + "\n"
|
69
|
-
end
|
70
|
-
|
71
|
-
doc << schema['definitions'].map do |_, definition|
|
72
|
-
next if (definition['links'] || []).empty?
|
73
|
-
resource = definition['id'].split('/').last
|
74
|
-
serialization = {}
|
75
|
-
if definition['definitions'].has_key?('identity')
|
76
|
-
identifiers = if definition['definitions']['identity'].has_key?('anyOf')
|
77
|
-
definition['definitions']['identity']['anyOf']
|
78
|
-
else
|
79
|
-
[definition['definitions']['identity']]
|
80
|
-
end
|
81
|
-
|
82
|
-
identifiers = identifiers.map {|ref| ref['$ref'].split('/').last }
|
83
|
-
end
|
84
|
-
if definition['properties']
|
85
|
-
definition['properties'].each do |key, value|
|
86
|
-
_, value = schema.dereference(value)
|
87
|
-
if value.has_key?('properties')
|
88
|
-
serialization[key] = {}
|
89
|
-
value['properties'].each do |k,v|
|
90
|
-
serialization[key][k] = schema.dereference(v).last['example']
|
91
|
-
end
|
92
|
-
else
|
93
|
-
serialization[key] = value['example']
|
94
|
-
end
|
95
|
-
end
|
96
|
-
else
|
97
|
-
serialization.merge!(definition['example'])
|
98
|
-
end
|
99
|
-
|
100
|
-
title = definition['title'].split(' - ', 2).last
|
101
|
-
|
102
|
-
Erubis::Eruby.new(File.read(File.dirname(__FILE__) + "/../views/endpoint.erb")).result({
|
103
|
-
definition: definition,
|
104
|
-
identifiers: identifiers,
|
105
|
-
resource: resource,
|
106
|
-
root_url: root_url,
|
107
|
-
schema: schema,
|
108
|
-
serialization: serialization,
|
109
|
-
title: title,
|
110
|
-
params_template: File.read(File.dirname(__FILE__) + "/../views/parameters.erb"),
|
111
|
-
}) + "\n"
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
data/lib/prmd/views/endpoint.erb
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
## <%= title %>
|
2
|
-
<%= definition['description'] %>
|
3
|
-
|
4
|
-
<%- if definition['properties'] %>
|
5
|
-
### Attributes
|
6
|
-
<table>
|
7
|
-
<tr>
|
8
|
-
<th>Name</th>
|
9
|
-
<th>Type</th>
|
10
|
-
<th>Description</th>
|
11
|
-
<th>Example</th>
|
12
|
-
</tr>
|
13
|
-
<%- extract_attributes(schema, definition['properties']).each do |(key, type, description, example)| %>
|
14
|
-
<tr>
|
15
|
-
<td><strong><%= key %></strong></td>
|
16
|
-
<td><em><%= type %></em></td>
|
17
|
-
<td><%= description %></td>
|
18
|
-
<td><%= example %></td>
|
19
|
-
</tr>
|
20
|
-
<%- end %>
|
21
|
-
</table>
|
22
|
-
|
23
|
-
<%- end %>
|
24
|
-
<%- definition['links'].each do |link, datum| %>
|
25
|
-
<%- path = link['href'].gsub(%r|(\{\([^\)]+\)\})|) do |ref|
|
26
|
-
ref = ref.gsub('%2F', '/').gsub('%23', '#').gsub(%r|[\{\(\)\}]|, '')
|
27
|
-
resource = ref.match(%r{^#/definitions/([^/]*)}).captures.first
|
28
|
-
identity_key, identity_value = schema.dereference(ref)
|
29
|
-
if identity_value.has_key?('anyOf')
|
30
|
-
'{' + resource + '_' + identity_value['anyOf'].map {|r| r['$ref'].split('/').last}.join('_or_') + '}'
|
31
|
-
else
|
32
|
-
'{' + resource + '_' + identity_key.split('/').last + '}'
|
33
|
-
end
|
34
|
-
end -%>
|
35
|
-
### <%= title %> <%= link['title'] %>
|
36
|
-
<%= link['description'] %>
|
37
|
-
|
38
|
-
```
|
39
|
-
<%= link['method'] %> <%= path %>
|
40
|
-
```
|
41
|
-
|
42
|
-
<%- if link.has_key?('schema') && link['schema'].has_key?('properties') %>
|
43
|
-
<%-
|
44
|
-
required, optional = link['schema']['properties'].partition do |k, v|
|
45
|
-
(link['schema']['required'] || []).include?(k)
|
46
|
-
end.map { |partition| Hash[partition] }
|
47
|
-
%>
|
48
|
-
<%- unless required.empty? %>
|
49
|
-
#### Required Parameters
|
50
|
-
<%= Erubis::Eruby.new(params_template).result(params: required, schema: schema) %>
|
51
|
-
|
52
|
-
<%- end %>
|
53
|
-
<%- unless optional.empty? %>
|
54
|
-
#### Optional Parameters
|
55
|
-
<%= Erubis::Eruby.new(params_template).result(params: optional, schema: schema) %>
|
56
|
-
<%- end %>
|
57
|
-
<%- end %>
|
58
|
-
|
59
|
-
#### Curl Example
|
60
|
-
```term
|
61
|
-
<%- path = path.gsub(/{([^}]*)}/) {|match| '$' + match.gsub(/[{}]/, '')} %>
|
62
|
-
<%- if link.has_key?('schema') && (link['schema'].has_key?('properties') || link['schema'].has_key?('example')) %>
|
63
|
-
<%-
|
64
|
-
data = {}
|
65
|
-
if link['schema']['properties']
|
66
|
-
link['schema']['properties'].each do |key, value|
|
67
|
-
_, value = schema.dereference(value)
|
68
|
-
if value.has_key?('anyOf')
|
69
|
-
id_ref = value['anyOf'].detect {|ref| ref['$ref'].split('/').last == 'id'}
|
70
|
-
data[key] = schema.dereference(id_ref).last['example']
|
71
|
-
elsif value.has_key?('properties')
|
72
|
-
data[key] = {}
|
73
|
-
value['properties'].each do |k,v|
|
74
|
-
data[key][k] = schema.dereference(v).last['example']
|
75
|
-
end
|
76
|
-
else
|
77
|
-
data[key] = schema.dereference(value).last['example']
|
78
|
-
end
|
79
|
-
end
|
80
|
-
else
|
81
|
-
data.merge!(link['schema']['example'])
|
82
|
-
end
|
83
|
-
%>
|
84
|
-
<%- if link['method'].upcase == 'GET' %>
|
85
|
-
<%-
|
86
|
-
unless data.empty?
|
87
|
-
path << '?'
|
88
|
-
data.sort_by {|k,_| k.to_s }.each do |key, values|
|
89
|
-
if values.nil?
|
90
|
-
path << key.to_s << '&'
|
91
|
-
else
|
92
|
-
[values].flatten.each do |value|
|
93
|
-
path << key.to_s << '=' << CGI.escape(value.to_s) << '&'
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
path.chop! # remove trailing '&'
|
98
|
-
end
|
99
|
-
%>
|
100
|
-
$ curl -n -X <%= link['method'] %> <%= root_url %><%= path %>
|
101
|
-
<%- else %>
|
102
|
-
$ curl -n -X <%= link['method'] %> <%= root_url %><%= path %> \
|
103
|
-
-H "Content-Type: application/json" \
|
104
|
-
-d '<%= data.to_json %>'
|
105
|
-
<%- end %>
|
106
|
-
<%- else %>
|
107
|
-
$ curl -n -X <%= link['method'] %> <%= root_url %><%= path %>
|
108
|
-
<%- end %>
|
109
|
-
```
|
110
|
-
|
111
|
-
#### Response Example
|
112
|
-
```
|
113
|
-
HTTP/1.1 <%= case link['rel']
|
114
|
-
when 'create'
|
115
|
-
'201 Created'
|
116
|
-
else
|
117
|
-
'200 OK'
|
118
|
-
end %>
|
119
|
-
```
|
120
|
-
```javascript```
|
121
|
-
<%- if link['rel'] == 'instances' %>
|
122
|
-
<%= JSON.pretty_generate([serialization]) %>
|
123
|
-
<%- else %>
|
124
|
-
<%= JSON.pretty_generate(serialization) %>
|
125
|
-
<%- end %>
|
126
|
-
```
|
127
|
-
|
128
|
-
<%- end -%>
|