rspec-api-docs 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +46 -7
- data/Rakefile +9 -0
- data/lib/rspec_api_docs/after.rb +7 -2
- data/lib/rspec_api_docs/dsl/doc_proxy.rb +23 -3
- data/lib/rspec_api_docs/formatter.rb +1 -1
- data/lib/rspec_api_docs/formatter/resource.rb +10 -1
- data/lib/rspec_api_docs/formatter/resource/example.rb +23 -5
- data/lib/rspec_api_docs/formatter/resource/example/deep_hash_set.rb +58 -0
- data/lib/rspec_api_docs/formatter/resource/parameter.rb +5 -0
- data/lib/rspec_api_docs/formatter/resource/response_field.rb +5 -0
- data/lib/rspec_api_docs/rake_task.rb +35 -17
- data/lib/rspec_api_docs/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eee6ccdbceb3b61271298301b471e3d09a9b7edb
|
4
|
+
data.tar.gz: 8a1e3b753359755cde4ca89586ffd5f3163d3d4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97555c1200f96e8370b333fc9ff86e5ffb011a782fa51aab4231d74527d1e0d21484bad7326233b7248c3cedb816fc29e1fdc91d851704810ea2a07216e2c98c
|
7
|
+
data.tar.gz: 6f93f8c19a225543f023a737e52a0ac8c6829c7fd62d97ed8f62b8c3e21d5697599e7594f73f41e51e3d5b8be6e15bad7ed1f57ef6be23899577b81fedb44cc5
|
data/README.md
CHANGED
@@ -154,6 +154,12 @@ Accepts a string that describes the resource.
|
|
154
154
|
|
155
155
|
> Characters inhabit the Land of Ooo.
|
156
156
|
|
157
|
+
#### `resource_precedence`
|
158
|
+
|
159
|
+
Accepts an optional integer.
|
160
|
+
|
161
|
+
Lower numbers are ordered first.
|
162
|
+
|
157
163
|
#### `name`
|
158
164
|
|
159
165
|
Accepts a string of the name of the resource.
|
@@ -185,20 +191,24 @@ Note: This defaults to the path of the first route requested in the example.
|
|
185
191
|
|
186
192
|
#### `field`
|
187
193
|
|
188
|
-
Accepts a `name`, `description`, and optionally a `scope` and `
|
194
|
+
Accepts a `name`, `description`, and optionally a `scope`, `type`, and `example`.
|
189
195
|
|
190
196
|
- `name` [`Symbol`] the name of the response field
|
191
197
|
- `description` [`String`] a description of the response field
|
192
198
|
- `scope` [`Symbol`, `Array<Symbol>`] _(optional)_ how the field is scoped
|
193
199
|
- `type` [`String`] _(optional)_ the type of the returned field
|
200
|
+
- `example` _(optional)_ an example value
|
194
201
|
|
195
202
|
This can be called multiple times for each response field.
|
196
203
|
|
197
204
|
``` ruby
|
198
|
-
field :id, 'The id of a character', scope: :character, type: 'integer'
|
205
|
+
field :id, 'The id of a character', scope: :character, type: 'integer', example: 42
|
199
206
|
field :name, "The character's name", scope: :character, type: 'string'
|
200
207
|
```
|
201
208
|
|
209
|
+
The `example` is useful if the data might change (i.e. a database ID column).
|
210
|
+
The value will be substituted in the resulting JSON.
|
211
|
+
|
202
212
|
#### `param`
|
203
213
|
|
204
214
|
Accepts a `name`, `description`, and optionally a `scope`, `type`, and `required` flag.
|
@@ -228,6 +238,12 @@ note 'You need to supply an id!'
|
|
228
238
|
note :warning, "An error will be thrown if you don't supply an id!"
|
229
239
|
```
|
230
240
|
|
241
|
+
#### `precedence`
|
242
|
+
|
243
|
+
Accepts an optional integer.
|
244
|
+
|
245
|
+
Lower numbers are ordered first.
|
246
|
+
|
231
247
|
See the integration specs for more examples of the DSL in use.
|
232
248
|
|
233
249
|
### Formatter
|
@@ -255,6 +271,34 @@ end
|
|
255
271
|
|
256
272
|
See [the documentation](http://www.rubydoc.info/github/twe4ked/rspec-api-docs/master).
|
257
273
|
|
274
|
+
## Rake tasks
|
275
|
+
|
276
|
+
``` ruby
|
277
|
+
require 'rspec_api_docs/rake_task'
|
278
|
+
|
279
|
+
RspecApiDocs::Rake.new do |task| # docs:generate
|
280
|
+
# Pattern for where to find the specs
|
281
|
+
task.pattern = 'spec/requests/**/*_spec.rb'
|
282
|
+
|
283
|
+
# Extra RSpec options
|
284
|
+
task.rspec_opts = ['--format progress']
|
285
|
+
end
|
286
|
+
|
287
|
+
RspecApiDocs::Rake.new do |task| # docs:ensure_updated
|
288
|
+
# Same as options above with some extras for when verify is true
|
289
|
+
|
290
|
+
# Raise an error if the generated docs don't match the existing docs
|
291
|
+
# The verify option only works with the :json renderer.
|
292
|
+
task.verify = true
|
293
|
+
|
294
|
+
# The existing (committed) output file
|
295
|
+
task.existing_file = 'docs/index.json'
|
296
|
+
end
|
297
|
+
|
298
|
+
# Non-verify task with custom name
|
299
|
+
RspecApiDocs::Rake.new :custom_task_name
|
300
|
+
```
|
301
|
+
|
258
302
|
## Development
|
259
303
|
|
260
304
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
@@ -271,11 +315,6 @@ Regenerate this project's integration spec docs locally:
|
|
271
315
|
$ ./bin/generate_integration_docs
|
272
316
|
```
|
273
317
|
|
274
|
-
## TODO
|
275
|
-
|
276
|
-
- Allow specifying an order (`precedence`?)
|
277
|
-
- Order Resources and Examples alphabetically
|
278
|
-
|
279
318
|
## Contributing
|
280
319
|
|
281
320
|
Bug reports and pull requests are welcome on GitHub at
|
data/Rakefile
CHANGED
@@ -24,6 +24,15 @@ RspecApiDocs::RakeTask.new do |task|
|
|
24
24
|
]
|
25
25
|
task.pattern = 'spec/integration/rspec_api_docs_spec.rb'
|
26
26
|
task.existing_file = 'spec/integration/output/json/index.json'
|
27
|
+
task.verify = true
|
28
|
+
end
|
29
|
+
|
30
|
+
RspecApiDocs::RakeTask.new do |task|
|
31
|
+
task.verbose = false
|
32
|
+
task.rspec_opts = [
|
33
|
+
'--format progress',
|
34
|
+
]
|
35
|
+
task.pattern = 'spec/integration/rspec_api_docs_spec.rb'
|
27
36
|
end
|
28
37
|
|
29
38
|
task default: [:rspec, :rubocop, :generate_integration_docs]
|
data/lib/rspec_api_docs/after.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# TODO: Move Resource out of formatter dir
|
2
|
+
require 'rspec_api_docs/formatter/resource'
|
1
3
|
require 'rspec_api_docs/after/type_checker'
|
2
4
|
|
3
5
|
module RspecApiDocs
|
@@ -15,8 +17,11 @@ module RspecApiDocs
|
|
15
17
|
|
16
18
|
metadata[:requests].each do |request, response|
|
17
19
|
request.params.each do |key, value|
|
18
|
-
|
19
|
-
|
20
|
+
parameter = RspecApiDocs::Resource::Example.new(example).parameters
|
21
|
+
.select { |parameter| parameter.name == key.to_sym }.first
|
22
|
+
|
23
|
+
if parameter
|
24
|
+
After::TypeChecker.call(type: parameter.type, value: value)
|
20
25
|
else
|
21
26
|
raise UndocumentedParameter, "undocumented parameter included in request #{key.inspect}"
|
22
27
|
end
|
@@ -36,6 +36,15 @@ module RspecApiDocs
|
|
36
36
|
metadata[METADATA_NAMESPACE][:resource_description] = value
|
37
37
|
end
|
38
38
|
|
39
|
+
# For setting the precedence of the resource
|
40
|
+
#
|
41
|
+
# Lower numbers will be ordered higher
|
42
|
+
#
|
43
|
+
# @param value [Integer] the precedence
|
44
|
+
def resource_precedence(value)
|
45
|
+
metadata[METADATA_NAMESPACE][:resource_precedence] = value
|
46
|
+
end
|
47
|
+
|
39
48
|
# For setting a description of the example.
|
40
49
|
#
|
41
50
|
# E.g. "Allows you to return a single character."
|
@@ -76,13 +85,15 @@ module RspecApiDocs
|
|
76
85
|
# @param description [String] a description of the response field
|
77
86
|
# @param scope [Symbol, Array<Symbol>] how the field is scoped
|
78
87
|
# @param type [String]
|
88
|
+
# @param example an example value
|
79
89
|
# @return [void]
|
80
|
-
def field(name, description, scope: [], type: nil)
|
90
|
+
def field(name, description, scope: [], type: nil, example: nil)
|
81
91
|
metadata[METADATA_NAMESPACE][:fields] ||= {}
|
82
|
-
metadata[METADATA_NAMESPACE][:fields][name] = {
|
92
|
+
metadata[METADATA_NAMESPACE][:fields][{name: name, scope: scope}] = {
|
83
93
|
description: description,
|
84
94
|
scope: Array(scope),
|
85
95
|
type: type,
|
96
|
+
example: example,
|
86
97
|
}
|
87
98
|
end
|
88
99
|
|
@@ -107,7 +118,7 @@ module RspecApiDocs
|
|
107
118
|
# @return [void]
|
108
119
|
def param(name, description, scope: [], type: nil, required: false)
|
109
120
|
metadata[METADATA_NAMESPACE][:parameters] ||= {}
|
110
|
-
metadata[METADATA_NAMESPACE][:parameters][name] = {
|
121
|
+
metadata[METADATA_NAMESPACE][:parameters][{name: name, scope: scope}] = {
|
111
122
|
description: description,
|
112
123
|
scope: Array(scope),
|
113
124
|
type: type,
|
@@ -126,6 +137,15 @@ module RspecApiDocs
|
|
126
137
|
metadata[METADATA_NAMESPACE][:note] ||= {}
|
127
138
|
metadata[METADATA_NAMESPACE][:note][level] = value
|
128
139
|
end
|
140
|
+
|
141
|
+
# For setting the precedence of an example
|
142
|
+
#
|
143
|
+
# Lower numbers will be ordered higher
|
144
|
+
#
|
145
|
+
# @param value [Integer] the precedence
|
146
|
+
def precedence(value)
|
147
|
+
metadata[METADATA_NAMESPACE][:example_precedence] = value
|
148
|
+
end
|
129
149
|
end
|
130
150
|
end
|
131
151
|
end
|
@@ -40,7 +40,7 @@ module RspecApiDocs
|
|
40
40
|
#
|
41
41
|
# @return [void]
|
42
42
|
def close(null_notification)
|
43
|
-
renderer.new(resources.values.sort_by
|
43
|
+
renderer.new(resources.values.sort_by { |resource| [resource.precedence, resource.name].join }).render
|
44
44
|
end
|
45
45
|
|
46
46
|
private
|
@@ -33,7 +33,7 @@ module RspecApiDocs
|
|
33
33
|
#
|
34
34
|
# @return [Array<Example>]
|
35
35
|
def examples
|
36
|
-
@examples.sort_by
|
36
|
+
@examples.sort_by { |example| [example.precedence, example.name].join }
|
37
37
|
end
|
38
38
|
|
39
39
|
# Add an example
|
@@ -43,6 +43,15 @@ module RspecApiDocs
|
|
43
43
|
@examples << example
|
44
44
|
end
|
45
45
|
|
46
|
+
# @return [String, nil]
|
47
|
+
def precedence
|
48
|
+
metadata[:resource_precedence]
|
49
|
+
end
|
50
|
+
|
51
|
+
def inspect
|
52
|
+
"#<RspecApiDocs::Resource #{name.inspect}, @examples=#{examples.inspect}>"
|
53
|
+
end
|
54
|
+
|
46
55
|
private
|
47
56
|
|
48
57
|
def metadata
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rspec_api_docs/formatter/resource/example/request_headers'
|
2
|
+
require 'rspec_api_docs/formatter/resource/example/deep_hash_set'
|
2
3
|
|
3
4
|
module RspecApiDocs
|
4
5
|
class Resource
|
@@ -31,8 +32,8 @@ module RspecApiDocs
|
|
31
32
|
#
|
32
33
|
# @return [Array<Parameter>]
|
33
34
|
def parameters
|
34
|
-
metadata.fetch(:parameters, []).map do |
|
35
|
-
Parameter.new(name, parameter)
|
35
|
+
metadata.fetch(:parameters, []).map do |name_hash, parameter|
|
36
|
+
Parameter.new(name_hash[:name], parameter)
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
@@ -40,8 +41,8 @@ module RspecApiDocs
|
|
40
41
|
#
|
41
42
|
# @return [Array<ResponseField>]
|
42
43
|
def response_fields
|
43
|
-
metadata.fetch(:fields, []).map do |
|
44
|
-
ResponseField.new(name, field)
|
44
|
+
metadata.fetch(:fields, []).map do |name_hash, field|
|
45
|
+
ResponseField.new(name_hash[:name], field)
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
@@ -89,6 +90,15 @@ module RspecApiDocs
|
|
89
90
|
metadata.fetch(:note, {})
|
90
91
|
end
|
91
92
|
|
93
|
+
# @return [String, nil]
|
94
|
+
def precedence
|
95
|
+
metadata[:example_precedence]
|
96
|
+
end
|
97
|
+
|
98
|
+
def inspect
|
99
|
+
"#<RspecApiDocs::Resource::Example #{name.inspect}>"
|
100
|
+
end
|
101
|
+
|
92
102
|
private
|
93
103
|
|
94
104
|
def request_response_pairs
|
@@ -111,7 +121,15 @@ module RspecApiDocs
|
|
111
121
|
end
|
112
122
|
|
113
123
|
def response_body(body)
|
114
|
-
body.empty?
|
124
|
+
unless body.empty?
|
125
|
+
parsed_body = JSON.parse(body, symbolize_names: true)
|
126
|
+
response_fields.each do |f|
|
127
|
+
unless f.example.nil?
|
128
|
+
DeepHashSet.call(parsed_body, f.scope + [f.name], f.example)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
JSON.dump(parsed_body)
|
132
|
+
end
|
115
133
|
end
|
116
134
|
|
117
135
|
def response_status_text(status)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module RspecApiDocs
|
2
|
+
class Resource
|
3
|
+
class Example
|
4
|
+
class DeepHashSet
|
5
|
+
attr_reader :hash, :keys, :value, :node
|
6
|
+
|
7
|
+
def self.call(*args)
|
8
|
+
new(*args).call
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(hash, keys, value)
|
12
|
+
@hash = hash
|
13
|
+
@keys = keys
|
14
|
+
@value = value
|
15
|
+
@node = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
keys.each_with_index do |key, index|
|
20
|
+
case
|
21
|
+
when key.empty? # TODO: should this require `key == []`?
|
22
|
+
deep_set_value_at_array(index)
|
23
|
+
break
|
24
|
+
when index == keys.size - 1
|
25
|
+
set_value_at(key)
|
26
|
+
else
|
27
|
+
node << key
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
hash
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :node
|
37
|
+
|
38
|
+
def deep_set_value_at_array(index)
|
39
|
+
array = deep_find(hash, node)
|
40
|
+
array && array.each do |inner_hash|
|
41
|
+
DeepHashSet.call(inner_hash, keys[index+1..-1], value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_value_at(key)
|
46
|
+
part = deep_find(hash, node)
|
47
|
+
if part.is_a?(Hash) && !part[key].nil?
|
48
|
+
part[key] = value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def deep_find(hash, keys)
|
53
|
+
keys.inject(hash) { |h, k| h && h[k] }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -10,23 +10,23 @@ module RspecApiDocs
|
|
10
10
|
extend RSpec::Matchers
|
11
11
|
end
|
12
12
|
|
13
|
-
attr_reader :name
|
14
|
-
|
15
13
|
attr_accessor \
|
16
14
|
:verbose,
|
17
15
|
:pattern,
|
18
16
|
:rspec_opts,
|
19
17
|
:existing_file,
|
20
|
-
:dir
|
18
|
+
:dir,
|
19
|
+
:verify
|
21
20
|
|
22
21
|
def initialize(name = nil, &block)
|
23
|
-
@name = name
|
22
|
+
@name = name
|
24
23
|
@verbose = true
|
25
24
|
@pattern = 'spec/requests/**/*_spec.rb'
|
26
25
|
@rspec_opts = []
|
27
26
|
@existing_file = 'docs/index.json'
|
27
|
+
@verify = false
|
28
28
|
|
29
|
-
block.call(self)
|
29
|
+
block.call(self) if block
|
30
30
|
|
31
31
|
define
|
32
32
|
end
|
@@ -34,17 +34,13 @@ module RspecApiDocs
|
|
34
34
|
private
|
35
35
|
|
36
36
|
def define
|
37
|
-
desc
|
37
|
+
desc default_desc
|
38
38
|
task name do
|
39
|
-
@dir = Dir.mktmpdir
|
39
|
+
@dir = Dir.mktmpdir if verify
|
40
40
|
|
41
41
|
rspec_task.run_task(verbose)
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
RSpecMatchers.expect(generated).to RSpecMatchers.eq(existing)
|
46
|
-
|
47
|
-
remove_dir
|
43
|
+
verify! if verify
|
48
44
|
end
|
49
45
|
end
|
50
46
|
|
@@ -59,11 +55,7 @@ module RspecApiDocs
|
|
59
55
|
def rspec_task
|
60
56
|
RSpec::Core::RakeTask.new.tap do |task|
|
61
57
|
task.pattern = pattern
|
62
|
-
task.rspec_opts =
|
63
|
-
'--format RspecApiDocs::Formatter',
|
64
|
-
'--order defined',
|
65
|
-
"--require #{spec_helper.path}",
|
66
|
-
]
|
58
|
+
task.rspec_opts = task_rspec_opts
|
67
59
|
end
|
68
60
|
end
|
69
61
|
|
@@ -87,5 +79,31 @@ module RspecApiDocs
|
|
87
79
|
def remove_dir
|
88
80
|
FileUtils.remove_entry dir
|
89
81
|
end
|
82
|
+
|
83
|
+
def verify!
|
84
|
+
configure_rspec
|
85
|
+
|
86
|
+
RSpecMatchers.expect(generated).to RSpecMatchers.eq(existing)
|
87
|
+
|
88
|
+
remove_dir
|
89
|
+
end
|
90
|
+
|
91
|
+
def task_rspec_opts
|
92
|
+
arr = rspec_opts + [
|
93
|
+
'--format RspecApiDocs::Formatter',
|
94
|
+
'--order defined',
|
95
|
+
]
|
96
|
+
arr += ["--require #{spec_helper.path}"] if verify
|
97
|
+
arr
|
98
|
+
end
|
99
|
+
|
100
|
+
def name
|
101
|
+
@name ||
|
102
|
+
verify ? :'docs:ensure_updated' : :'docs:generate'
|
103
|
+
end
|
104
|
+
|
105
|
+
def default_desc
|
106
|
+
verify ? 'Ensure API docs are up to date' : 'Generate API docs'
|
107
|
+
end
|
90
108
|
end
|
91
109
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-api-docs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Odin Dutton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -149,6 +149,7 @@ files:
|
|
149
149
|
- lib/rspec_api_docs/formatter/renderer/slate_renderer/slate_index.html.md.erb
|
150
150
|
- lib/rspec_api_docs/formatter/resource.rb
|
151
151
|
- lib/rspec_api_docs/formatter/resource/example.rb
|
152
|
+
- lib/rspec_api_docs/formatter/resource/example/deep_hash_set.rb
|
152
153
|
- lib/rspec_api_docs/formatter/resource/example/request_headers.rb
|
153
154
|
- lib/rspec_api_docs/formatter/resource/parameter.rb
|
154
155
|
- lib/rspec_api_docs/formatter/resource/response_field.rb
|