rspec_api_documentation 4.9.0 → 5.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 16b47e6bedc1c86cc5e418233eca8b80d16b5628
4
- data.tar.gz: e1da890616a2fa4cf6a8ff8c44eff5977abc32f6
3
+ metadata.gz: eab76d5c83c6d908b0d547852bf776001916a096
4
+ data.tar.gz: e104765306842544aae1e13e425df1048898490e
5
5
  SHA512:
6
- metadata.gz: c16d4338f5d6e06f1c913a951c1370b31f2e6c85a6aef38a593d5fbd332226ad99c9fae76207e9bed5089500010a5b588a123e0354cdef25d057cbc0483a1933
7
- data.tar.gz: 03ef3fbf5306d2951fa6f3b82f08e2679306069e791ef9bf5d30f58693dbb83528bb78009821cc4b26fb1da166c33620c1e5d327c9d91f8008bd45afa93eead4
6
+ metadata.gz: 950451c2559d9a185e3afd901ce7253c8c11d47dc2dff603e210b7d38fcb591cf0f15382113919f4c6bda2b4d90488a7c9c9fd7170ce39047c2091436694259c
7
+ data.tar.gz: 7f319b73f94430152e6a6d161fb63f61abfb7d2bffc73109298a7caea084325ee9deba64425880b1991a0c9d04ac025a8542448e252ff7a867947f0b68def133
@@ -44,6 +44,7 @@ module RspecApiDocumentation
44
44
  autoload :CombinedTextWriter
45
45
  autoload :CombinedJsonWriter
46
46
  autoload :SlateWriter
47
+ autoload :ApiBlueprintWriter
47
48
  end
48
49
 
49
50
  module Views
@@ -59,6 +60,8 @@ module RspecApiDocumentation
59
60
  autoload :MarkdownExample
60
61
  autoload :SlateIndex
61
62
  autoload :SlateExample
63
+ autoload :ApiBlueprintIndex
64
+ autoload :ApiBlueprintExample
62
65
  end
63
66
 
64
67
  def self.configuration
@@ -96,14 +96,14 @@ module RspecApiDocumentation
96
96
  end
97
97
 
98
98
  def clean_out_uploaded_data(params, request_body)
99
- params.each do |_, value|
100
- if value.is_a?(Hash)
101
- if value.has_key?(:tempfile)
102
- data = value[:tempfile].read
103
- request_body = request_body.gsub(data, "[uploaded data]")
104
- else
105
- request_body = clean_out_uploaded_data(value,request_body)
106
- end
99
+ params.each do |value|
100
+ if [Hash, Array].member? value.class
101
+ request_body = if value.respond_to?(:has_key?) && value.has_key?(:tempfile)
102
+ data = value[:tempfile].read
103
+ request_body.gsub(data, "[uploaded data]")
104
+ else
105
+ clean_out_uploaded_data(value, request_body)
106
+ end
107
107
  end
108
108
  end
109
109
  request_body
@@ -16,7 +16,7 @@ module RspecApiDocumentation
16
16
  end
17
17
 
18
18
  def get
19
- "curl \"#{url}#{get_data}\" -X GET #{headers}"
19
+ "curl -g \"#{url}#{get_data}\" -X GET #{headers}"
20
20
  end
21
21
 
22
22
  def head
@@ -9,6 +9,8 @@ module RspecApiDocumentation::DSL
9
9
  extend ActiveSupport::Concern
10
10
  include Rack::Test::Utils
11
11
 
12
+ URL_PARAMS_REGEX = /[:\{](\w+)\}?/.freeze
13
+
12
14
  delegate :response_headers, :response_status, :response_body, :to => :rspec_api_documentation_client
13
15
 
14
16
  module ClassMethods
@@ -96,8 +98,16 @@ module RspecApiDocumentation::DSL
96
98
  rspec_api_documentation_client.status
97
99
  end
98
100
 
101
+ def in_path?(param)
102
+ path_params.include?(param)
103
+ end
104
+
105
+ def path_params
106
+ example.metadata[:route].scan(URL_PARAMS_REGEX).flatten
107
+ end
108
+
99
109
  def path
100
- example.metadata[:route].gsub(/:(\w+)/) do |match|
110
+ example.metadata[:route].gsub(URL_PARAMS_REGEX) do |match|
101
111
  if extra_params.keys.include?($1)
102
112
  delete_extra_param($1)
103
113
  elsif respond_to?($1)
@@ -8,7 +8,12 @@ module RspecApiDocumentation::DSL
8
8
  define_method method do |*args, &block|
