prmd 0.6.2 → 0.7.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 +5 -13
- data/CONTRIBUTORS.md +8 -0
- data/Gemfile.lock +6 -6
- data/README.md +57 -8
- data/Rakefile +4 -4
- data/bin/prmd +3 -117
- data/docs/schemata.md +11 -11
- data/lib/prmd.rb +7 -18
- data/lib/prmd/cli.rb +108 -0
- data/lib/prmd/cli/base.rb +151 -0
- data/lib/prmd/cli/combine.rb +42 -0
- data/lib/prmd/cli/doc.rb +69 -0
- data/lib/prmd/cli/generate.rb +44 -0
- data/lib/prmd/cli/render.rb +48 -0
- data/lib/prmd/cli/verify.rb +49 -0
- data/lib/prmd/commands.rb +4 -0
- data/lib/prmd/commands/combine.rb +85 -58
- data/lib/prmd/commands/init.rb +30 -98
- data/lib/prmd/commands/render.rb +30 -17
- data/lib/prmd/commands/verify.rb +78 -35
- data/lib/prmd/core/combiner.rb +91 -0
- data/lib/prmd/core/generator.rb +27 -0
- data/lib/prmd/core/renderer.rb +56 -0
- data/lib/prmd/core/schema_hash.rb +47 -0
- data/lib/prmd/core_ext/optparse.rb +6 -0
- data/lib/prmd/hash_helpers.rb +38 -0
- data/lib/prmd/load_schema_file.rb +25 -0
- data/lib/prmd/rake_tasks/base.rb +33 -0
- data/lib/prmd/rake_tasks/combine.rb +50 -0
- data/lib/prmd/rake_tasks/doc.rb +73 -0
- data/lib/prmd/rake_tasks/verify.rb +60 -0
- data/lib/prmd/schema.rb +86 -34
- data/lib/prmd/template.rb +65 -8
- data/lib/prmd/templates/combine_head.json +6 -0
- data/lib/prmd/templates/init_default.json +9 -0
- data/lib/prmd/templates/init_resource.json.erb +90 -0
- data/lib/prmd/templates/link_schema_properties.md.erb +5 -0
- data/lib/prmd/templates/schema.erb +2 -2
- data/lib/prmd/templates/schemata.md.erb +37 -0
- data/lib/prmd/templates/schemata/helper.erb +29 -15
- data/lib/prmd/templates/schemata/link.md.erb +74 -0
- data/lib/prmd/templates/schemata/{link_curl_example.erb → link_curl_example.md.erb} +8 -2
- data/lib/prmd/url_generator.rb +11 -69
- data/lib/prmd/url_generators/generators/default.rb +66 -0
- data/lib/prmd/url_generators/generators/json.rb +30 -0
- data/lib/prmd/version.rb +10 -1
- data/prmd.gemspec +15 -15
- data/test/cli/combine_test.rb +23 -0
- data/test/cli/doc_test.rb +25 -0
- data/test/cli/generate_test.rb +23 -0
- data/test/cli/render_test.rb +25 -0
- data/test/cli/verify_test.rb +21 -0
- data/test/commands/init_test.rb +7 -0
- data/test/commands/render_test.rb +93 -0
- data/test/commands/verify_test.rb +60 -60
- data/test/helpers.rb +61 -6
- data/test/schema_test.rb +17 -11
- data/test/schemata/input/doc-settings.json +4 -0
- metadata +73 -28
- data/lib/prmd/commands/expand.rb +0 -108
- data/lib/prmd/templates/link_schema_properties.erb +0 -16
- data/lib/prmd/templates/schemata.erb +0 -47
- data/lib/prmd/templates/schemata/link.erb +0 -61
data/lib/prmd/commands/init.rb
CHANGED
@@ -1,107 +1,39 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'prmd/template'
|
3
|
+
require 'prmd/core/generator'
|
4
|
+
|
5
|
+
# :nodoc:
|
1
6
|
module Prmd
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
'
|
9
|
-
'
|
10
|
-
|
11
|
-
|
7
|
+
# Schema generation
|
8
|
+
module Generate
|
9
|
+
# Creates a default Prmd::Generator using default templates
|
10
|
+
#
|
11
|
+
# @return [Prmd::Generator]
|
12
|
+
def self.make_generator
|
13
|
+
base = Prmd::Template.load_json('init_default.json')
|
14
|
+
template = Prmd::Template.load_template('init_resource.json.erb', '')
|
15
|
+
Prmd::Generator.new(base: base, template: template)
|
16
|
+
end
|
17
|
+
end
|
12
18
|
|
13
|
-
|
19
|
+
# Generate a schema template
|
20
|
+
#
|
21
|
+
# @param [String] resource
|
22
|
+
# @param [Hash<Symbol, Object>] options
|
23
|
+
# @return [String] schema template in YAML (yaml option was enabled) else JSON
|
24
|
+
def self.init(resource, options = {})
|
25
|
+
gen = Generate.make_generator
|
14
26
|
|
27
|
+
generator_options = { resource: nil, parent: nil }
|
15
28
|
if resource
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
schema['title'] = "FIXME - #{resource[0...1].upcase}#{resource[1..-1]}"
|
21
|
-
schema['definitions'] = {
|
22
|
-
"created_at" => {
|
23
|
-
"description" => "when #{resource} was created",
|
24
|
-
"example" => "2012-01-01T12:00:00Z",
|
25
|
-
"format" => "date-time",
|
26
|
-
"type" => ["string"]
|
27
|
-
},
|
28
|
-
"id" => {
|
29
|
-
"description" => "unique identifier of #{resource}",
|
30
|
-
"example" => "01234567-89ab-cdef-0123-456789abcdef",
|
31
|
-
"format" => "uuid",
|
32
|
-
"type" => ["string"]
|
33
|
-
},
|
34
|
-
"identity" => {
|
35
|
-
"$ref" => "/schemata/#{resource}#/definitions/id"
|
36
|
-
},
|
37
|
-
"updated_at" => {
|
38
|
-
"description" => "when #{resource} was updated",
|
39
|
-
"example" => "2012-01-01T12:00:00Z",
|
40
|
-
"format" => "date-time",
|
41
|
-
"type" => ["string"]
|
42
|
-
}
|
43
|
-
}
|
44
|
-
schema['links'] = [
|
45
|
-
{
|
46
|
-
"description" => "Create a new #{resource}.",
|
47
|
-
"href" => "/#{resource}s",
|
48
|
-
"method" => "POST",
|
49
|
-
"rel" => "create",
|
50
|
-
"schema" => {
|
51
|
-
"properties" => {},
|
52
|
-
"type" => ["object"]
|
53
|
-
},
|
54
|
-
"title" => "Create"
|
55
|
-
},
|
56
|
-
{
|
57
|
-
"description" => "Delete an existing #{resource}.",
|
58
|
-
"href" => "/#{resource}s/{(%2Fschemata%2F#{resource}%23%2Fdefinitions%2Fidentity)}",
|
59
|
-
"method" => "DELETE",
|
60
|
-
"rel" => "destroy",
|
61
|
-
"title" => "Delete"
|
62
|
-
},
|
63
|
-
{
|
64
|
-
"description" => "Info for existing #{resource}.",
|
65
|
-
"href" => "/#{resource}s/{(%2Fschemata%2F#{resource}%23%2Fdefinitions%2Fidentity)}",
|
66
|
-
"method" => "GET",
|
67
|
-
"rel" => "self",
|
68
|
-
"title" => "Info"
|
69
|
-
},
|
70
|
-
{
|
71
|
-
"description" => "List existing #{resource}s.",
|
72
|
-
"href" => "/#{resource}s",
|
73
|
-
"method" => "GET",
|
74
|
-
"rel" => "instances",
|
75
|
-
"title" => "List"
|
76
|
-
},
|
77
|
-
{
|
78
|
-
"description" => "Update an existing #{resource}.",
|
79
|
-
"href" => "/#{resource}s/{(%2Fschemata%2F#{resource}%23%2Fdefinitions%2Fidentity)}",
|
80
|
-
"method" => "PATCH",
|
81
|
-
"rel" => "update",
|
82
|
-
"schema" => {
|
83
|
-
"properties" => {},
|
84
|
-
"type" => ["object"]
|
85
|
-
},
|
86
|
-
"title" => "Update"
|
87
|
-
}
|
88
|
-
]
|
89
|
-
if parent
|
90
|
-
schema['links'] << {
|
91
|
-
"description" => "List existing #{resource}s for existing #{parent}.",
|
92
|
-
"href" => "/#{parent}s/{(%2Fschemata%2F#{parent}%23%2Fdefinitions%2Fidentity)}/#{resource}s",
|
93
|
-
"method" => "GET",
|
94
|
-
"rel" => "instances",
|
95
|
-
"title" => "List"
|
96
|
-
}
|
97
|
-
end
|
98
|
-
schema['properties'] = {
|
99
|
-
"created_at" => { "$ref" => "/schemata/#{resource}#/definitions/created_at" },
|
100
|
-
"id" => { "$ref" => "/schemata/#{resource}#/definitions/id" },
|
101
|
-
"updated_at" => { "$ref" => "/schemata/#{resource}#/definitions/updated_at" }
|
102
|
-
}
|
29
|
+
parent = nil
|
30
|
+
parent, resource = resource.split('/') if resource.include?('/')
|
31
|
+
generator_options[:parent] = parent
|
32
|
+
generator_options[:resource] = resource
|
103
33
|
end
|
104
34
|
|
35
|
+
schema = gen.generate(generator_options)
|
36
|
+
|
105
37
|
if options[:yaml]
|
106
38
|
schema.to_yaml
|
107
39
|
else
|
data/lib/prmd/commands/render.rb
CHANGED
@@ -1,24 +1,37 @@
|
|
1
|
-
|
2
|
-
def self.render(schema, options={})
|
3
|
-
doc = ''
|
4
|
-
|
5
|
-
options[:content_type] ||= 'application/json'
|
6
|
-
options[:style] ||= 'default'
|
1
|
+
require 'prmd/core/renderer'
|
7
2
|
|
8
|
-
|
9
|
-
|
3
|
+
# :nodoc:
|
4
|
+
module Prmd
|
5
|
+
# Render helper module
|
6
|
+
module Render
|
7
|
+
# Retrieve the schema template
|
8
|
+
#
|
9
|
+
# @param [Hash<Symbol, Object>] options
|
10
|
+
# @return (see Prmd::Template.load_template)
|
11
|
+
def self.get_template(options)
|
12
|
+
template = options.fetch(:template) do
|
13
|
+
abort 'render: Template was not provided'
|
14
|
+
end
|
15
|
+
template_dir = File.expand_path(template)
|
16
|
+
# to keep backward compatibility
|
17
|
+
template_dir = File.dirname(template) unless File.directory?(template_dir)
|
18
|
+
Prmd::Template.load_template('schema.erb', template_dir)
|
10
19
|
end
|
20
|
+
end
|
11
21
|
|
12
|
-
|
13
|
-
|
14
|
-
|
22
|
+
# Render provided schema to Markdown
|
23
|
+
#
|
24
|
+
# @param [Prmd::Schema] schema
|
25
|
+
# @return [String] rendered schema in Markdown
|
26
|
+
def self.render(schema, options = {})
|
27
|
+
renderer = Prmd::Renderer.new(template: Render.get_template(options))
|
28
|
+
doc = ''
|
29
|
+
if options[:prepend]
|
30
|
+
doc <<
|
31
|
+
options[:prepend].map { |path| File.read(path) }.join("\n") <<
|
32
|
+
"\n"
|
15
33
|
end
|
16
|
-
|
17
|
-
doc << Prmd::Template::render('schema.erb', template_dir, {
|
18
|
-
options: options,
|
19
|
-
schema: schema
|
20
|
-
})
|
21
|
-
|
34
|
+
doc << renderer.render(schema, options)
|
22
35
|
doc
|
23
36
|
end
|
24
37
|
end
|
data/lib/prmd/commands/verify.rb
CHANGED
@@ -1,51 +1,94 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'json'
|
2
|
+
require 'json_schema'
|
3
3
|
|
4
|
+
# :nodoc:
|
4
5
|
module Prmd
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
# Schema Verification
|
7
|
+
module Verification
|
8
|
+
# These schemas are listed manually and in order because they reference each
|
9
|
+
# other.
|
10
|
+
SCHEMAS = [
|
11
|
+
'schema.json',
|
12
|
+
'hyper-schema.json',
|
13
|
+
'interagent-hyper-schema.json'
|
14
|
+
]
|
12
15
|
|
13
|
-
|
14
|
-
|
16
|
+
# @return [JsonSchema::DocumentStore]
|
17
|
+
def self.init_document_store
|
18
|
+
store = JsonSchema::DocumentStore.new
|
19
|
+
SCHEMAS.each do |file|
|
20
|
+
file = File.expand_path("../../../../schemas/#{file}", __FILE__)
|
21
|
+
data = JSON.parse(File.read(file))
|
22
|
+
schema = JsonSchema::Parser.new.parse!(data)
|
23
|
+
schema.expand_references!(store: store)
|
24
|
+
store.add_schema(schema)
|
25
|
+
end
|
26
|
+
store
|
27
|
+
end
|
15
28
|
|
16
|
-
|
17
|
-
|
29
|
+
# @return [JsonSchema::DocumentStore]
|
30
|
+
def self.document_store
|
31
|
+
@document_store ||= init_document_store
|
18
32
|
end
|
19
33
|
|
20
|
-
#
|
21
|
-
#
|
22
|
-
|
23
|
-
|
34
|
+
# @param [Hash] schema_data
|
35
|
+
# @return [Array<String>] errors from failed verfication
|
36
|
+
def self.verify_parsable(schema_data)
|
37
|
+
# for good measure, make sure that the schema parses and that its
|
38
|
+
# references can be expanded
|
39
|
+
schema, errors = JsonSchema.parse!(schema_data)
|
40
|
+
return JsonSchema::SchemaError.aggregate(errors) unless schema
|
24
41
|
|
25
|
-
|
26
|
-
|
42
|
+
valid, errors = schema.expand_references(store: document_store)
|
43
|
+
return JsonSchema::SchemaError.aggregate(errors) unless valid
|
27
44
|
|
28
|
-
|
29
|
-
return ["Unknown $schema: #{schema_uri}."]
|
45
|
+
[]
|
30
46
|
end
|
31
47
|
|
32
|
-
|
33
|
-
return
|
48
|
+
# @param [Hash] schema_data
|
49
|
+
# @return [Array<String>] errors from failed verfication
|
50
|
+
def self.verify_meta_schema(meta_schema, schema_data)
|
51
|
+
valid, errors = meta_schema.validate(schema_data)
|
52
|
+
return JsonSchema::SchemaError.aggregate(errors) unless valid
|
34
53
|
|
35
|
-
|
36
|
-
|
54
|
+
[]
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param [Hash] schema_data
|
58
|
+
# @return [Array<String>] errors from failed verfication
|
59
|
+
def self.verify_schema(schema_data)
|
60
|
+
schema_uri = schema_data['$schema']
|
61
|
+
return ['Missing $schema key.'] unless schema_uri
|
62
|
+
|
63
|
+
meta_schema = document_store.lookup_schema(schema_uri)
|
64
|
+
return ["Unknown $schema: #{schema_uri}."] unless meta_schema
|
37
65
|
|
38
|
-
|
66
|
+
verify_meta_schema(meta_schema, schema_data)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Verfies that a given schema is valid
|
70
|
+
#
|
71
|
+
# @param [Hash] schema_data
|
72
|
+
# @return [Array<String>] errors from failed verification
|
73
|
+
def self.verify(schema_data)
|
74
|
+
a = verify_schema(schema_data)
|
75
|
+
return a unless a.empty?
|
76
|
+
b = verify_parsable(schema_data)
|
77
|
+
return b unless b.empty?
|
78
|
+
[]
|
79
|
+
end
|
39
80
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
schema.expand_references!(store: store)
|
47
|
-
store.add_schema(schema)
|
81
|
+
class << self
|
82
|
+
private :init_document_store
|
83
|
+
private :document_store
|
84
|
+
private :verify_parsable
|
85
|
+
private :verify_schema
|
86
|
+
private :verify_meta_schema
|
48
87
|
end
|
49
|
-
|
88
|
+
end
|
89
|
+
|
90
|
+
# (see Prmd::Verification.verify)
|
91
|
+
def self.verify(schema_data)
|
92
|
+
Verification.verify(schema_data)
|
50
93
|
end
|
51
94
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'prmd/schema'
|
2
|
+
require 'prmd/core/schema_hash'
|
3
|
+
|
4
|
+
# :nodoc:
|
5
|
+
module Prmd
|
6
|
+
# Schema combiner
|
7
|
+
class Combiner
|
8
|
+
#
|
9
|
+
# @param [Hash<Symbol, Object>] properties
|
10
|
+
def initialize(properties = {})
|
11
|
+
@properties = properties
|
12
|
+
@schema = properties.fetch(:schema)
|
13
|
+
@base = properties.fetch(:base, {})
|
14
|
+
@meta = properties.fetch(:meta, {})
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param [Array] array
|
18
|
+
# @return [Array]
|
19
|
+
def reference_localizer_array(array)
|
20
|
+
array.map { |element| reference_localizer(element) }
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [Hash] hash
|
24
|
+
# @return [Hash]
|
25
|
+
def reference_localizer_hash(hash)
|
26
|
+
if hash.key?('$ref')
|
27
|
+
hash['$ref'] = '#/definitions' + hash['$ref'].gsub('#', '')
|
28
|
+
.gsub('/schemata', '')
|
29
|
+
end
|
30
|
+
if hash.key?('href') && hash['href'].is_a?(String)
|
31
|
+
hash['href'] = hash['href'].gsub('%23', '')
|
32
|
+
.gsub(/%2Fschemata(%2F[^%]*%2F)/,
|
33
|
+
'%23%2Fdefinitions\1')
|
34
|
+
end
|
35
|
+
hash.each_with_object({}) { |(k, v), r| r[k] = reference_localizer(v) }
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# @param [Object] datum
|
40
|
+
# @return [Object]
|
41
|
+
def reference_localizer(datum)
|
42
|
+
case datum
|
43
|
+
when Array
|
44
|
+
reference_localizer_array(datum)
|
45
|
+
when Hash
|
46
|
+
reference_localizer_hash(datum)
|
47
|
+
else
|
48
|
+
datum
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# @param [Prmd::SchemaHash] schemata
|
54
|
+
# @return [Prmd::Schema]
|
55
|
+
def combine(*schemata)
|
56
|
+
# tracks which entities where defined in which file
|
57
|
+
schemata_map = {}
|
58
|
+
|
59
|
+
data = {}
|
60
|
+
data.merge!(@base)
|
61
|
+
data.merge!(@meta)
|
62
|
+
|
63
|
+
schemata.each do |schema|
|
64
|
+
id = schema.fetch('id')
|
65
|
+
id_ary = id.split('/').last
|
66
|
+
|
67
|
+
if s = schemata_map[id]
|
68
|
+
$stderr.puts "`#{id}` (from #{schema.filename}) was already defined" \
|
69
|
+
"in `#{s.filename}` and will overwrite the first" \
|
70
|
+
"definition"
|
71
|
+
end
|
72
|
+
# avoinding damaging the original schema
|
73
|
+
embedded_schema = schema.dup
|
74
|
+
# schemas are now in a single scope by combine
|
75
|
+
embedded_schema.delete('id')
|
76
|
+
schemata_map[id] = embedded_schema
|
77
|
+
|
78
|
+
data['definitions'][id_ary] = embedded_schema.to_h
|
79
|
+
data['properties'][id_ary] = { '$ref' => "#/definitions/#{id_ary}" }
|
80
|
+
|
81
|
+
reference_localizer(data['definitions'][id_ary])
|
82
|
+
end
|
83
|
+
|
84
|
+
Prmd::Schema.new(data)
|
85
|
+
end
|
86
|
+
|
87
|
+
private :reference_localizer_array
|
88
|
+
private :reference_localizer_hash
|
89
|
+
private :reference_localizer
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'prmd/schema'
|
3
|
+
|
4
|
+
# :nodoc:
|
5
|
+
module Prmd
|
6
|
+
# Schema generator
|
7
|
+
class Generator
|
8
|
+
#
|
9
|
+
# @param [Hash<Symbol, Object>] properties
|
10
|
+
def initialize(properties = {})
|
11
|
+
@properties = properties
|
12
|
+
@base = properties.fetch(:base, {})
|
13
|
+
@template = properties.fetch(:template)
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# @param [Hash<Symbol, Object>] options
|
18
|
+
def generate(options = {})
|
19
|
+
res = @template.result(options)
|
20
|
+
resource_schema = JSON.parse(res)
|
21
|
+
schema = Prmd::Schema.new
|
22
|
+
schema.merge!(@base)
|
23
|
+
schema.merge!(resource_schema)
|
24
|
+
schema
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'prmd/template'
|
2
|
+
|
3
|
+
# :nodoc:
|
4
|
+
module Prmd
|
5
|
+
# Schema Generator
|
6
|
+
class Renderer
|
7
|
+
#
|
8
|
+
# @param [Hash<Symbol, Object>] properties
|
9
|
+
def initialize(properties = {})
|
10
|
+
@properties = properties
|
11
|
+
@template = @properties.fetch(:template)
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# @return [Hash<Symbol, Object>]
|
16
|
+
def default_options
|
17
|
+
{
|
18
|
+
http_header: {},
|
19
|
+
content_type: 'application/json',
|
20
|
+
doc: {},
|
21
|
+
prepend: nil
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# @param [Hash<Symbol, Object>] options
|
27
|
+
# @return [void]
|
28
|
+
def append_default_options(options)
|
29
|
+
options[:doc] = {
|
30
|
+
url_style: 'default',
|
31
|
+
disable_title_and_description: false
|
32
|
+
}.merge(options[:doc])
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# @param [Hash<Symbol, Object>] options
|
37
|
+
# @return [Hash<Symbol, Object>]
|
38
|
+
def setup_options(options)
|
39
|
+
opts = default_options
|
40
|
+
opts.merge!(options)
|
41
|
+
append_default_options(opts)
|
42
|
+
opts
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# @param [Prmd::Schema] schema
|
47
|
+
# @param [Hash<Symbol, Object>] options
|
48
|
+
def render(schema, options = {})
|
49
|
+
@template.result(schema: schema, options: setup_options(options))
|
50
|
+
end
|
51
|
+
|
52
|
+
private :default_options
|
53
|
+
private :append_default_options
|
54
|
+
private :setup_options
|
55
|
+
end
|
56
|
+
end
|