rspec-api-docs 0.1.0 → 0.2.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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +55 -0
  4. data/README.md +222 -4
  5. data/Rakefile +9 -2
  6. data/bin/generate_integration_docs +14 -0
  7. data/lib/rspec_api_docs.rb +2 -16
  8. data/lib/rspec_api_docs/after.rb +20 -3
  9. data/lib/rspec_api_docs/after/type_checker.rb +56 -0
  10. data/lib/rspec_api_docs/config.rb +27 -0
  11. data/lib/rspec_api_docs/dsl.rb +35 -10
  12. data/lib/rspec_api_docs/dsl/doc_proxy.rb +71 -0
  13. data/lib/rspec_api_docs/dsl/request_store.rb +21 -0
  14. data/lib/rspec_api_docs/formatter.rb +44 -6
  15. data/lib/rspec_api_docs/formatter/renderer/json_renderer.rb +35 -0
  16. data/lib/rspec_api_docs/formatter/renderer/json_renderer/example_serializer.rb +47 -0
  17. data/lib/rspec_api_docs/formatter/renderer/json_renderer/name.rb +18 -0
  18. data/lib/rspec_api_docs/formatter/renderer/json_renderer/resource_serializer.rb +31 -0
  19. data/lib/rspec_api_docs/formatter/renderer/raddocs_renderer.rb +56 -0
  20. data/lib/rspec_api_docs/formatter/renderer/raddocs_renderer/index_serializer.rb +57 -0
  21. data/lib/rspec_api_docs/formatter/renderer/raddocs_renderer/link.rb +11 -0
  22. data/lib/rspec_api_docs/formatter/renderer/raddocs_renderer/resource_serializer.rb +58 -0
  23. data/lib/rspec_api_docs/formatter/renderer/slate_renderer.rb +29 -0
  24. data/lib/rspec_api_docs/formatter/renderer/slate_renderer/slate_index.html.md.erb +45 -0
  25. data/lib/rspec_api_docs/formatter/resource.rb +25 -111
  26. data/lib/rspec_api_docs/formatter/resource/example.rb +125 -0
  27. data/lib/rspec_api_docs/formatter/resource/parameter.rb +39 -0
  28. data/lib/rspec_api_docs/formatter/resource/response_field.rb +39 -0
  29. data/lib/rspec_api_docs/version.rb +1 -1
  30. data/rspec-api-docs.gemspec +1 -0
  31. metadata +34 -5
  32. data/lib/rspec_api_docs/formatter/index_serializer.rb +0 -37
  33. data/lib/rspec_api_docs/formatter/renderers/json_renderer.rb +0 -34
  34. data/lib/rspec_api_docs/formatter/resource_serializer.rb +0 -21