9
9
  options = args.extract_options!
10
10
  options[:method] = method
11
- options[:route] = args.first
11
+ if metadata[:route_uri]
12
+ options[:route] = metadata[:route_uri]
13
+ options[:action_name] = args.first
14
+ else
15
+ options[:route] = args.first
16
+ end
12
17
  options[:api_doc_dsl] = :endpoint
13
18
  args.push(options)
14
19
  args[0] = "#{method.to_s.upcase} #{args[0]}"
@@ -38,10 +43,25 @@ module RspecApiDocumentation::DSL
38
43
  context(*args, &block)
39
44
  end
40
45
 
46
+ def route(*args, &block)
47
+ raise "You must define the route URI" if args[0].blank?
48
+ raise "You must define the route name" if args[1].blank?
49
+ options = args.extract_options!
50
+ options[:route_uri] = args[0].gsub(/\{.*\}/, "")
51
+ options[:route_optionals] = (optionals = args[0].match(/(\{.*\})/) and optionals[-1])
52
+ options[:route_name] = args[1]
53
+ args.push(options)
54
+ context(*args, &block)
55
+ end
56
+
41
57
  def parameter(name, *args)
42
58
  parameters.push(field_specification(name, *args))
43
59
  end
44
60
 
61
+ def attribute(name, *args)
62
+ attributes.push(field_specification(name, *args))
63
+ end
64
+
45
65
  def response_field(name, *args)
46
66
  response_fields.push(field_specification(name, *args))
47
67
  end
@@ -75,6 +95,10 @@ module RspecApiDocumentation::DSL
75
95
  safe_metadata(:parameters, [])
76
96
  end
77
97
 
98
+ def attributes
99
+ safe_metadata(:attributes, [])
100
+ end
101
+
78
102
  def response_fields
79
103
  safe_metadata(:response_fields, [])
80
104
  end
@@ -38,6 +38,10 @@ module RspecApiDocumentation
38
38
  respond_to?(:parameters) && parameters.present?
39
39
  end
40
40
 
41
+ def has_attributes?
42
+ respond_to?(:attributes) && attributes.present?
43
+ end
44
+
41
45
  def has_response_fields?
42
46
  respond_to?(:response_fields) && response_fields.present?
43
47
  end
@@ -1,7 +1,7 @@
1
1
  module RspecApiDocumentation
2
2
  class Railtie < Rails::Railtie
3
3
  rake_tasks do
