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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZGFhZTI3Yjc0OTA3ZWI5Mzc4NzBlNWQzMWY4YmQ5ZmRmNjAzZGIwMg==
4
+ ZTIwZDk1MjQwNDczNDkxOTkyZWE4NjVhNjgyZGI5MDBhOWY4ODNlZA==
5
5
  data.tar.gz: !binary |-
6
- MTZlMzFjMDhjMWYyYmRlM2YxZjIyNjNkZjRjOWI0NmZkNDk4ZmU1MQ==
6
+ NGQxYTRlZjE5NmQ5YzcwZGM4OWZmMzFiYTU5OGU3N2FjYzkyYTMxOA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MDM1ZTE1MTI1YWNiMDMzZmQ4YmRhYzQ0MDI4Nzc4ZmI1ODM3NTUyODEzYjZm
10
- YTY5NGU3Mjk1ZmVhZDQ3Mjg2OTMwZjA2NmRlYzc3OTNkNWEyOWZjYTM4NzI2
11
- OWU5OTZjMTU4ZmNiYzUyMTg5NmE4NGM0MTgyN2QxMzFiNzEyMDY=
9
+ MmUwNWIxYjEwMDExY2FkYTM5MDliNDM0MGNlYjY2NzFhYzhhNzhlNWNhY2M2
10
+ YmFiOTFiOGFkMzdhZGFlMWU5ODk2OTY4ZWVkMDE3MjA5YzAyOTIyZDFlZjdi
11
+ MjEwNzVlZjUzMWUyNzI5NDc0NDk2OTgzMmZiZTc2NjNkYmI0N2E=
12
12
  data.tar.gz: !binary |-
13
- NjI0ZWJjOTRjNTA5YTM4MzI4NzI0ZDc2ZTYyZDQyNzRlYmM2MzIwOWQ2MDMy
14
- NWVlYmExZDNhNTJiMDkxYmU5ZmRiNzRiZGNhYjA1NDUwZjcwOGQ0NDhhZmY0
15
- Yjg4MDMwYmFlOWJkMDY0ZDljOTdiMWRhY2Q0NzA4ZmQ3YWFkMDU=
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
- data = JSON.parse($stdin.read)
71
- schema = Prmd::Schema.new(data)
72
- puts Prmd.doc(schema, options)
78
+ data = unless $stdin.tty?
79
+ $stdin.read
73
80
  else
74
- data = JSON.parse(File.read(ARGV[0]))
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?
@@ -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 `"Heroku Platform API - #{title_case_plural_resource}"`
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:
@@ -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
- schemas = files.sort.map { |file| [file, YAML.load(File.read(file))] }
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
- definitions_map = {}
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
- schemas.each do |schema_file, schema_data|
28
- id = if schema_data['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 = definitions_map[id]
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
- definitions_map[id] = schema_file
33
+ schemata_map[schema_data['id']] = schema_file
37
34
 
38
- data['definitions'][id] = schema_data
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
- if datum['$ref'].include?('/schema/')
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
- if datum['href'].include?('%2Fschema%2F')
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
@@ -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
@@ -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 -%>
@@ -1,3 +1,3 @@
1
1
  module Prmd
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,34 +1,34 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), 'helpers'))
2
2
 
3
- class SchemaTest < Minitest::Unit::TestCase
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.2.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-04-28 00:00:00.000000000 Z
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
@@ -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
@@ -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 -%>