@@ -0,0 +1,57 @@
1
+ require 'rspec_api_docs/formatter/renderer/raddocs_renderer/link'
2
+ require 'rspec_api_docs/formatter/resource'
3
+
4
+ module RspecApiDocs
5
+ module Renderer
6
+ class RaddocsRenderer
7
+ class IndexSerializer
8
+ class ExampleSerializer
9
+ attr_reader :example, :resource_name
10
+
11
+ def initialize(example, resource_name)
12
+ @example = example
13
+ @resource_name = resource_name
14
+ end
15
+
16
+ def to_h
17
+ {
18
+ description: example.name,
19
+ link: link,
20
+ groups: groups,
21
+ route: example.path,
22
+ method: example.http_method.downcase,
23
+ }
24
+ end
25
+
26
+ private
27
+
28
+ def link
29
+ Link.(resource_name, example.name)
30
+ end
31
+
32
+ def groups
33
+ 'all'
34
+ end
35
+ end
36
+
37
+ attr_reader :resources
38
+
39
+ def initialize(resources)
40
+ @resources = resources
41
+ end
42
+
43
+ def to_h
44
+ {
45
+ resources: resources.map do |resource|
46
+ {
47
+ name: resource.name,
48
+ explanation: nil,
49
+ examples: resource.examples.map { |example| ExampleSerializer.new(example, resource.name).to_h },
50
+ }
51
+ end,
52
+ }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,11 @@
1
+ module RspecApiDocs
2
+ module Renderer
3
+ class RaddocsRenderer
4
+ class Link
5
+ def self.call(resource_name, example_name)
6
+ "#{resource_name.parameterize.underscore}/#{example_name.parameterize.underscore}.json"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ module RspecApiDocs
2
+ module Renderer
3
+ class RaddocsRenderer
4
+ class ResourceSerializer
5
+ attr_reader :resource, :example
6
+
7
+ def initialize(resource, example)
8
+ @resource = resource
9
+ @example = example
10
+ end
11
+
12
+ def to_h # rubocop:disable Metrics/AbcSize
13
+ {
14
+ resource: resource.name,
15
+ resource_explanation: nil,
16
+ http_method: example.http_method,
17
+ route: example.path,
18
+ description: example.name,
19
+ explanation: example.description,
20
+ parameters: parameters(example.parameters),
21
+ response_fields: response_fields(example.response_fields),
22
+ requests: requests,
23
+ }
24
+ end
25
+
26
+ private
27
+
28
+ def requests
29
+ example.requests.map { |request| request.merge(curl: nil) }
30
+ end
31
+
32
+ def parameters(parameters)
33
+ parameters.map do |parameter|
34
+ result = {}
35
+ result[:required] = true if parameter.required
36
+ result[:scope] = parameter.scope
37
+ result = result.merge(
38
+ name: parameter.name,
39
+ description: parameter.description,
40
+ )
41
+ result
42
+ end
43
+ end
44
+
45
+ def response_fields(fields)
46
+ fields.map do |field|
47
+ {
48
+ scope: field.scope,
49
+ Type: field.type,
50
+ name: field.name,
51
+ description: field.description,
52
+ }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,29 @@
1
+ module RspecApiDocs
2
+ module Renderer
3
+ class SlateRenderer
4
+ attr_reader :resources
5
+
6
+ def initialize(resources)
7
+ @resources = resources
8
+ end
9
+
10
+ def render
11
+ FileUtils.mkdir_p output_file.dirname
12
+
13
+ File.open(output_file, 'w') do |f|
14
+ f.write ERB.new(File.read(template), nil, '-').result(binding)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def output_file
21
+ Pathname.new(RspecApiDocs.configuration.output_dir) + 'index.html.md'
22
+ end
23
+
24
+ def template
25
+ File.expand_path('../slate_renderer/slate_index.html.md.erb', __FILE__)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,45 @@
1
+ ---
2
+ title: API Reference
3
+ search: true
4
+ ---
5
+
6
+ <% resources.each do |resource| %>
7
+ # <%= resource.name %>
8
+
9
+ <% resource.examples.each do |example| %>
10
+ ## <%= example.name %>
11
+
12
+ <%= example.description %>
13
+
14
+ <% example.requests.each do |request| -%>
15
+ ```json
16
+ <% response_body = JSON.parse(request[:response_body]) if request[:response_body] %>
17
+ <%= JSON.pretty_generate(response_body) if response_body %>
18
+ ```
19
+
20
+ ### HTTP Request
21
+
22
+ `<%= request[:request_method] %> http://example.com<%= request[:request_path] %>`
23
+
24
+ <% end -%>
25
+ <% unless example.parameters.empty? -%>
26
+ ### Query Parameters
27
+
28
+ Parameter | Required | Description
29
+ --------- | ------- | -----------
30
+ <% example.parameters.each do |parameter| -%>
31
+ <%= parameter.name %> | <%= !!parameter.required %> | <%= parameter.description %>
32
+ <% end -%>
33
+ <% end -%>
34
+
35
+ <% unless example.response_fields.empty? -%>
36
+ ### Response Fields
37
+
38
+ Parameter | Type | Description
39
+ --------- | ------- | -----------
40
+ <% example.response_fields.each do |parameter| -%>
41
+ <%= parameter.name %> | <%= parameter.type %> | <%= parameter.description %>
42
+ <% end -%>
43
+ <% end -%>
44
+ <% end -%>
45
+ <% end -%>
@@ -1,130 +1,44 @@
1
1
  require 'active_support/inflector'
2
+ require 'rspec_api_docs/formatter/resource/example'
3
+ require 'rspec_api_docs/formatter/resource/parameter'
4
+ require 'rspec_api_docs/formatter/resource/response_field'
2
5
 