4
- load "tasks/docs.rake"
4
+ load File.join(File.dirname(__FILE__), '../tasks/docs.rake')
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,104 @@
1
+ module RspecApiDocumentation
2
+ module Views
3
+ class ApiBlueprintExample < MarkupExample
4
+ TOTAL_SPACES_INDENTATION = 8.freeze
5
+
6
+ def initialize(example, configuration)
7
+ super
8
+ self.template_name = "rspec_api_documentation/api_blueprint_example"
9
+ end
10
+
11
+ def parameters
12
+ super.map do |parameter|
13
+ parameter.merge({
14
+ :required => !!parameter[:required],
15
+ :has_example => !!parameter[:example],
16
+ :has_type => !!parameter[:type]
17
+ })
18
+ end
19
+ end
20
+
21
+ def requests
22
+ super.map do |request|
23
+ request[:request_headers_text] = remove_utf8_for_json(request[:request_headers_text])
24
+ request[:request_headers_text] = indent(request[:request_headers_text])
25
+ request[:request_content_type] = content_type(request[:request_headers])
26
+ request[:request_content_type] = remove_utf8_for_json(request[:request_content_type])
27
+ request[:request_body] = body_to_json(request, :request)
28
+ request[:request_body] = indent(request[:request_body])
29
+
30
+ request[:response_headers_text] = remove_utf8_for_json(request[:response_headers_text])
31
+ request[:response_headers_text] = indent(request[:response_headers_text])
32
+ request[:response_content_type] = content_type(request[:response_headers])
33
+ request[:response_content_type] = remove_utf8_for_json(request[:response_content_type])
34
+ request[:response_body] = body_to_json(request, :response)
35
+ request[:response_body] = indent(request[:response_body])
36
+
37
+ request[:has_request?] = has_request?(request)
38
+ request[:has_response?] = has_response?(request)
39
+ request
40
+ end
41
+ end
42
+
43
+ def extension
44
+ Writers::ApiBlueprintWriter::EXTENSION
45
+ end
46
+
47
+ private
48
+
49
+ def has_request?(metadata)
50
+ metadata.any? do |key, value|
51
+ [:request_body, :request_headers, :request_content_type].include?(key) && value
52
+ end
53
+ end
54
+
55
+ def has_response?(metadata)
56
+ metadata.any? do |key, value|
57
+ [:response_status, :response_body, :response_headers, :response_content_type].include?(key) && value
58
+ end
59
+ end
60
+
61
+ def indent(string)
62
+ string.tap do |str|
63
+ str.gsub!(/\n/, "\n" + (" " * TOTAL_SPACES_INDENTATION)) if str
64
+ end
65
+ end
66
+
67
+ # http_call: the hash that contains all information about the HTTP
68
+ # request and response.
69
+ # message_direction: either `request` or `response`.
70
+ def body_to_json(http_call, message_direction)
71
+ content_type = http_call["#{message_direction}_content_type".to_sym]
72
+ body = http_call["#{message_direction}_body".to_sym] # e.g request_body
73
+
74
+ if json?(content_type) && body
75
+ body = JSON.pretty_generate(JSON.parse(body))
76
+ end
77
+
78
+ body
79
+ end
80
+
81
+ # JSON requests should use UTF-8 by default according to
82
+ # http://www.ietf.org/rfc/rfc4627.txt, so we will remove `charset=utf-8`
83
+ # when we find it to remove noise.
84
+ def remove_utf8_for_json(headers)
85
+ return unless headers
86
+ headers
87
+ .split("\n")
88
+ .map { |header|
89
+ header.gsub!(/; *charset=utf-8/, "") if json?(header)
90
+ header
91
+ }
92
+ .join("\n")
93
+ end
94
+
95
+ def content_type(headers)
96
+ headers && headers.fetch("Content-Type", nil)
97
+ end
98
+
99
+ def json?(string)
100
+ string =~ /application\/.*json/
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,90 @@
1
+ module RspecApiDocumentation
2
+ module Views
3
+ class ApiBlueprintIndex < MarkupIndex
4
+ def initialize(index, configuration)
5
+ super
6
+ self.template_name = "rspec_api_documentation/api_blueprint_index"
7
+ end
8
+
9
+ def sections
10
+ super.map do |section|
11
+ routes = section[:examples].group_by { |e| "#{e.route_uri}#{e.route_optionals}" }.map do |route, examples|
12
+ attrs = fields(:attributes, examples)
13
+ params = fields(:parameters, examples)
14
+
15
+ methods = examples.group_by(&:http_method).map do |http_method, examples|
16
+ {
17
+ http_method: http_method,
18
+ description: examples.first.respond_to?(:action_name) && examples.first.action_name,
19
+ examples: examples
20
+ }
21
+ end
22
+
23
+ {
24
+ "has_attributes?".to_sym => attrs.size > 0,
25
+ "has_parameters?".to_sym => params.size > 0,
26
+ route: route,
27
+ route_name: examples[0][:route_name],
28
+ attributes: attrs,
29
+ parameters: params,
30
+ http_methods: methods
31
+ }
32
+ end
33
+
34
+ section.merge({
35
+ routes: routes
36
+ })
37
+ end
38
+ end
39
+
40
+ def examples
41
+ @index.examples.map do |example|
42
+ ApiBlueprintExample.new(example, @configuration)
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ # APIB has both `parameters` and `attributes`. This generates a hash
49
+ # with all of its properties, like name, description, required.
50
+ # {
51
+ # required: true,
52
+ # example: "1",
53
+ # type: "string",
54
+ # name: "id",
55
+ # description: "The id",
56
+ # properties_description: "required, string"
57
+ # }
58
+ def fields(property_name, examples)
59
+ examples
60
+ .map { |example| example.metadata[property_name] }
61
+ .flatten
62
+ .compact
63
+ .uniq { |property| property[:name] }
64
+ .map do |property|
65
+ properties = []
66
+ properties << "required" if property[:required]
67
+ properties << property[:type] if property[:type]
68
+ if properties.count > 0
69
+ property[:properties_description] = properties.join(", ")
70
+ else
71
+ property[:properties_description] = nil
72
+ end
73
+
74
+ property[:description] = nil if description_blank?(property)
75
+ property
76
+ end
77
+ end
78
+
79
+ # When no `description` was specified for a parameter, the DSL class
80
+ # is making `description = "#{scope} #{name}"`, which is bad because it
81
+ # assumes that all formats want this behavior. To avoid changing there
82
+ # and breaking everything, I do my own check here and if description
83
+ # equals the name, I assume it is blank.
84
+ def description_blank?(property)
85
+ !property[:description] ||
86
+ property[:description].to_s.strip == property[:name].to_s.strip
87
+ end
88
+ end
89
+ end
90
+ end
@@ -83,6 +83,10 @@ module RspecApiDocumentation
83
83
  end
