dox 1.1.0 → 2.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.
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
data/bin/console CHANGED
@@ -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
data/dox.gemspec CHANGED
@@ -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'
32
- spec.add_development_dependency 'rake', '~> 10.0'
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'
35
+ spec.add_development_dependency 'rake'
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
data/lib/dox/config.rb CHANGED
@@ -1,16 +1,50 @@
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_accessor :groups_order
12
+ attr_reader :descriptions_location
5
13
 
6
- def header_file_path=(file_path)
14
+ def descriptions_location=(folder_path)
15
+ raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
16
+
17
+ @descriptions_location = folder_path
18
+ end
19
+
20
+ def schema_request_folder_path=(folder_path)
21
+ raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
22
+
23
+ @schema_request_folder_path = folder_path
24
+ end
25
+
26
+ def schema_response_folder_path=(folder_path)
27
+ raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
28
+
29
+ @schema_response_folder_path = folder_path
30
+ end
31
+
32
+ def schema_response_fail_file_path=(file_path)
7
33
  raise(Errors::FileNotFoundError, file_path) unless File.exist?(file_path)
8
- @header_file_path = file_path
34
+
35
+ @schema_response_fail_file_path = file_path
9
36
  end
10
37
 
11
38
  def desc_folder_path=(folder_path)
12
- raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
13
- @desc_folder_path = folder_path
39
+ warn(
40
+ 'DEPRECATION WARNING: desc_folder_path will be removed in the next release, please use descriptions_location instead' # rubocop:disable Layout/LineLength
41
+ )
42
+
43
+ self.descriptions_location = folder_path
44
+ end
45
+
46
+ def header_file_path=(_file_path)
47
+ warn('WARNING: header_file_path is no longer used. Move header description to config.header_description.')
14
48
  end
15
49
  end
16
50
  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 : {})
@@ -16,7 +16,6 @@ module Dox
16
16
 
17
17
  raise(Dox::Errors::InvalidResourceError, 'Resource name is required!') if @name.blank?
18
18
  raise(Dox::Errors::InvalidResourceError, 'Resource group is required!') if @group.blank?
19
- raise(Dox::Errors::InvalidResourceError, 'Resource endpoint is required!') if @endpoint.blank?
20
19
  end
21
20
 
22
21
  def config
@@ -24,7 +23,6 @@ module Dox
24
23
  resource_name: @name.presence,
25
24
  resource_desc: @desc.presence,
26
25
  resource_group_name: @group.presence,
27
- resource_endpoint: @endpoint.presence,
28
26
  apidoc: true
29
27
  }
30
28
  end
@@ -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,25 +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 ||=
34
- request.path_parameters.symbolize_keys.except(:action, :controller, :format, :subdomain)
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
35
51
  end
36
52
 
37
- def template_path_params
38
- h = {}
39
- path_params.each do |param, value|
40
- param_type = guess_param_type(value)
41
- 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] } }
42
63
  end
43
- h
44
64
  end
45
65
 
46
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 ||= request.body.read
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
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|
@@ -1,17 +1,15 @@
1
1
  module Dox
2
2
  module Entities
3
3
  class Resource
4
-
5
- attr_reader :name, :desc, :endpoint
4
+ attr_reader :name, :desc, :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
- @endpoint = details[:resource_endpoint]
12
11
  @actions = {}
13
12
  end
14
-
15
13
  end
16
14
  end
17
15
  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
data/lib/dox/formatter.rb CHANGED
@@ -20,7 +20,7 @@ module Dox
20
20
 
21
21
  def stop(notification)
22
22
  if notification.failed_examples.any?
23
- $stderr.puts(notification.fully_formatted_failed_examples)
23
+ warn(notification.fully_formatted_failed_examples)
24
24
  else
25
25
  printer.print(passed_examples)
26
26
  end
@@ -28,7 +28,8 @@ module Dox
28
28
 
29
29
  def dump_summary(summary)
30
30
  return if summary.failed_examples.none?
31
- $stderr.puts(summary.fully_formatted)
31
+
32
+ warn(summary.fully_formatted)
32
33
  exit(-1)
33
34
  end
34
35
 
@@ -44,28 +45,26 @@ module Dox
44
45
  if group
45
46
  group.tap { |g| g.desc ||= current_example.resource_group_desc }
46
47
  else
47
- passed_examples[group_name] = Entities::ResourceGroup.new(group_name,
48
- current_example.metadata)
48
+ passed_examples[group_name] = Entities::ResourceGroup.new(current_example.metadata)
49
49
  end
50
50
  end
51
51
 
52
52
  def load_or_save_resource_to_group(group)
53
- resource_name = current_example.resource_name
54
- group.resources[resource_name] ||= Entities::Resource.new(resource_name,
55
- current_example.metadata)
53
+ group.resources[current_example.resource_name] ||= Entities::Resource.new(current_example.metadata)
56
54
  end
57
55
 
58
56
  def load_or_save_action_to_resource(resource)
59
- action_name = current_example.action_name
60
- resource.actions[action_name] ||= Entities::Action.new(action_name, current_example.metadata,
61
- current_example.request)
57
+ resource.actions[current_example.action_name] ||= Entities::Action.new(current_example.metadata,
58
+ current_example.request)
62
59
  end
63
60
 
64
61
  def move_example_to_passed
65
62
  group = load_or_save_group
66
63
  resource = load_or_save_resource_to_group(group)
67
64
  action = load_or_save_action_to_resource(resource)
68
- 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)
69
68
  end
70
69
 
71
70
  def printer