dox 1.0.2 → 2.0.0.beta3

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.
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "dox"
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 "irb"
13
+ require 'irb'
14
14
  IRB.start
@@ -1,5 +1,4 @@
1
- # coding: utf-8
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,31 +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 api blueprint format.'
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
 
16
- spec.required_ruby_version = '>=2.0.0'
15
+ spec.required_ruby_version = '>= 2.0.0'
17
16
 
18
17
  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
19
18
  # delete this section to allow pushing this gem to any host.
20
- if spec.respond_to?(:metadata)
21
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
22
- else
23
- raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
24
- 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'
25
22
 
26
23
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
24
  spec.bindir = 'exe'
28
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
26
  spec.require_paths = ['lib']
30
27
 
31
- spec.add_development_dependency 'bundler', '~> 1.11'
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'
32
35
  spec.add_development_dependency 'rake', '~> 10.0'
33
36
  spec.add_development_dependency 'rspec', '~> 3.0'
34
- spec.add_development_dependency 'pry'
37
+ spec.add_development_dependency 'rubocop'
35
38
  spec.add_development_dependency 'simplecov'
36
- spec.add_development_dependency 'codeclimate-test-reporter'
37
39
  spec.add_runtime_dependency 'rspec-core'
38
- spec.add_runtime_dependency 'rails', '>= 4.0'
39
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/example_printer'
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
@@ -1,16 +1,49 @@
1
1
  module Dox
2
2
  class Config
3
- attr_reader :header_file_path, :desc_folder_path
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 header_file_path=(file_path)
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
- @header_file_path = file_path
33
+
34
+ @schema_response_fail_file_path = file_path
9
35
  end
10
36
 
11
37
  def desc_folder_path=(folder_path)
12
- raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
13
- @desc_folder_path = folder_path
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
@@ -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.presence
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 : {})
@@ -10,6 +10,7 @@ module Dox
10
10
 
11
11
  def const_missing(name)
12
12
  documentation = _subjects[infer_subject(name)]
13
+
13
14
  return super unless documentation
14
15
 
15
16
  Module.new do
@@ -1,16 +1,17 @@
1
1
  module Dox
2
2
  module Entities
3
3
  class Action
4
- attr_reader :name, :desc, :verb, :path, :uri_params
4
+ attr_reader :name, :desc, :verb, :path, :resource, :params
5
5
  attr_accessor :examples
6
6
 
7
- def initialize(name, details, request)
7
+ def initialize(details, request)
8
8
  @request = request
9
- @name = 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
- @uri_params = details[:action_params] || template_path_params
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("?").first
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
- @path_params ||= request.path_parameters.symbolize_keys.except(:action, :controller, :format)
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 template_path_params
37
- h = {}
38
- path_params.each do |param, value|
39
- param_type = guess_param_type(value)
40
- h[param] = { type: param_type, required: :required, value: value }
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)
@@ -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 ||= parse_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("?")[0]
77
+ request.path.presence || request.fullpath.split('?')[0]
48
78
  end
49
79
 
50
- attr_reader :desc, :request, :response
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(name, details)
9
- @name = 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(name, details)
9
- @name = name
7
+ def initialize(details)
8
+ @name = details[:resource_group_name]
10
9
  @desc = details[:resource_group_desc]
11
10
  @resources = {}
12
11
  end
@@ -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(_notification)
21
- printer.print(passed_examples)
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(group_name,
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 = current_example.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 = current_example.action_name
49
- resource.actions[action_name] ||= Entities::Action.new(action_name, current_example.metadata,
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, current_example.request, current_example.response)
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