3
6
  module RspecApiDocs
4
7
  class Resource
5
- attr_reader :example
8
+ attr_reader :rspec_example
6
9
 
7
- def initialize(example)
8
- @example = example
9
- end
10
+ # Returns an array of {Example}s
11
+ #
12
+ # @return [Array<Example>]
13
+ attr_accessor :examples
10
14
 
11
- def name
12
- metadata.fetch(:resource_name, example.metadata[:example_group][:description])
15
+ def initialize(rspec_example)
16
+ @rspec_example = rspec_example
17
+ @examples = []
13
18
  end
14
19
 
15
- def example_name
16
- metadata.fetch(:example_name, example.description)
20
+ # The name of the resource.
21
+ #
22
+ # E.g. "Characters"
23
+ #
24
+ # @return [String]
25
+ def name
26
+ metadata.fetch(:resource_name, rspec_example.metadata[:example_group][:description])
17
27
  end
18
28
 
29
+ # The description of the resource.
30
+ #
31
+ # E.g. "Orders can be created, viewed, and deleted"
32
+ #
33
+ # @return [String]
19
34
  def description
20
- metadata[:description]
21
- end
22
-
23
- def parameters
24
- metadata.fetch(:parameters, []).map do |name, field|
25
- result = {}
26
- result[:required] = true if field[:required]
27
- result[:scope] = field[:scope].join unless field[:scope].empty?
28
- result = result.merge(
29
- name: name,
30
- description: field[:description],
31
- )
32
- result
33
- end
34
- end
35
-
36
- def response_fields
37
- metadata.fetch(:fields, []).map do |name, field|
38
- {
39
- scope: field[:scope].join,
40
- Type: field[:type],
41
- name: name,
42
- description: field[:description],
43
- }
44
- end
45
- end
46
-
47
- def requests
48
- reqs = metadata.fetch(:requests, []).reject { |x| x.any?(&:nil?) }
49
- reqs.map do |request, response|
50
- {
51
- request_method: request.request_method,
52
- request_path: request_path(request),
53
- request_body: request_body(request.body),
54
- request_headers: request_headers(request.env),
55
- request_query_parameters: request.params,
56
- request_content_type: request.content_type,
57
- response_status: response.status,
58
- response_status_text: response_status_text(response.status),
59
- response_body: response_body(response.body),
60
- response_headers: response.headers,
61
- response_content_type: response.content_type,
62
- curl: curl,
63
- }
64
- end
65
- end
66
-
67
- # NOTE: returns the first route requested
68
- def path
69
- metadata.fetch(:path) do
70
- reqs = metadata.fetch(:requests, []).reject { |x| x.any?(&:nil?) }
71
- return if reqs.empty?
72
- reqs.first.first.path
73
- end
74
- end
75
-
76
- # NOTE: returns the first HTTP method used
77
- def http_method
78
- reqs = metadata.fetch(:requests, []).reject { |x| x.any?(&:nil?) }
79
- return if reqs.empty?
80
- reqs.first.first.request_method
81
- end
82
-
83
- def link
84
- "#{name.downcase}/#{example_name.parameterize.underscore}.json"
85
- end
86
-
87
- def groups
88
- 'all'
35
+ metadata[:resource_description]
89
36
  end
90
37
 
91
38
  private
92
39
 
93
- # http://stackoverflow.com/a/33235714/826820
94
- def request_headers(env)
95
- Hash[
96
- *env.select { |k,v| k.start_with? 'HTTP_' }
97
- .collect { |k,v| [k.sub(/^HTTP_/, ''), v] }
98
- .collect { |k,v| [k.split('_').collect(&:capitalize).join('-'), v] }
99
- .sort.flatten
100
- ]
101
- end
102
-
103
- def request_path(request)
104
- URI(request.path).tap do |uri|
105
- uri.query = request.query_string unless request.query_string.empty?
106
- end.to_s
107
- end
108
-
109
- def request_body(body)
110
- body = body.read
111
- body.empty? ? nil : body
112
- end
113
-
114
- def response_body(body)
115
- body.empty? ? nil : body
116
- end
117
-
118
- def response_status_text(status)
119
- Rack::Utils::HTTP_STATUS_CODES[status]
120
- end
121
-
122
- # TODO
123
- def curl
124
- end
125
-
126
40
  def metadata
