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
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
# :nodoc:
|
4
|
+
module Prmd
|
5
|
+
# Specialized Hash for handling loaded Schema data
|
6
|
+
class SchemaHash
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
# @return [Hash]
|
10
|
+
attr_reader :data
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :filename
|
13
|
+
|
14
|
+
def_delegator :@data, :[]
|
15
|
+
def_delegator :@data, :[]=
|
16
|
+
def_delegator :@data, :delete
|
17
|
+
def_delegator :@data, :each
|
18
|
+
|
19
|
+
# @param [Hash] data
|
20
|
+
# @param [Hash<Symbol, Object>] options
|
21
|
+
def initialize(data, options = {})
|
22
|
+
@data = data
|
23
|
+
@filename = options.fetch(:filename, '')
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param [Prmd::SchemaHash] other
|
27
|
+
# @return [self]
|
28
|
+
def initialize_copy(other)
|
29
|
+
super
|
30
|
+
@data = other.data.dup
|
31
|
+
@filename = other.filename.dup
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param [String] key
|
35
|
+
# @return [self]
|
36
|
+
def fetch(key)
|
37
|
+
@data.fetch(key) { abort "Missing key #{key} in #{filename}" }
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Hash]
|
41
|
+
def to_h
|
42
|
+
@data.dup
|
43
|
+
end
|
44
|
+
|
45
|
+
protected :data
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# :nodoc:
|
2
|
+
module Prmd
|
3
|
+
# Hash helper methods
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
module HashHelpers
|
7
|
+
# Attempts to convert all keys in the hash to a Symbol.
|
8
|
+
# This operation is recursive with subhashes
|
9
|
+
#
|
10
|
+
# @param [Hash] hash
|
11
|
+
# @return [Hash]
|
12
|
+
def self.deep_symbolize_keys(hash)
|
13
|
+
deep_transform_keys(hash) do |key|
|
14
|
+
if key.respond_to?(:to_sym)
|
15
|
+
key.to_sym
|
16
|
+
else
|
17
|
+
key
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Think of this as hash.keys.map! { |key| }, that actually maps recursively.
|
23
|
+
#
|
24
|
+
# @param [Hash] hash
|
25
|
+
# @return [Hash]
|
26
|
+
# @yield [Object] key
|
27
|
+
def self.deep_transform_keys(hash, &block)
|
28
|
+
result = {}
|
29
|
+
hash.each do |key, value|
|
30
|
+
new_key = yield(key)
|
31
|
+
new_value = value
|
32
|
+
new_value = deep_transform_keys(value, &block) if value.is_a?(Hash)
|
33
|
+
result[new_key] = new_value
|
34
|
+
end
|
35
|
+
result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
# :nodoc:
|
5
|
+
module Prmd
|
6
|
+
# Attempts to load either a json or yaml file, the type is determined by
|
7
|
+
# filename extension.
|
8
|
+
#
|
9
|
+
# @param [String] filename
|
10
|
+
# @return [Object] data
|
11
|
+
def self.load_schema_file(filename)
|
12
|
+
extname = File.extname(filename)
|
13
|
+
File.open(filename) do |file|
|
14
|
+
case extname.downcase
|
15
|
+
when '.yaml', '.yml'
|
16
|
+
YAML.load(file.read)
|
17
|
+
when '.json'
|
18
|
+
JSON.load(file.read)
|
19
|
+
else
|
20
|
+
abort "Cannot load schema file #{filename}" \
|
21
|
+
"(unsupported file extension #{extname})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
# :nodoc:
|
5
|
+
module Prmd
|
6
|
+
# :nodoc:
|
7
|
+
module RakeTasks
|
8
|
+
# Common class for Prmd rake tasks
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
class Base < Rake::TaskLib
|
12
|
+
# The name of the task
|
13
|
+
# @return [String] the task name
|
14
|
+
attr_accessor :name
|
15
|
+
|
16
|
+
# Options to pass to command
|
17
|
+
# @return [Hash<Symbol, Object>] the options passed to the command
|
18
|
+
attr_accessor :options
|
19
|
+
|
20
|
+
# Creates a new task with name +name+.
|
21
|
+
#
|
22
|
+
# @param [String, Symbol] name the name of the rake task
|
23
|
+
def initialize(name)
|
24
|
+
@name = name
|
25
|
+
@options = {}
|
26
|
+
|
27
|
+
yield self if block_given?
|
28
|
+
|
29
|
+
define
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'prmd/commands/combine'
|
2
|
+
require 'prmd/rake_tasks/base'
|
3
|
+
|
4
|
+
# :nodoc:
|
5
|
+
module Prmd
|
6
|
+
# :nodoc:
|
7
|
+
module RakeTasks
|
8
|
+
# Schema combine rake task
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Prmd::RakeTasks::Combine.new do |t|
|
12
|
+
# t.options[:meta] = 'schema/meta.json'
|
13
|
+
# t.paths << 'schema/schemata/api'
|
14
|
+
# t.output_file = 'schema/api.json'
|
15
|
+
# end
|
16
|
+
class Combine < Base
|
17
|
+
#
|
18
|
+
# @return [Array<String>] list of paths
|
19
|
+
attr_accessor :paths
|
20
|
+
|
21
|
+
# target file the combined result should be written
|
22
|
+
# @return [String>] target filename
|
23
|
+
attr_accessor :output_file
|
24
|
+
|
25
|
+
# Creates a new task with name +name+.
|
26
|
+
#
|
27
|
+
# @param [String, Symbol] name the name of the rake task
|
28
|
+
def initialize(name = :combine)
|
29
|
+
@paths = []
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
# Defines the rake task
|
36
|
+
# @return [void]
|
37
|
+
def define
|
38
|
+
desc 'Combine schemas' unless Rake.application.last_comment
|
39
|
+
task(name) do
|
40
|
+
result = Prmd.combine(paths, options)
|
41
|
+
if output_file
|
42
|
+
File.open(output_file, 'w') do |file|
|
43
|
+
file.write(result)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'prmd/commands/render'
|
2
|
+
require 'prmd/rake_tasks/base'
|
3
|
+
require 'prmd/load_schema_file'
|
4
|
+
require 'prmd/template'
|
5
|
+
require 'prmd/schema'
|
6
|
+
|
7
|
+
# :nodoc:
|
8
|
+
module Prmd
|
9
|
+
# :nodoc:
|
10
|
+
module RakeTasks
|
11
|
+
# Documentation rake task
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# Prmd::RakeTasks::Doc.new do |t|
|
15
|
+
# t.files = { 'schema/api.json' => 'schema/api.md' }
|
16
|
+
# end
|
17
|
+
class Doc < Base
|
18
|
+
# Schema files that should be verified
|
19
|
+
# @return [Array<String>, Hash<String, String>] list of files
|
20
|
+
attr_accessor :files
|
21
|
+
|
22
|
+
# Creates a new task with name +name+.
|
23
|
+
#
|
24
|
+
# @param [String, Symbol] name the name of the rake task
|
25
|
+
def initialize(name = :doc, &block)
|
26
|
+
@files = []
|
27
|
+
super name, &block
|
28
|
+
@options[:template] ||= Prmd::Template.template_dirname
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Render file to markdown
|
34
|
+
#
|
35
|
+
# @param [String] filename
|
36
|
+
# @return (see Prmd.render)
|
37
|
+
def render_file(filename)
|
38
|
+
data = Prmd.load_schema_file(filename)
|
39
|
+
schema = Prmd::Schema.new(data)
|
40
|
+
Prmd.render(schema, options)
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param [String] infile
|
44
|
+
# @param [String] outfile
|
45
|
+
# @return [void]
|
46
|
+
def render_to_file(infile, outfile)
|
47
|
+
result = render_file(infile)
|
48
|
+
File.open(outfile, 'w') do |file|
|
49
|
+
file.write(result)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
# Defines the rake task
|
56
|
+
# @return [void]
|
57
|
+
def define
|
58
|
+
desc 'Verifying schemas' unless Rake.application.last_comment
|
59
|
+
task(name) do
|
60
|
+
if files.is_a?(Hash)
|
61
|
+
files.each do |infile, outfile|
|
62
|
+
render_to_file(infile, outfile)
|
63
|
+
end
|
64
|
+
else
|
65
|
+
files.each do |infile|
|
66
|
+
render_to_file(infile, infile.ext('md'))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'prmd/commands/verify'
|
2
|
+
require 'prmd/rake_tasks/base'
|
3
|
+
require 'prmd/load_schema_file'
|
4
|
+
|
5
|
+
# :nodoc:
|
6
|
+
module Prmd
|
7
|
+
# :nodoc:
|
8
|
+
module RakeTasks
|
9
|
+
# Schema Verify rake task
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# Prmd::RakeTasks::Verify.new do |t|
|
13
|
+
# t.files << 'schema/api.json'
|
14
|
+
# end
|
15
|
+
class Verify < Base
|
16
|
+
# Schema files that should be verified
|
17
|
+
# @return [Array<String>] list of files
|
18
|
+
attr_accessor :files
|
19
|
+
|
20
|
+
# Creates a new task with name +name+.
|
21
|
+
#
|
22
|
+
# @param [String, Symbol] name the name of the rake task
|
23
|
+
def initialize(name = :verify)
|
24
|
+
@files = []
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Defines the rake task
|
31
|
+
#
|
32
|
+
# @param [String] filename
|
33
|
+
# @return [Array<String>] list of errors produced
|
34
|
+
def verify_file(filename)
|
35
|
+
data = Prmd.load_schema_file(filename)
|
36
|
+
errors = Prmd.verify(data)
|
37
|
+
unless errors.empty?
|
38
|
+
errors.map! { |error| "#{filename}: #{error}" } if filename
|
39
|
+
errors.each { |error| $stderr.puts(error) }
|
40
|
+
end
|
41
|
+
errors
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
# Defines the rake task
|
47
|
+
# @return [void]
|
48
|
+
def define
|
49
|
+
desc 'Verifying schemas' unless Rake.application.last_comment
|
50
|
+
task(name) do
|
51
|
+
all_errors = []
|
52
|
+
files.each do |filename|
|
53
|
+
all_errors.concat(verify_file(filename))
|
54
|
+
end
|
55
|
+
fail unless all_errors.empty?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/prmd/schema.rb
CHANGED
@@ -1,35 +1,67 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
# :nodoc:
|
1
5
|
module Prmd
|
6
|
+
# Schema object
|
2
7
|
class Schema
|
8
|
+
# @return [Hash] data
|
9
|
+
attr_reader :data
|
10
|
+
|
11
|
+
# @param [Hash<String, Object>] new_data
|
12
|
+
def initialize(new_data = {})
|
13
|
+
@data = convert_type_to_array(new_data)
|
14
|
+
@schemata_examples = {}
|
15
|
+
end
|
3
16
|
|
17
|
+
#
|
18
|
+
# @param [Object] datum
|
19
|
+
# @return [Object] same type as the input object
|
20
|
+
def convert_type_to_array(datum)
|
21
|
+
case datum
|
22
|
+
when Array
|
23
|
+
datum.map { |element| convert_type_to_array(element) }
|
24
|
+
when Hash
|
25
|
+
if datum.key?('type') && datum['type'].is_a?(String)
|
26
|
+
datum['type'] = [*datum['type']]
|
27
|
+
end
|
28
|
+
datum.each_with_object({}) do |(k, v), hash|
|
29
|
+
hash[k] = convert_type_to_array(v)
|
30
|
+
end
|
31
|
+
else
|
32
|
+
datum
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [String] key
|
37
|
+
# @return [Object]
|
4
38
|
def [](key)
|
5
39
|
@data[key]
|
6
40
|
end
|
7
41
|
|
42
|
+
# @param [String] key
|
43
|
+
# @param [Object] value
|
8
44
|
def []=(key, value)
|
9
45
|
@data[key] = value
|
10
46
|
end
|
11
47
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
datum.each { |k,v| datum[k] = convert_type_to_array.call(v) }
|
22
|
-
else
|
23
|
-
datum
|
24
|
-
end
|
48
|
+
# Merge schema data with provided schema
|
49
|
+
#
|
50
|
+
# @param [Hash, Prmd::Schema] schema
|
51
|
+
# @return [void]
|
52
|
+
def merge!(schema)
|
53
|
+
if schema.is_a?(Schema)
|
54
|
+
@data.merge!(schema.data)
|
55
|
+
else
|
56
|
+
@data.merge!(schema)
|
25
57
|
end
|
26
|
-
@data = convert_type_to_array.call(new_data)
|
27
|
-
@schemata_examples = {}
|
28
58
|
end
|
29
59
|
|
60
|
+
#
|
61
|
+
# @param [Hash, String] reference
|
30
62
|
def dereference(reference)
|
31
63
|
if reference.is_a?(Hash)
|
32
|
-
if reference.
|
64
|
+
if reference.key?('$ref')
|
33
65
|
value = reference.dup
|
34
66
|
key = value.delete('$ref')
|
35
67
|
else
|
@@ -40,7 +72,7 @@ module Prmd
|
|
40
72
|
end
|
41
73
|
begin
|
42
74
|
datum = @data
|
43
|
-
key.gsub(
|
75
|
+
key.gsub(/[^#]*#\//, '').split('/').each do |fragment|
|
44
76
|
datum = datum[fragment]
|
45
77
|
end
|
46
78
|
# last dereference will have nil key, so compact it out
|
@@ -48,25 +80,29 @@ module Prmd
|
|
48
80
|
dereferenced_key, dereferenced_value = dereference(datum)
|
49
81
|
[
|
50
82
|
[key, dereferenced_key].compact.last,
|
51
|
-
[dereferenced_value, value].inject({}
|
83
|
+
[dereferenced_value, value].inject({}, &:merge)
|
52
84
|
]
|
53
85
|
rescue => error
|
54
86
|
$stderr.puts("Failed to dereference `#{key}`")
|
55
|
-
raise
|
87
|
+
raise error
|
56
88
|
end
|
57
89
|
end
|
58
90
|
|
91
|
+
# @param [Hash] value
|
59
92
|
def schema_value_example(value)
|
60
|
-
if value.
|
93
|
+
if value.key?('example')
|
61
94
|
value['example']
|
62
|
-
elsif value.
|
63
|
-
|
95
|
+
elsif value.key?('anyOf')
|
96
|
+
id_ref = value['anyOf'].find do |ref|
|
97
|
+
ref['$ref'] && ref['$ref'].split('/').last == 'id'
|
98
|
+
end
|
99
|
+
ref = id_ref || value['anyOf'].first
|
64
100
|
schema_example(ref)
|
65
|
-
elsif value.
|
101
|
+
elsif value.key?('properties') # nested properties
|
66
102
|
schema_example(value)
|
67
|
-
elsif value.
|
103
|
+
elsif value.key?('items') # array of objects
|
68
104
|
_, items = dereference(value['items'])
|
69
|
-
if value['items'].
|
105
|
+
if value['items'].key?('example')
|
70
106
|
[items['example']]
|
71
107
|
else
|
72
108
|
[schema_example(items)]
|
@@ -74,23 +110,25 @@ module Prmd
|
|
74
110
|
end
|
75
111
|
end
|
76
112
|
|
113
|
+
# @param [Hash, String] schema
|
77
114
|
def schema_example(schema)
|
78
|
-
_,
|
115
|
+
_, dff_schema = dereference(schema)
|
79
116
|
|
80
|
-
if
|
81
|
-
|
82
|
-
elsif
|
117
|
+
if dff_schema.key?('example')
|
118
|
+
dff_schema['example']
|
119
|
+
elsif dff_schema.key?('properties')
|
83
120
|
example = {}
|
84
|
-
|
121
|
+
dff_schema['properties'].each do |key, value|
|
85
122
|
_, value = dereference(value)
|
86
123
|
example[key] = schema_value_example(value)
|
87
124
|
end
|
88
125
|
example
|
89
|
-
elsif
|
90
|
-
schema_value_example(
|
126
|
+
elsif dff_schema.key?('items')
|
127
|
+
schema_value_example(dff_schema)
|
91
128
|
end
|
92
129
|
end
|
93
130
|
|
131
|
+
# @param [String] schemata_id
|
94
132
|
def schemata_example(schemata_id)
|
95
133
|
_, schema = dereference("#/definitions/#{schemata_id}")
|
96
134
|
@schemata_examples[schemata_id] ||= begin
|
@@ -98,24 +136,38 @@ module Prmd
|
|
98
136
|
end
|
99
137
|
end
|
100
138
|
|
139
|
+
# Retrieve this schema's href
|
140
|
+
#
|
141
|
+
# @return [String, nil]
|
101
142
|
def href
|
102
|
-
(@data['links'].
|
143
|
+
(@data['links'] && @data['links'].find { |link| link['rel'] == 'self' } || {})['href']
|
103
144
|
end
|
104
145
|
|
146
|
+
# Convert Schema to JSON
|
147
|
+
#
|
148
|
+
# @return [String]
|
105
149
|
def to_json
|
106
150
|
new_json = JSON.pretty_generate(@data)
|
107
151
|
# nuke empty lines
|
108
|
-
new_json = new_json.split("\n").
|
152
|
+
new_json = new_json.split("\n").reject(&:empty?).join("\n") + "\n"
|
109
153
|
new_json
|
110
154
|
end
|
111
155
|
|
156
|
+
# Convert Schema to YAML
|
157
|
+
#
|
158
|
+
# @return [String]
|
112
159
|
def to_yaml
|
113
160
|
YAML.dump(@data)
|
114
161
|
end
|
115
162
|
|
163
|
+
# Convert Schema to String
|
164
|
+
#
|
165
|
+
# @return [String]
|
116
166
|
def to_s
|
117
167
|
to_json
|
118
168
|
end
|
119
169
|
|
170
|
+
private :convert_type_to_array
|
171
|
+
protected :data
|
120
172
|
end
|
121
173
|
end
|