84
84
  end.join
85
85
  end
86
+
87
+ def content_type(headers)
88
+ headers && headers.fetch("Content-Type", nil)
89
+ end
86
90
  end
87
91
  end
88
92
  end
@@ -0,0 +1,29 @@
1
+ module RspecApiDocumentation
2
+ module Writers
3
+ class ApiBlueprintWriter < GeneralMarkupWriter
4
+ EXTENSION = 'apib'
5
+
6
+ def markup_index_class
7
+ RspecApiDocumentation::Views::ApiBlueprintIndex
8
+ end
9
+
10
+ def markup_example_class
11
+ RspecApiDocumentation::Views::ApiBlueprintExample
12
+ end
13
+
14
+ def extension
15
+ EXTENSION
16
+ end
17
+
18
+ private
19
+
20
+ # API Blueprint is a spec, not navigable like HTML, therefore we generate
21
+ # only one file with all resources.
22
+ def render_options
23
+ super.merge({
24
+ examples: false
25
+ })
26
+ end
27
+ end
28
+ end
29
+ end
@@ -6,16 +6,20 @@ module RspecApiDocumentation
6
6
 
7
7
  # Write out the generated documentation
8
8
  def write
9
- File.open(configuration.docs_dir.join(index_file_name + '.' + extension), "w+") do |f|
10
- f.write markup_index_class.new(index, configuration).render
9
+ if render_options.fetch(:index, true)
10
+ File.open(configuration.docs_dir.join(index_file_name + '.' + extension), "w+") do |f|
11
+ f.write markup_index_class.new(index, configuration).render
12
+ end
11
13
  end
12
14
 
13
- index.examples.each do |example|
14
- markup_example = markup_example_class.new(example, configuration)
15
- FileUtils.mkdir_p(configuration.docs_dir.join(markup_example.dirname))
15
+ if render_options.fetch(:examples, true)
16
+ index.examples.each do |example|
17
+ markup_example = markup_example_class.new(example, configuration)
18
+ FileUtils.mkdir_p(configuration.docs_dir.join(markup_example.dirname))
16
19
 
17
- File.open(configuration.docs_dir.join(markup_example.dirname, markup_example.filename), "w+") do |f|
18
- f.write markup_example.render
20
+ File.open(configuration.docs_dir.join(markup_example.dirname, markup_example.filename), "w+") do |f|
21
+ f.write markup_example.render
22
+ end
19
23
  end
20
24
  end
21
25
  end
@@ -27,6 +31,15 @@ module RspecApiDocumentation
27
31
  def extension
28
32
  raise 'Parent class. This method should not be called.'
29
33
  end
34
+
35
+ private
36
+
37
+ def render_options
38
+ {
39
+ index: true,
40
+ examples: true
41
+ }
42
+ end
30
43
  end
31
44
  end
32
45
  end
