prmd 0.2.0 → 0.3.0
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.
- 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 -%>
|