dox 1.0.1 → 2.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +6 -3
- data/.ruby-version +1 -1
- data/.travis.yml +7 -11
- data/CHANGES.md +40 -0
- data/Gemfile +1 -1
- data/README.md +96 -104
- data/Rakefile +3 -3
- data/bin/console +3 -3
- data/dox.gemspec +15 -12
- data/lib/dox.rb +14 -1
- data/lib/dox/config.rb +38 -5
- data/lib/dox/dsl/action.rb +10 -4
- data/lib/dox/dsl/documentation.rb +4 -0
- data/lib/dox/dsl/syntax.rb +1 -0
- data/lib/dox/entities/action.rb +33 -12
- data/lib/dox/entities/example.rb +34 -10
- data/lib/dox/entities/resource.rb +4 -5
- data/lib/dox/entities/resource_group.rb +2 -3
- data/lib/dox/formatter.rb +22 -12
- data/lib/dox/formatters/base.rb +19 -0
- data/lib/dox/formatters/json.rb +14 -0
- data/lib/dox/formatters/multipart.rb +21 -0
- data/lib/dox/formatters/plain.rb +9 -0
- data/lib/dox/formatters/xml.rb +14 -0
- data/lib/dox/printers/action_printer.rb +18 -27
- data/lib/dox/printers/base_printer.rb +36 -17
- data/lib/dox/printers/document_printer.rb +27 -7
- data/lib/dox/printers/example_request_printer.rb +69 -0
- data/lib/dox/printers/example_response_printer.rb +86 -0
- data/lib/dox/printers/resource_group_printer.rb +7 -7
- data/lib/dox/printers/resource_printer.rb +12 -7
- data/lib/dox/util/http.rb +64 -0
- data/lib/dox/version.rb +1 -1
- metadata +96 -35
- data/lib/dox/printers/example_printer.rb +0 -110
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'dox'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +10,5 @@ require "dox"
|
|
10
10
|
# require "pry"
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start
|
data/dox.gemspec
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'dox/version'
|
5
4
|
|
@@ -9,29 +8,33 @@ Gem::Specification.new do |spec|
|
|
9
8
|
spec.authors = ['Melita Kokot', 'Vedran Hrnčić']
|
10
9
|
spec.email = ['melita.kokot@gmail.com', 'vrabac266@gmail.com']
|
11
10
|
|
12
|
-
spec.summary = 'Generates API documentation for rspec in
|
11
|
+
spec.summary = 'Generates API documentation for rspec in OpenAPI format.'
|
13
12
|
spec.homepage = 'https://github.com/infinum/dox'
|
14
13
|
spec.license = 'MIT'
|
15
14
|
|
15
|
+
spec.required_ruby_version = '>= 2.0.0'
|
16
|
+
|
16
17
|
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
17
18
|
# delete this section to allow pushing this gem to any host.
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
|
22
|
-
end
|
19
|
+
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.' unless spec.respond_to?(:metadata)
|
20
|
+
|
21
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
23
22
|
|
24
23
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
24
|
spec.bindir = 'exe'
|
26
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
26
|
spec.require_paths = ['lib']
|
28
27
|
|
29
|
-
spec.add_development_dependency 'bundler'
|
28
|
+
spec.add_development_dependency 'bundler'
|
29
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
30
|
+
spec.add_development_dependency 'json'
|
31
|
+
spec.add_development_dependency 'pry-nav'
|
32
|
+
spec.add_development_dependency 'pry-rails'
|
33
|
+
spec.add_development_dependency 'pry-stack_explorer'
|
34
|
+
spec.add_runtime_dependency 'rails', '>= 4.0'
|
30
35
|
spec.add_development_dependency 'rake', '~> 10.0'
|
31
36
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
32
|
-
spec.add_development_dependency '
|
37
|
+
spec.add_development_dependency 'rubocop'
|
33
38
|
spec.add_development_dependency 'simplecov'
|
34
|
-
spec.add_development_dependency 'codeclimate-test-reporter'
|
35
39
|
spec.add_runtime_dependency 'rspec-core'
|
36
|
-
spec.add_runtime_dependency 'rails', '>= 4.0'
|
37
40
|
end
|
data/lib/dox.rb
CHANGED
@@ -19,10 +19,16 @@ require 'dox/errors/invalid_action_error'
|
|
19
19
|
require 'dox/errors/invalid_resource_error'
|
20
20
|
require 'dox/errors/invalid_resource_group_error'
|
21
21
|
require 'dox/formatter'
|
22
|
+
require 'dox/formatters/base'
|
23
|
+
require 'dox/formatters/json'
|
24
|
+
require 'dox/formatters/multipart'
|
25
|
+
require 'dox/formatters/plain'
|
26
|
+
require 'dox/formatters/xml'
|
22
27
|
require 'dox/printers/base_printer'
|
23
28
|
require 'dox/printers/action_printer'
|
24
29
|
require 'dox/printers/document_printer'
|
25
|
-
require 'dox/printers/
|
30
|
+
require 'dox/printers/example_request_printer'
|
31
|
+
require 'dox/printers/example_response_printer'
|
26
32
|
require 'dox/printers/resource_group_printer'
|
27
33
|
require 'dox/printers/resource_printer'
|
28
34
|
require 'dox/util/http'
|
@@ -43,4 +49,11 @@ module Dox
|
|
43
49
|
def self.full_headers_whitelist
|
44
50
|
(config.headers_whitelist.to_a + DEFAULT_HEADERS_WHITELIST).uniq
|
45
51
|
end
|
52
|
+
|
53
|
+
RSpec.configure do |config|
|
54
|
+
config.after(:each, :dox) do |example|
|
55
|
+
example.metadata[:request] = request
|
56
|
+
example.metadata[:response] = response
|
57
|
+
end
|
58
|
+
end
|
46
59
|
end
|
data/lib/dox/config.rb
CHANGED
@@ -1,16 +1,49 @@
|
|
1
1
|
module Dox
|
2
2
|
class Config
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :schema_request_folder_path
|
4
|
+
attr_reader :schema_response_folder_path
|
5
|
+
attr_reader :schema_response_fail_file_path
|
4
6
|
attr_accessor :headers_whitelist
|
7
|
+
attr_accessor :openapi_version
|
8
|
+
attr_accessor :api_version
|
9
|
+
attr_accessor :title
|
10
|
+
attr_accessor :header_description
|
11
|
+
attr_reader :descriptions_location
|
5
12
|
|
6
|
-
def
|
13
|
+
def descriptions_location=(folder_path)
|
14
|
+
raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
|
15
|
+
|
16
|
+
@descriptions_location = folder_path
|
17
|
+
end
|
18
|
+
|
19
|
+
def schema_request_folder_path=(folder_path)
|
20
|
+
raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
|
21
|
+
|
22
|
+
@schema_request_folder_path = folder_path
|
23
|
+
end
|
24
|
+
|
25
|
+
def schema_response_folder_path=(folder_path)
|
26
|
+
raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
|
27
|
+
|
28
|
+
@schema_response_folder_path = folder_path
|
29
|
+
end
|
30
|
+
|
31
|
+
def schema_response_fail_file_path=(file_path)
|
7
32
|
raise(Errors::FileNotFoundError, file_path) unless File.exist?(file_path)
|
8
|
-
|
33
|
+
|
34
|
+
@schema_response_fail_file_path = file_path
|
9
35
|
end
|
10
36
|
|
11
37
|
def desc_folder_path=(folder_path)
|
12
|
-
|
13
|
-
|
38
|
+
warn(
|
39
|
+
'DEPRECATION WARNING: desc_folder_path will be removed in the next release, please use descriptions_location instead' # rubocop:disable Layout/LineLength
|
40
|
+
)
|
41
|
+
|
42
|
+
self.descriptions_location = folder_path
|
43
|
+
end
|
44
|
+
|
45
|
+
def header_file_path=(_file_path)
|
46
|
+
warn('WARNING: header_file_path is no longer used. Move header description to config.header_description.')
|
14
47
|
end
|
15
48
|
end
|
16
49
|
end
|
data/lib/dox/dsl/action.rb
CHANGED
@@ -8,6 +8,10 @@ module Dox
|
|
8
8
|
attr_writer :path
|
9
9
|
attr_writer :desc
|
10
10
|
attr_writer :params
|
11
|
+
attr_writer :query_params
|
12
|
+
attr_writer :request_schema
|
13
|
+
attr_writer :response_schema_success
|
14
|
+
attr_writer :response_schema_fail
|
11
15
|
|
12
16
|
def initialize(name, &block)
|
13
17
|
self.name = name
|
@@ -17,13 +21,15 @@ module Dox
|
|
17
21
|
end
|
18
22
|
|
19
23
|
def config
|
20
|
-
{
|
24
|
+
{ action_request_schema: @request_schema.presence,
|
25
|
+
action_response_schema_success: @response_schema_success.presence,
|
26
|
+
action_response_schema_fail: @response_schema_fail.presence,
|
21
27
|
action_name: @name.presence,
|
22
28
|
action_verb: @verb.presence,
|
23
29
|
action_path: @path.presence,
|
24
|
-
action_desc: @desc.presence,
|
25
|
-
action_params: @params
|
26
|
-
|
30
|
+
action_desc: @desc.presence || '',
|
31
|
+
action_params: @params,
|
32
|
+
action_query_params: @query_params.presence || [] }
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
@@ -14,6 +14,8 @@ module Dox
|
|
14
14
|
self._resource = Resource.new(name, &block)
|
15
15
|
end
|
16
16
|
|
17
|
+
alias tag resource
|
18
|
+
|
17
19
|
def action(name, &block)
|
18
20
|
self._action = Action.new(name, &block)
|
19
21
|
end
|
@@ -22,6 +24,8 @@ module Dox
|
|
22
24
|
self._group = ResourceGroup.new(name, &block)
|
23
25
|
end
|
24
26
|
|
27
|
+
alias x_tag group
|
28
|
+
|
25
29
|
def config
|
26
30
|
{}.merge(_resource ? _resource.config : {})
|
27
31
|
.merge(_action ? _action.config : {})
|
data/lib/dox/dsl/syntax.rb
CHANGED
data/lib/dox/entities/action.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
module Dox
|
2
2
|
module Entities
|
3
3
|
class Action
|
4
|
-
attr_reader :name, :desc, :verb, :path, :
|
4
|
+
attr_reader :name, :desc, :verb, :path, :resource, :params
|
5
5
|
attr_accessor :examples
|
6
6
|
|
7
|
-
def initialize(
|
7
|
+
def initialize(details, request)
|
8
8
|
@request = request
|
9
|
-
@name =
|
9
|
+
@name = details[:action_name]
|
10
|
+
@resource = details[:resource_name]
|
10
11
|
@desc = details[:action_desc]
|
11
12
|
@verb = details[:action_verb] || request.method
|
12
13
|
@path = details[:action_path] || template_path
|
13
|
-
@
|
14
|
+
@params = template_params(details[:action_params], details[:action_query_params] || [])
|
14
15
|
@examples = []
|
15
16
|
|
16
17
|
validate!
|
@@ -22,24 +23,44 @@ module Dox
|
|
22
23
|
|
23
24
|
# /pokemons/1 => pokemons/{id}
|
24
25
|
def template_path
|
25
|
-
path = request.path.dup.presence || request.fullpath.split(
|
26
|
+
path = request.path.dup.presence || request.fullpath.split('?').first
|
26
27
|
path_params.each do |key, value|
|
27
28
|
path.sub!(%r{\/#{value}(\/|$)}, "/{#{key}}\\1")
|
28
29
|
end
|
30
|
+
|
29
31
|
path
|
30
32
|
end
|
31
33
|
|
34
|
+
def template_params(defined_params, query_params)
|
35
|
+
acquire_path_params + acquire_defined_params(defined_params) + query_params
|
36
|
+
end
|
37
|
+
|
32
38
|
def path_params
|
33
|
-
|
39
|
+
request.path_parameters.symbolize_keys.except(:action, :controller, :format, :subdomain)
|
40
|
+
end
|
41
|
+
|
42
|
+
def acquire_path_params
|
43
|
+
return [] if path_params.nil?
|
44
|
+
|
45
|
+
path_params.map do |param, value|
|
46
|
+
{ name: param,
|
47
|
+
in: :path,
|
48
|
+
schema: { type: guess_param_type(value) },
|
49
|
+
example: value }
|
50
|
+
end
|
34
51
|
end
|
35
52
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
53
|
+
def acquire_defined_params(defined_params)
|
54
|
+
return [] if defined_params.nil?
|
55
|
+
|
56
|
+
defined_params.map do |key, value|
|
57
|
+
{ name: key,
|
58
|
+
in: 'query',
|
59
|
+
required: value[:required],
|
60
|
+
example: value[:value],
|
61
|
+
description: value[:description].presence || '',
|
62
|
+
schema: { type: value[:type] } }
|
41
63
|
end
|
42
|
-
h
|
43
64
|
end
|
44
65
|
|
45
66
|
def guess_param_type(param)
|
data/lib/dox/entities/example.rb
CHANGED
@@ -3,20 +3,29 @@ module Dox
|
|
3
3
|
class Example
|
4
4
|
extend Forwardable
|
5
5
|
|
6
|
+
attr_reader :desc, :name, :request_schema, :response_schema_success, :response_schema_fail
|
7
|
+
|
6
8
|
def_delegator :response, :status, :response_status
|
7
9
|
def_delegator :response, :content_type, :response_content_type
|
8
|
-
def_delegator :response, :body, :response_body
|
9
10
|
def_delegator :request, :content_type, :request_content_type
|
10
11
|
def_delegator :request, :method, :request_method
|
11
12
|
|
12
13
|
def initialize(details, request, response)
|
13
14
|
@desc = details[:description]
|
15
|
+
@name = details[:resource_name].downcase
|
16
|
+
@request_schema = details[:action_request_schema]
|
17
|
+
@response_schema_success = details[:action_response_schema_success]
|
18
|
+
@response_schema_fail = details[:action_response_schema_fail]
|
14
19
|
@request = request
|
15
20
|
@response = response
|
16
21
|
end
|
17
22
|
|
18
23
|
def request_body
|
19
|
-
@request_body ||=
|
24
|
+
@request_body ||= format_content(request, request_content_type)
|
25
|
+
end
|
26
|
+
|
27
|
+
def response_body
|
28
|
+
@response_body ||= format_content(response, response_content_type)
|
20
29
|
end
|
21
30
|
|
22
31
|
def request_identifier
|
@@ -31,6 +40,10 @@ module Dox
|
|
31
40
|
@request_headers ||= filter_headers(request)
|
32
41
|
end
|
33
42
|
|
43
|
+
def response_success?
|
44
|
+
response.successful?
|
45
|
+
end
|
46
|
+
|
34
47
|
# Rails 4 includes the body params in the request_fullpath
|
35
48
|
def request_fullpath
|
36
49
|
if request.query_parameters.present?
|
@@ -42,12 +55,29 @@ module Dox
|
|
42
55
|
|
43
56
|
private
|
44
57
|
|
58
|
+
def format_content(http_env, content_type)
|
59
|
+
formatter(content_type).new(http_env).format
|
60
|
+
end
|
61
|
+
|
62
|
+
def formatter(content_type)
|
63
|
+
case content_type
|
64
|
+
when %r{application\/.*json}
|
65
|
+
Dox::Formatters::Json
|
66
|
+
when /xml/
|
67
|
+
Dox::Formatters::Xml
|
68
|
+
when /multipart/
|
69
|
+
Dox::Formatters::Multipart
|
70
|
+
else
|
71
|
+
Dox::Formatters::Plain
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
45
75
|
# Rails 5.0.2 returns "" for request.path
|
46
76
|
def request_path
|
47
|
-
request.path.presence || request.fullpath.split(
|
77
|
+
request.path.presence || request.fullpath.split('?')[0]
|
48
78
|
end
|
49
79
|
|
50
|
-
attr_reader :
|
80
|
+
attr_reader :request, :response
|
51
81
|
|
52
82
|
def filter_headers(obj)
|
53
83
|
headers_whitelist.map do |header|
|
@@ -65,12 +95,6 @@ module Dox
|
|
65
95
|
def request_url_query_parameters
|
66
96
|
CGI.unescape(request.query_parameters.to_query)
|
67
97
|
end
|
68
|
-
|
69
|
-
def parse_request_body
|
70
|
-
body = request.body.read
|
71
|
-
return body if body.blank?
|
72
|
-
JSON.parse(body)
|
73
|
-
end
|
74
98
|
end
|
75
99
|
end
|
76
100
|
end
|
@@ -1,17 +1,16 @@
|
|
1
1
|
module Dox
|
2
2
|
module Entities
|
3
3
|
class Resource
|
4
|
-
|
5
|
-
attr_reader :name, :desc, :endpoint
|
4
|
+
attr_reader :name, :desc, :endpoint, :group
|
6
5
|
attr_accessor :actions
|
7
6
|
|
8
|
-
def initialize(
|
9
|
-
@name =
|
7
|
+
def initialize(details)
|
8
|
+
@name = details[:resource_name]
|
9
|
+
@group = details[:resource_group_name]
|
10
10
|
@desc = details[:resource_desc]
|
11
11
|
@endpoint = details[:resource_endpoint]
|
12
12
|
@actions = {}
|
13
13
|
end
|
14
|
-
|
15
14
|
end
|
16
15
|
end
|
17
16
|
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
module Dox
|
2
2
|
module Entities
|
3
3
|
class ResourceGroup
|
4
|
-
|
5
4
|
attr_reader :name
|
6
5
|
attr_accessor :desc, :resources
|
7
6
|
|
8
|
-
def initialize(
|
9
|
-
@name =
|
7
|
+
def initialize(details)
|
8
|
+
@name = details[:resource_group_name]
|
10
9
|
@desc = details[:resource_group_desc]
|
11
10
|
@resources = {}
|
12
11
|
end
|
data/lib/dox/formatter.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'rspec/core'
|
2
2
|
require 'rspec/core/formatters/base_formatter'
|
3
|
+
require 'rspec/core/formatters/console_codes'
|
3
4
|
|
4
5
|
module Dox
|
5
6
|
class Formatter < RSpec::Core::Formatters::BaseFormatter
|
6
7
|
extend Forwardable
|
7
8
|
|
8
|
-
RSpec::Core::Formatters.register self, :example_passed, :stop
|
9
|
+
RSpec::Core::Formatters.register self, :example_passed, :stop, :dump_summary
|
9
10
|
|
10
11
|
def initialize(output)
|
11
12
|
super
|
@@ -17,8 +18,19 @@ module Dox
|
|
17
18
|
move_example_to_passed if current_example.document?
|
18
19
|
end
|
19
20
|
|
20
|
-
def stop(
|
21
|
-
|
21
|
+
def stop(notification)
|
22
|
+
if notification.failed_examples.any?
|
23
|
+
warn(notification.fully_formatted_failed_examples)
|
24
|
+
else
|
25
|
+
printer.print(passed_examples)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def dump_summary(summary)
|
30
|
+
return if summary.failed_examples.none?
|
31
|
+
|
32
|
+
warn(summary.fully_formatted)
|
33
|
+
exit(-1)
|
22
34
|
end
|
23
35
|
|
24
36
|
private
|
@@ -33,28 +45,26 @@ module Dox
|
|
33
45
|
if group
|
34
46
|
group.tap { |g| g.desc ||= current_example.resource_group_desc }
|
35
47
|
else
|
36
|
-
passed_examples[group_name] = Entities::ResourceGroup.new(
|
37
|
-
current_example.metadata)
|
48
|
+
passed_examples[group_name] = Entities::ResourceGroup.new(current_example.metadata)
|
38
49
|
end
|
39
50
|
end
|
40
51
|
|
41
52
|
def load_or_save_resource_to_group(group)
|
42
|
-
resource_name
|
43
|
-
group.resources[resource_name] ||= Entities::Resource.new(resource_name,
|
44
|
-
current_example.metadata)
|
53
|
+
group.resources[current_example.resource_name] ||= Entities::Resource.new(current_example.metadata)
|
45
54
|
end
|
46
55
|
|
47
56
|
def load_or_save_action_to_resource(resource)
|
48
|
-
action_name
|
49
|
-
|
50
|
-
current_example.request)
|
57
|
+
resource.actions[current_example.action_name] ||= Entities::Action.new(current_example.metadata,
|
58
|
+
current_example.request)
|
51
59
|
end
|
52
60
|
|
53
61
|
def move_example_to_passed
|
54
62
|
group = load_or_save_group
|
55
63
|
resource = load_or_save_resource_to_group(group)
|
56
64
|
action = load_or_save_action_to_resource(resource)
|
57
|
-
action.examples << Entities::Example.new(current_example.metadata,
|
65
|
+
action.examples << Entities::Example.new(current_example.metadata,
|
66
|
+
current_example.request,
|
67
|
+
current_example.response)
|
58
68
|
end
|
59
69
|
|
60
70
|
def printer
|