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 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 -%>