dox 1.0.0.alpha → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|