127
- example.metadata[METADATA_NAMESPACE]
41
+ rspec_example.metadata[METADATA_NAMESPACE]
128
42
  end
129
43
  end
130
44
  end
@@ -0,0 +1,125 @@
1
+ module RspecApiDocs
2
+ class Resource
3
+ class Example
4
+ attr_reader :example
5
+
6
+ def initialize(example)
7
+ @example = example
8
+ end
9
+
10
+ # The name of the example.
11
+ #
12
+ # E.g. "Returns a Character"
13
+ #
14
+ # @return [String]
15
+ def name
16
+ metadata.fetch(:example_name, example.description)
17
+ end
18
+
19
+ # The description of the example.
20
+ #
21
+ # E.g. "For getting information about a Character."
22
+ #
23
+ # @return [String]
24
+ def description
25
+ metadata[:description]
26
+ end
27
+
28
+ # Parameters for the example.
29
+ #
30
+ # @return [Array<Parameter>]
31
+ def parameters
32
+ metadata.fetch(:parameters, []).map do |name, parameter|
33
+ Parameter.new(name, parameter)
34
+ end
35
+ end
36
+
37
+ # Response fields for the example.
38
+ #
39
+ # @return [Array<ResponseField>]
40
+ def response_fields
41
+ metadata.fetch(:fields, []).map do |name, field|
42
+ ResponseField.new(name, field)
43
+ end
44
+ end
45
+
46
+ # Requests stored for the example.
47
+ #
48
+ # @return [Array<Hash>]
49
+ def requests # rubocop:disable Metrics/AbcSize
50
+ request_response_pairs.map do |request, response|
51
+ {
52
+ request_method: request.request_method,
53
+ request_path: request_path(request),
54
+ request_body: request_body(request.body),
55
+ request_headers: request_headers(request.env),
56
+ request_query_parameters: request.params,
57
+ request_content_type: request.content_type,
58
+ response_status: response.status,
59
+ response_status_text: response_status_text(response.status),
60
+ response_body: response_body(response.body),
61
+ response_headers: response.headers,
62
+ response_content_type: response.content_type,
63
+ }
64
+ end
65
+ end
66
+
67
+ # Path stored on the example OR the path of first route requested.
68
+ #
69
+ # @return [String, nil]
70
+ def path
71
+ metadata.fetch(:path) do
72
+ return if request_response_pairs.empty?
73
+ request_response_pairs.first.first.path
74
+ end
75
+ end
76
+
77
+ # The HTTP method of first route requested.
78
+ #
79
+ # @return [String, nil]
80
+ def http_method
81
+ return if request_response_pairs.empty?
82
+ request_response_pairs.first.first.request_method
83
+ end
84
+
85
+ private
86
+
87
+ def request_response_pairs
88
+ metadata.fetch(:requests, []).reject { |pair| pair.any?(&:nil?) }
89
+ end
90
+
91
+ # http://stackoverflow.com/a/33235714/826820
92
+ def request_headers(env)
93
+ Hash[
94
+ *env.select { |k,v| k.start_with? 'HTTP_' }
95
+ .collect { |k,v| [k.sub(/^HTTP_/, ''), v] }
96
+ .collect { |k,v| [k.split('_').collect(&:capitalize).join('-'), v] }
97
+ .sort.flatten
98
+ ]
99
+ end
100
+
101
+ def request_path(request)
102
+ URI(request.path).tap do |uri|
103
+ uri.query = request.query_string unless request.query_string.empty?
104
+ end.to_s
105
+ end
106
+
107
+ def request_body(body)
108
+ body = body.read
109
+ body.empty? ? nil : body
110
+ end
111
+
112
+ def response_body(body)
113
+ body.empty? ? nil : body
114
+ end
115
+
116
+ def response_status_text(status)
117
+ Rack::Utils::HTTP_STATUS_CODES[status]
118
+ end
119
+
120
+ def metadata
121
+ example.metadata[METADATA_NAMESPACE]
122
+ end
123
+ end
124
+ end
125
+ end