prmd 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +5 -13
  2. data/CONTRIBUTORS.md +8 -0
  3. data/Gemfile.lock +6 -6
  4. data/README.md +57 -8
  5. data/Rakefile +4 -4
  6. data/bin/prmd +3 -117
  7. data/docs/schemata.md +11 -11
  8. data/lib/prmd.rb +7 -18
  9. data/lib/prmd/cli.rb +108 -0
  10. data/lib/prmd/cli/base.rb +151 -0
  11. data/lib/prmd/cli/combine.rb +42 -0
  12. data/lib/prmd/cli/doc.rb +69 -0
  13. data/lib/prmd/cli/generate.rb +44 -0
  14. data/lib/prmd/cli/render.rb +48 -0
  15. data/lib/prmd/cli/verify.rb +49 -0
  16. data/lib/prmd/commands.rb +4 -0
  17. data/lib/prmd/commands/combine.rb +85 -58
  18. data/lib/prmd/commands/init.rb +30 -98
  19. data/lib/prmd/commands/render.rb +30 -17
  20. data/lib/prmd/commands/verify.rb +78 -35
  21. data/lib/prmd/core/combiner.rb +91 -0
  22. data/lib/prmd/core/generator.rb +27 -0
  23. data/lib/prmd/core/renderer.rb +56 -0
  24. data/lib/prmd/core/schema_hash.rb +47 -0
  25. data/lib/prmd/core_ext/optparse.rb +6 -0
  26. data/lib/prmd/hash_helpers.rb +38 -0
  27. data/lib/prmd/load_schema_file.rb +25 -0
  28. data/lib/prmd/rake_tasks/base.rb +33 -0
  29. data/lib/prmd/rake_tasks/combine.rb +50 -0
  30. data/lib/prmd/rake_tasks/doc.rb +73 -0
  31. data/lib/prmd/rake_tasks/verify.rb +60 -0
  32. data/lib/prmd/schema.rb +86 -34
  33. data/lib/prmd/template.rb +65 -8
  34. data/lib/prmd/templates/combine_head.json +6 -0
  35. data/lib/prmd/templates/init_default.json +9 -0
  36. data/lib/prmd/templates/init_resource.json.erb +90 -0
  37. data/lib/prmd/templates/link_schema_properties.md.erb +5 -0
  38. data/lib/prmd/templates/schema.erb +2 -2
  39. data/lib/prmd/templates/schemata.md.erb +37 -0
  40. data/lib/prmd/templates/schemata/helper.erb +29 -15
  41. data/lib/prmd/templates/schemata/link.md.erb +74 -0
  42. data/lib/prmd/templates/schemata/{link_curl_example.erb → link_curl_example.md.erb} +8 -2
  43. data/lib/prmd/url_generator.rb +11 -69
  44. data/lib/prmd/url_generators/generators/default.rb +66 -0
  45. data/lib/prmd/url_generators/generators/json.rb +30 -0
  46. data/lib/prmd/version.rb +10 -1
  47. data/prmd.gemspec +15 -15
  48. data/test/cli/combine_test.rb +23 -0
  49. data/test/cli/doc_test.rb +25 -0
  50. data/test/cli/generate_test.rb +23 -0
  51. data/test/cli/render_test.rb +25 -0
  52. data/test/cli/verify_test.rb +21 -0
  53. data/test/commands/init_test.rb +7 -0
  54. data/test/commands/render_test.rb +93 -0
  55. data/test/commands/verify_test.rb +60 -60
  56. data/test/helpers.rb +61 -6
  57. data/test/schema_test.rb +17 -11
  58. data/test/schemata/input/doc-settings.json +4 -0
  59. metadata +73 -28
  60. data/lib/prmd/commands/expand.rb +0 -108
  61. data/lib/prmd/templates/link_schema_properties.erb +0 -16
  62. data/lib/prmd/templates/schemata.erb +0 -47
  63. 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,6 @@
1
+ require 'optparse'
2
+
3
+ # Extension of the standard library OptionParser
4
+ class OptionParser
5
+ alias :to_str :to_s
6
+ 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
@@ -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
- def initialize(new_data = {})
13
- convert_type_to_array = lambda do |datum|
14
- case datum
15
- when Array
16
- datum.map { |element| convert_type_to_array.call(element) }
17
- when Hash
18
- if datum.has_key?('type') && datum['type'].is_a?(String)
19
- datum['type'] = [*datum['type']]
20
- end
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.has_key?('$ref')
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(%r{[^#]*#/}, '').split('/').each do |fragment|
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({}) { |composite, element| composite.merge(element) }
83
+ [dereferenced_value, value].inject({}, &:merge)
52
84
  ]
53
85
  rescue => error
54
86
  $stderr.puts("Failed to dereference `#{key}`")
55
- raise(error)
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.has_key?('example')
93
+ if value.key?('example')
61
94
  value['example']
62
- elsif value.has_key?('anyOf')
63
- ref = value['anyOf'].detect {|ref| ref['$ref'].split('/').last == 'id'} || value['anyOf'].first
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.has_key?('properties') # nested properties
101
+ elsif value.key?('properties') # nested properties
66
102
  schema_example(value)
67
- elsif value.has_key?('items') # array of objects
103
+ elsif value.key?('items') # array of objects
68
104
  _, items = dereference(value['items'])
69
- if value['items'].has_key?('example')
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
- _, _schema = dereference(schema)
115
+ _, dff_schema = dereference(schema)
79
116
 
80
- if _schema.has_key?('example')
81
- _schema['example']
82
- elsif _schema.has_key?('properties')
117
+ if dff_schema.key?('example')
118
+ dff_schema['example']
119
+ elsif dff_schema.key?('properties')
83
120
  example = {}
84
- _schema['properties'].each do |key, value|
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 _schema.has_key?('items')
90
- schema_value_example(_schema)
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'].detect { |link| link['rel'] == 'self' } || {})['href']
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").delete_if {|line| line.empty?}.join("\n") + "\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