@@ -0,0 +1,79 @@
1
+ FORMAT: A1
2
+ {{# sections }}
3
+
4
+ # Group {{ resource_name }}
5
+ {{# resource_explanation }}
6
+
7
+ {{{ resource_explanation }}}
8
+ {{/ resource_explanation }}
9
+ {{# description }}
10
+
11
+ {{ description }}
12
+ {{/ description }}
13
+ {{# routes }}
14
+
15
+ ## {{ route_name }} [{{ route }}]
16
+ {{# description }}
17
+
18
+ description: {{ description }}
19
+ {{/ description }}
20
+ {{# explanation }}
21
+
22
+ explanation: {{ explanation }}
23
+ {{/ explanation }}
24
+ {{# has_parameters? }}
25
+
26
+ + Parameters
27
+ {{# parameters }}
28
+ + {{ name }}{{# example }}: {{ example }}{{/ example }}{{# properties_description }} ({{ properties_description }}){{/ properties_description }}{{# description }} - {{ description }}{{/ description }}
29
+ {{/ parameters }}
30
+ {{/ has_parameters? }}
31
+ {{# has_attributes? }}
32
+
33
+ + Attributes (object)
34
+ {{# attributes }}
35
+ + {{ name }}{{# example }}: {{ example }}{{/ example }}{{# properties_description }} ({{ properties_description }}){{/ properties_description }}{{# description }} - {{ description }}{{/ description }}
36
+ {{/ attributes }}
37
+ {{/ has_attributes? }}
38
+ {{# http_methods }}
39
+
40
+ ### {{ description }} [{{ http_method }}]
41
+ {{# examples }}
42
+ {{# requests }}
43
+ {{# has_request? }}
44
+
45
+ + Request {{ description }}{{# request_content_type }} ({{ request_content_type }}){{/ request_content_type }}
46
+ {{/ has_request? }}
47
+ {{# request_headers_text }}
48
+
49
+ + Headers
50
+
51
+ {{{ request_headers_text }}}
52
+ {{/ request_headers_text }}
53
+ {{# request_body }}
54
+
55
+ + Body
56
+
57
+ {{{ request_body }}}
58
+ {{/ request_body }}
59
+ {{# has_response? }}
60
+
61
+ + Response {{ response_status }} ({{ response_content_type }})
62
+ {{/ has_response? }}
63
+ {{# response_headers_text }}
64
+
65
+ + Headers
66
+
67
+ {{{ response_headers_text }}}
68
+ {{/ response_headers_text }}
69
+ {{# response_body }}
70
+
71
+ + Body
72
+
73
+ {{{ response_body }}}
74
+ {{/ response_body }}
75
+ {{/ requests }}
76
+ {{/ examples }}
77
+ {{/ http_methods }}
78
+ {{/ routes }}
79
+ {{/ sections }}
@@ -25,10 +25,11 @@
25
25
  {{# has_response_fields? }}
26
26
 
27
27
  ### Response Fields
28
- {{# response_fields }}
29
28
 
30
- Name : {{ name }}
31
- Description : {{ description }}
29
+ | Name | Description | Scope |
30
+ |------|-------------|-------|
31
+ {{# response_fields }}
32
+ | {{ name }} | {{ description }} | {{ scope }} |
32
33
  {{/ response_fields }}
33
34
 
34
35
  {{/ has_response_fields? }}
@@ -9,7 +9,7 @@
9
9
  #### Endpoint
10
10
 
11
11
  {{# requests}}
12
- ```
12
+ ```plaintext
13
13
  {{ request_method }} {{ request_path }}
14
14
  {{ request_headers_text }}
15
15
  ```
@@ -50,7 +50,7 @@ None known.
50
50
 
51
51
  ### Response
52
52
 
53
- ```
53
+ ```plaintext
54
54
  {{ response_headers_text }}
55
55
  {{ response_status }} {{ response_status_text}}
56
56
  ```
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec_api_documentation
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.9.0
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Cahoon
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-12-08 00:00:00.000000000 Z
13
+ date: 2017-06-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -282,6 +282,8 @@ files:
282
282
  - lib/rspec_api_documentation/rack_test_client.rb
283
283
  - lib/rspec_api_documentation/railtie.rb
284
284
  - lib/rspec_api_documentation/test_server.rb
285
+ - lib/rspec_api_documentation/views/api_blueprint_example.rb
286
+ - lib/rspec_api_documentation/views/api_blueprint_index.rb
285
287
  - lib/rspec_api_documentation/views/html_example.rb
286
288
  - lib/rspec_api_documentation/views/html_index.rb
287
289
  - lib/rspec_api_documentation/views/markdown_example.rb
@@ -292,6 +294,7 @@ files:
292
294
  - lib/rspec_api_documentation/views/slate_index.rb
293
295
  - lib/rspec_api_documentation/views/textile_example.rb
294
296
  - lib/rspec_api_documentation/views/textile_index.rb
297
+ - lib/rspec_api_documentation/writers/api_blueprint_writer.rb
295
298
  - lib/rspec_api_documentation/writers/append_json_writer.rb
296
299
  - lib/rspec_api_documentation/writers/combined_json_writer.rb
297
300
  - lib/rspec_api_documentation/writers/combined_text_writer.rb
@@ -306,6 +309,7 @@ files:
306
309
  - lib/rspec_api_documentation/writers/textile_writer.rb
307
310
  - lib/rspec_api_documentation/writers/writer.rb
308
311
  - lib/tasks/docs.rake
312
+ - templates/rspec_api_documentation/api_blueprint_index.mustache
309
313
  - templates/rspec_api_documentation/html_example.mustache
310
314
  - templates/rspec_api_documentation/html_index.mustache
311
315
  - templates/rspec_api_documentation/markdown_example.mustache