dox 1.0.0.alpha → 1.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 +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +14 -0
- data/.ruby-version +1 -0
- data/.travis.yml +23 -2
- data/CHANGES.md +51 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +1 -1
- data/README.md +236 -48
- data/dox.gemspec +18 -16
- data/exe/dox +3 -0
- data/infinum.png +0 -0
- data/lib/dox.rb +12 -5
- data/lib/dox/config.rb +3 -5
- data/lib/dox/dsl/attr_proxy.rb +1 -1
- data/lib/dox/dsl/documentation.rb +1 -1
- data/lib/dox/entities/action.rb +31 -5
- data/lib/dox/entities/example.rb +45 -12
- data/lib/dox/formatter.rb +66 -42
- data/lib/dox/printers/action_printer.rb +24 -8
- data/lib/dox/printers/base_printer.rb +10 -7
- data/lib/dox/printers/document_printer.rb +0 -2
- data/lib/dox/printers/example_printer.rb +78 -9
- data/lib/dox/printers/resource_group_printer.rb +12 -3
- data/lib/dox/printers/resource_printer.rb +12 -3
- data/lib/dox/util/http.rb +11 -0
- data/lib/dox/version.rb +1 -1
- metadata +42 -5
data/dox.gemspec
CHANGED
@@ -4,32 +4,34 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'dox/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'dox'
|
8
8
|
spec.version = Dox::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Melita Kokot', 'Vedran Hrnčić']
|
10
|
+
spec.email = ['melita.kokot@gmail.com', 'vrabac266@gmail.com']
|
11
11
|
|
12
|
-
spec.summary =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
12
|
+
spec.summary = 'Generates API documentation for rspec in api blueprint format.'
|
13
|
+
spec.homepage = 'https://github.com/infinum/dox'
|
14
|
+
spec.license = 'MIT'
|
15
15
|
|
16
16
|
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
17
17
|
# delete this section to allow pushing this gem to any host.
|
18
18
|
if spec.respond_to?(:metadata)
|
19
|
-
spec.metadata['allowed_push_host'] =
|
19
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
20
20
|
else
|
21
|
-
raise
|
21
|
+
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
|
22
22
|
end
|
23
23
|
|
24
24
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
-
spec.bindir =
|
25
|
+
spec.bindir = 'exe'
|
26
26
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
-
spec.require_paths = [
|
27
|
+
spec.require_paths = ['lib']
|
28
28
|
|
29
|
-
spec.add_development_dependency
|
30
|
-
spec.add_development_dependency
|
31
|
-
spec.add_development_dependency
|
32
|
-
spec.add_development_dependency
|
33
|
-
spec.
|
34
|
-
spec.
|
29
|
+
spec.add_development_dependency 'bundler', '~> 1.11'
|
30
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
31
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
32
|
+
spec.add_development_dependency 'pry'
|
33
|
+
spec.add_development_dependency 'simplecov'
|
34
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
35
|
+
spec.add_runtime_dependency 'rspec-core'
|
36
|
+
spec.add_runtime_dependency 'rails', '>= 4.0'
|
35
37
|
end
|
data/exe/dox
ADDED
data/infinum.png
ADDED
Binary file
|
data/lib/dox.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/string'
|
3
|
+
require 'forwardable'
|
4
|
+
|
1
5
|
require 'dox/config'
|
2
6
|
require 'dox/dsl/attr_proxy'
|
3
7
|
require 'dox/dsl/action'
|
@@ -21,19 +25,22 @@ require 'dox/printers/document_printer'
|
|
21
25
|
require 'dox/printers/example_printer'
|
22
26
|
require 'dox/printers/resource_group_printer'
|
23
27
|
require 'dox/printers/resource_printer'
|
28
|
+
require 'dox/util/http'
|
24
29
|
require 'dox/version'
|
25
30
|
|
26
|
-
|
27
31
|
module Dox
|
28
|
-
|
29
|
-
attr_writer :config
|
30
|
-
end
|
32
|
+
Error = Class.new(StandardError)
|
31
33
|
|
32
34
|
def self.configure
|
33
35
|
yield(config) if block_given?
|
34
36
|
end
|
35
37
|
|
36
38
|
def self.config
|
37
|
-
@
|
39
|
+
@config ||= Dox::Config.new
|
40
|
+
end
|
41
|
+
|
42
|
+
DEFAULT_HEADERS_WHITELIST = ['Accept', 'Content-Type'].freeze
|
43
|
+
def self.full_headers_whitelist
|
44
|
+
(config.headers_whitelist.to_a + DEFAULT_HEADERS_WHITELIST).uniq
|
38
45
|
end
|
39
46
|
end
|
data/lib/dox/config.rb
CHANGED
@@ -1,18 +1,16 @@
|
|
1
1
|
module Dox
|
2
2
|
class Config
|
3
|
-
|
4
3
|
attr_reader :header_file_path, :desc_folder_path
|
4
|
+
attr_accessor :headers_whitelist
|
5
5
|
|
6
6
|
def header_file_path=(file_path)
|
7
|
-
raise(Errors::FileNotFoundError, file_path) unless File.
|
7
|
+
raise(Errors::FileNotFoundError, file_path) unless File.exist?(file_path)
|
8
8
|
@header_file_path = file_path
|
9
9
|
end
|
10
10
|
|
11
11
|
def desc_folder_path=(folder_path)
|
12
|
-
raise(Errors::FolderNotFoundError, folder_path) unless Dir.
|
12
|
+
raise(Errors::FolderNotFoundError, folder_path) unless Dir.exist?(folder_path)
|
13
13
|
@desc_folder_path = folder_path
|
14
14
|
end
|
15
|
-
|
16
15
|
end
|
17
16
|
end
|
18
|
-
|
data/lib/dox/dsl/attr_proxy.rb
CHANGED
data/lib/dox/entities/action.rb
CHANGED
@@ -10,23 +10,49 @@ module Dox
|
|
10
10
|
@desc = details[:action_desc]
|
11
11
|
@verb = details[:action_verb] || request.method
|
12
12
|
@path = details[:action_path] || template_path
|
13
|
-
@uri_params = details[:action_params]
|
13
|
+
@uri_params = details[:action_params] || template_path_params
|
14
14
|
@examples = []
|
15
|
+
|
16
|
+
validate!
|
15
17
|
end
|
16
18
|
|
17
19
|
private
|
18
20
|
|
19
21
|
attr_reader :request
|
20
22
|
|
23
|
+
# /pokemons/1 => pokemons/{id}
|
21
24
|
def template_path
|
22
|
-
|
23
|
-
path = request.path.dup
|
25
|
+
path = request.path.dup.presence || request.fullpath.split("?").first
|
24
26
|
path_params.each do |key, value|
|
25
|
-
|
26
|
-
path.sub!(/\/#{value}(\/|$)/, "/{#{key}}\\1")
|
27
|
+
path.sub!(%r{\/#{value}(\/|$)}, "/{#{key}}\\1")
|
27
28
|
end
|
28
29
|
path
|
29
30
|
end
|
31
|
+
|
32
|
+
def path_params
|
33
|
+
@path_params ||= request.path_parameters.symbolize_keys.except(:action, :controller, :format)
|
34
|
+
end
|
35
|
+
|
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 }
|
41
|
+
end
|
42
|
+
h
|
43
|
+
end
|
44
|
+
|
45
|
+
def guess_param_type(param)
|
46
|
+
if param =~ /^\d+$/
|
47
|
+
:number
|
48
|
+
else
|
49
|
+
:string
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate!
|
54
|
+
raise(Error, "Unrecognized HTTP verb #{verb}") unless Util::Http.verb?(verb)
|
55
|
+
end
|
30
56
|
end
|
31
57
|
end
|
32
58
|
end
|
data/lib/dox/entities/example.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
module Dox
|
2
2
|
module Entities
|
3
3
|
class Example
|
4
|
+
extend Forwardable
|
4
5
|
|
5
|
-
|
6
|
+
def_delegator :response, :status, :response_status
|
7
|
+
def_delegator :response, :content_type, :response_content_type
|
8
|
+
def_delegator :response, :body, :response_body
|
9
|
+
def_delegator :request, :content_type, :request_content_type
|
10
|
+
def_delegator :request, :method, :request_method
|
6
11
|
|
7
12
|
def initialize(details, request, response)
|
8
13
|
@desc = details[:description]
|
@@ -11,29 +16,57 @@ module Dox
|
|
11
16
|
end
|
12
17
|
|
13
18
|
def request_parameters
|
14
|
-
request.parameters
|
15
|
-
|
16
|
-
|
17
|
-
def request_content_type
|
18
|
-
request.content_type
|
19
|
+
request.parameters
|
20
|
+
.except(*request.path_parameters.keys.map(&:to_s))
|
21
|
+
.except(*request.query_parameters.keys.map(&:to_s))
|
19
22
|
end
|
20
23
|
|
21
24
|
def request_identifier
|
22
25
|
@desc
|
23
26
|
end
|
24
27
|
|
25
|
-
def
|
26
|
-
response
|
28
|
+
def response_headers
|
29
|
+
@response_headers ||= filter_headers(response)
|
30
|
+
end
|
31
|
+
|
32
|
+
def request_headers
|
33
|
+
@request_headers ||= filter_headers(request)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Rails 4 includes the body params in the request_fullpath
|
37
|
+
def request_fullpath
|
38
|
+
if request.query_parameters.present?
|
39
|
+
"#{request_path}?#{request_url_query_parameters}"
|
40
|
+
else
|
41
|
+
request_path
|
42
|
+
end
|
27
43
|
end
|
28
44
|
|
29
|
-
|
30
|
-
|
45
|
+
private
|
46
|
+
|
47
|
+
# Rails 5.0.2 returns "" for request.path
|
48
|
+
def request_path
|
49
|
+
request.path.presence || request.fullpath.split("?")[0]
|
31
50
|
end
|
32
51
|
|
33
|
-
|
34
|
-
|
52
|
+
attr_reader :desc, :request, :response
|
53
|
+
|
54
|
+
def filter_headers(obj)
|
55
|
+
headers_whitelist.map do |header|
|
56
|
+
header_val = obj.headers[header]
|
57
|
+
next if header_val.blank?
|
58
|
+
|
59
|
+
[header, header_val]
|
60
|
+
end.compact
|
35
61
|
end
|
36
62
|
|
63
|
+
def headers_whitelist
|
64
|
+
@headers_whitelist ||= Dox.full_headers_whitelist
|
65
|
+
end
|
66
|
+
|
67
|
+
def request_url_query_parameters
|
68
|
+
CGI.unescape(request.query_parameters.to_query)
|
69
|
+
end
|
37
70
|
end
|
38
71
|
end
|
39
72
|
end
|
data/lib/dox/formatter.rb
CHANGED
@@ -1,86 +1,110 @@
|
|
1
|
+
require 'rspec/core'
|
1
2
|
require 'rspec/core/formatters/base_formatter'
|
2
|
-
require 'pry'
|
3
3
|
|
4
4
|
module Dox
|
5
5
|
class Formatter < RSpec::Core::Formatters::BaseFormatter
|
6
|
+
extend Forwardable
|
6
7
|
|
7
|
-
RSpec::Core::Formatters.register self, :example_passed, :
|
8
|
+
RSpec::Core::Formatters.register self, :example_passed, :stop
|
8
9
|
|
9
10
|
def initialize(output)
|
10
11
|
super
|
11
|
-
|
12
|
-
@group_level = 0
|
13
|
-
end
|
14
|
-
|
15
|
-
def example_started(notification)
|
16
|
-
@example_group_instance = notification.example.example_group_instance
|
12
|
+
self.passed_examples = {}
|
17
13
|
end
|
18
14
|
|
19
15
|
def example_passed(passed)
|
20
|
-
|
21
|
-
if
|
22
|
-
move_example_to_passed
|
23
|
-
end
|
24
|
-
@example_group_instance = nil
|
16
|
+
self.current_example = CurrentExample.new(passed.example)
|
17
|
+
move_example_to_passed if current_example.document?
|
25
18
|
end
|
26
19
|
|
27
20
|
def stop(_notification)
|
28
|
-
printer.print(
|
21
|
+
printer.print(passed_examples)
|
29
22
|
end
|
30
23
|
|
31
24
|
private
|
32
25
|
|
26
|
+
attr_accessor :passed_examples
|
27
|
+
attr_accessor :current_example
|
28
|
+
|
33
29
|
def load_or_save_group
|
34
|
-
group_name =
|
35
|
-
group =
|
30
|
+
group_name = current_example.resource_group_name
|
31
|
+
group = passed_examples[group_name]
|
36
32
|
|
37
33
|
if group
|
38
|
-
group.desc ||=
|
39
|
-
group
|
34
|
+
group.tap { |g| g.desc ||= current_example.resource_group_desc }
|
40
35
|
else
|
41
|
-
|
36
|
+
passed_examples[group_name] = Entities::ResourceGroup.new(group_name,
|
37
|
+
current_example.metadata)
|
42
38
|
end
|
43
39
|
end
|
44
40
|
|
45
41
|
def load_or_save_resource_to_group(group)
|
46
|
-
resource_name =
|
47
|
-
group.resources[resource_name] ||= Entities::Resource.new(resource_name,
|
42
|
+
resource_name = current_example.resource_name
|
43
|
+
group.resources[resource_name] ||= Entities::Resource.new(resource_name,
|
44
|
+
current_example.metadata)
|
48
45
|
end
|
49
46
|
|
50
47
|
def load_or_save_action_to_resource(resource)
|
51
|
-
action_name =
|
52
|
-
resource.actions[action_name] ||= Entities::Action.new(action_name,
|
48
|
+
action_name = current_example.action_name
|
49
|
+
resource.actions[action_name] ||= Entities::Action.new(action_name, current_example.metadata,
|
50
|
+
current_example.request)
|
53
51
|
end
|
54
52
|
|
55
53
|
def move_example_to_passed
|
56
54
|
group = load_or_save_group
|
57
55
|
resource = load_or_save_resource_to_group(group)
|
58
56
|
action = load_or_save_action_to_resource(resource)
|
59
|
-
action.examples << Entities::Example.new(
|
57
|
+
action.examples << Entities::Example.new(current_example.metadata, current_example.request, current_example.response)
|
60
58
|
end
|
61
59
|
|
62
|
-
def
|
63
|
-
@
|
64
|
-
!@current_example_data[:nodoc]
|
65
|
-
# error check
|
66
|
-
#&&
|
67
|
-
# @current_example_data[:resource_group] &&
|
68
|
-
# @current_example_data[:resource] &&
|
69
|
-
# @current_example_data[:action] &&
|
70
|
-
# !@current_example_data[:nodoc]
|
60
|
+
def printer
|
61
|
+
@printer ||= Printers::DocumentPrinter.new(output)
|
71
62
|
end
|
72
63
|
|
73
|
-
|
74
|
-
|
75
|
-
end
|
64
|
+
class CurrentExample # :nodoc:
|
65
|
+
extend Forwardable
|
76
66
|
|
77
|
-
|
78
|
-
@example_group_instance.response
|
79
|
-
end
|
67
|
+
delegate [:metadata] => :example
|
80
68
|
|
81
|
-
|
82
|
-
|
83
|
-
|
69
|
+
attr_reader :example
|
70
|
+
|
71
|
+
def initialize(example)
|
72
|
+
@example = example
|
73
|
+
end
|
74
|
+
|
75
|
+
def resource_group_name
|
76
|
+
metadata[:resource_group_name]
|
77
|
+
end
|
84
78
|
|
79
|
+
def resource_group_desc
|
80
|
+
metadata[:resource_group_desc]
|
81
|
+
end
|
82
|
+
|
83
|
+
def resource_name
|
84
|
+
metadata[:resource_name]
|
85
|
+
end
|
86
|
+
|
87
|
+
def action_name
|
88
|
+
metadata[:action_name]
|
89
|
+
end
|
90
|
+
|
91
|
+
def request
|
92
|
+
metadata[:request]
|
93
|
+
end
|
94
|
+
|
95
|
+
def response
|
96
|
+
metadata[:response]
|
97
|
+
end
|
98
|
+
|
99
|
+
def document?
|
100
|
+
tagged_with?(:apidoc) && tagged_with?(:dox) && !tagged_with?(:nodoc)
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def tagged_with?(key)
|
106
|
+
metadata.key?(key) && metadata[key] == true
|
107
|
+
end
|
108
|
+
end
|
85
109
|
end
|
86
110
|
end
|
@@ -1,13 +1,10 @@
|
|
1
1
|
module Dox
|
2
2
|
module Printers
|
3
3
|
class ActionPrinter < BasePrinter
|
4
|
-
|
5
4
|
def print(action)
|
6
|
-
|
7
|
-
|
8
|
-
if action.uri_params.present?
|
9
|
-
@output.puts("+ Parameters\n#{formatted_params(action.uri_params)}")
|
10
|
-
end
|
5
|
+
self.action = action
|
6
|
+
@output.puts action_title
|
7
|
+
@output.puts action_uri_params if action.uri_params.present?
|
11
8
|
|
12
9
|
action.examples.each do |example|
|
13
10
|
example_printer.print(example)
|
@@ -16,16 +13,35 @@ module Dox
|
|
16
13
|
|
17
14
|
private
|
18
15
|
|
16
|
+
attr_accessor :action
|
17
|
+
|
18
|
+
def action_title
|
19
|
+
<<-HEREDOC
|
20
|
+
|
21
|
+
### #{action.name} [#{action.verb.upcase} #{action.path}]
|
22
|
+
#{print_desc(action.desc)}
|
23
|
+
HEREDOC
|
24
|
+
end
|
25
|
+
|
26
|
+
def action_uri_params
|
27
|
+
<<-HEREDOC
|
28
|
+
+ Parameters
|
29
|
+
#{formatted_params(action.uri_params)}
|
30
|
+
HEREDOC
|
31
|
+
end
|
32
|
+
|
19
33
|
def example_printer
|
20
34
|
@example_printer ||= ExamplePrinter.new(@output)
|
21
35
|
end
|
22
36
|
|
23
37
|
def formatted_params(uri_params)
|
24
38
|
uri_params.map do |param, details|
|
25
|
-
" + #{CGI.escape(param.to_s)}: `#{CGI.escape(details[:value].to_s)}` (#{details[:type]}, #{details[:required]})
|
39
|
+
desc = " + #{CGI.escape(param.to_s)}: `#{CGI.escape(details[:value].to_s)}` (#{details[:type]}, #{details[:required]})"
|
40
|
+
desc += " - #{details[:description]}" if details[:description].present?
|
41
|
+
desc += "\n + Default: #{details[:default]}" if details[:default].present?
|
42
|
+
desc
|
26
43
|
end.flatten.join("\n")
|
27
44
|
end
|
28
|
-
|
29
45
|
end
|
30
46
|
end
|
31
47
|
end
|