rspec-api-docs 0.8.0 → 0.9.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 +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
|