rails_api_documentation 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -8
- data/app/assets/javascripts/table.js.coffee +10 -1
- data/app/assets/stylesheets/rails_api_doc/default.sass +39 -16
- data/app/assets/stylesheets/rails_api_doc/table.sass +21 -4
- data/app/controllers/rails_api_doc/api_docs_controller.rb +17 -20
- data/app/helpers/rails_api_doc/application_helper.rb +11 -0
- data/app/models/rails_api_doc/api_datum.rb +17 -0
- data/app/views/layouts/rails_api_doc/application.slim +2 -5
- data/app/views/rails_api_doc/api_docs/_request_api_table.slim +1 -1
- data/app/views/rails_api_doc/api_docs/_response_api_table.slim +4 -3
- data/app/views/rails_api_doc/api_docs/{index.slim → show.slim} +0 -0
- data/app/views/shared/_param_inputs.slim +2 -3
- data/app/views/shared/_table.slim +8 -6
- data/app/views/shared/_title.slim +8 -0
- data/config/routes.rb +1 -1
- data/lib/generators/rails_api_doc/templates/api_datum_migration.rb +4 -1
- data/lib/rails_api_doc.rb +16 -1
- data/lib/rails_api_doc/controller/headers.rb +59 -0
- data/lib/rails_api_doc/controller/param.rb +113 -0
- data/lib/rails_api_doc/controller/repo.rb +35 -0
- data/lib/rails_api_doc/controller/request/dsl.rb +1 -3
- data/lib/rails_api_doc/controller/request/factory.rb +29 -0
- data/lib/rails_api_doc/controller/request/headers.rb +11 -42
- data/lib/rails_api_doc/controller/request/param.rb +26 -33
- data/lib/rails_api_doc/controller/request/repository.rb +5 -26
- data/lib/rails_api_doc/controller/response/factory.rb +20 -2
- data/lib/rails_api_doc/controller/response/headers.rb +10 -21
- data/lib/rails_api_doc/controller/response/param.rb +38 -1
- data/lib/rails_api_doc/controller/response/rabl.rb +8 -19
- data/lib/rails_api_doc/controller/response/rabl_compiler.rb +7 -9
- data/lib/rails_api_doc/controller/response/repository.rb +38 -0
- data/lib/rails_api_doc/model/attribute_merger.rb +130 -0
- data/lib/rails_api_doc/model/attribute_parser.rb +23 -5
- data/lib/rails_api_doc/version.rb +1 -1
- metadata +10 -3
@@ -6,12 +6,30 @@ class RailsApiDoc::Controller::Response::Factory
|
|
6
6
|
class << self
|
7
7
|
|
8
8
|
# TODO: add more options later depending on app settings
|
9
|
+
# TODO: rename to :load_repo
|
9
10
|
def repo
|
10
|
-
|
11
|
+
attributes = RailsApiDoc::Controller::Response::Repository.new(repository.repo)
|
12
|
+
|
13
|
+
attributes = merge_attributes_from_model attributes
|
14
|
+
|
15
|
+
attributes
|
11
16
|
end
|
12
17
|
|
13
18
|
def controllers
|
14
|
-
RailsApiDoc::Controller::Request::
|
19
|
+
RailsApiDoc::Controller::Request::Factory.registered_controllers
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def repository
|
25
|
+
@repo ||= ::RailsApiDoc::Controller::Response::Rabl.new(controllers)
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# do not mutate attributes
|
30
|
+
#
|
31
|
+
def merge_attributes_from_model(attributes)
|
32
|
+
RailsApiDoc::Model::AttributeMerger.new(attributes).call(api_type: 'response')
|
15
33
|
end
|
16
34
|
|
17
35
|
end
|
@@ -1,27 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
1
3
|
# :nodoc:
|
2
4
|
module RailsApiDoc::Controller::Response::Headers
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
title = row_values.attr.to_s
|
13
|
-
title += '(NESTED)' if row_values.nested?
|
14
|
-
title
|
15
|
-
}
|
16
|
-
},
|
17
|
-
'Desc' => {
|
18
|
-
value: -> (row_name, row_values) {
|
19
|
-
'TODO: description' # if row_values[:desc]
|
20
|
-
},
|
21
|
-
fill_type: :input,
|
22
|
-
param: :desc
|
23
|
-
}
|
24
|
-
}
|
6
|
+
include RailsApiDoc::Controller::Headers
|
7
|
+
|
8
|
+
RESPONSE_HEADERS = [
|
9
|
+
NAME_HEADER,
|
10
|
+
TYPE_HEADER,
|
11
|
+
VALUE_HEADER,
|
12
|
+
DESC_HEADER
|
13
|
+
].reduce(&:merge).freeze
|
25
14
|
|
26
15
|
def headers
|
27
16
|
RESPONSE_HEADERS
|
@@ -1,13 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
1
3
|
# :nodoc:
|
2
4
|
module RailsApiDoc
|
3
5
|
module Controller
|
4
6
|
module Response
|
5
|
-
|
7
|
+
|
8
|
+
#
|
9
|
+
# name - attribute key returned in json response
|
10
|
+
# attr - actual attribute usage attribute
|
11
|
+
# nested - attribute nesting if present
|
12
|
+
# model - attribute model if nested and has model
|
13
|
+
# desc - description for attribute
|
14
|
+
# type - attribute type if set
|
15
|
+
# id - param id in DB for lookup on display
|
16
|
+
#
|
17
|
+
class Param < RailsApiDoc::Controller::Param
|
18
|
+
|
19
|
+
define_accessors *VALID_RESPONSE_KEYS
|
20
|
+
|
21
|
+
def initialize(name, attr, nested, model = nil, desc = nil, type = nil, id = nil, action_type = nil, is_new: false)
|
22
|
+
@name = name
|
23
|
+
@store = {
|
24
|
+
name: name,
|
25
|
+
attr: attr,
|
26
|
+
nested: nested,
|
27
|
+
model: model,
|
28
|
+
desc: desc,
|
29
|
+
type: type,
|
30
|
+
action_type: action_type,
|
31
|
+
id: id
|
32
|
+
}.compact
|
33
|
+
|
34
|
+
@is_new = is_new
|
35
|
+
@new = []
|
36
|
+
end
|
6
37
|
|
7
38
|
def nested?
|
8
39
|
!nested.nil?
|
9
40
|
end
|
10
41
|
|
42
|
+
def display_value
|
43
|
+
title = attr.to_s
|
44
|
+
title += '(NESTED)' if nested?
|
45
|
+
title
|
46
|
+
end
|
47
|
+
|
11
48
|
end
|
12
49
|
end
|
13
50
|
end
|
@@ -7,48 +7,37 @@ module RailsApiDoc::Controller::Response
|
|
7
7
|
# :nodoc:
|
8
8
|
class Rabl
|
9
9
|
|
10
|
-
include RailsApiDoc::Controller::Response::Headers
|
11
|
-
|
12
10
|
class << self
|
13
11
|
|
14
12
|
attr_accessor :renderer
|
15
13
|
|
16
14
|
end
|
17
15
|
|
18
|
-
attr_reader :
|
16
|
+
attr_reader :repo
|
19
17
|
|
20
18
|
# pass all controllers registered for api doc
|
21
19
|
# TODO: add setting for displaying all from start
|
22
20
|
def initialize(controllers)
|
23
21
|
@controllers = controllers
|
24
|
-
@routes = Rails.application.routes.set.anchored_routes.reject { |r| r.defaults[:internal] }
|
25
|
-
@map = construct_controller_template_map
|
26
|
-
end
|
27
22
|
|
28
|
-
|
29
|
-
RablCompiler.new("#{ctrl.controller_path}/#{action}").compile_source
|
30
|
-
end
|
23
|
+
@routes = Rails.application.routes.set.anchored_routes.reject { |r| r.defaults[:internal] }
|
31
24
|
|
32
|
-
|
33
|
-
action_route = @map[ctrl][:routes].detect { |r| r.defaults[:action] == action }
|
34
|
-
return unless action_route
|
35
|
-
method = action_route.instance_variable_get(:@request_method_match)&.first&.name&.split('::')&.last
|
36
|
-
route = action_route.path.spec.to_s
|
37
|
-
[method, route].compact.join(' ')
|
25
|
+
@repo = construct_controller_template_map
|
38
26
|
end
|
39
27
|
|
40
|
-
private
|
41
|
-
|
42
28
|
def construct_controller_template_map
|
43
|
-
@controllers.each_with_object({}) do |ctrl,
|
44
|
-
|
29
|
+
@controllers.each_with_object({}) do |ctrl, repo|
|
30
|
+
repo[ctrl] = ctrl_actions(ctrl)
|
45
31
|
end
|
46
32
|
end
|
47
33
|
|
34
|
+
private
|
35
|
+
|
48
36
|
def ctrl_actions(ctrl)
|
49
37
|
routes = @routes.select do |route|
|
50
38
|
route.defaults[:controller].in?([ctrl.controller_path, ctrl.controller_name])
|
51
39
|
end
|
40
|
+
|
52
41
|
actions = routes.map { |r| r.defaults[:action] }
|
53
42
|
{
|
54
43
|
routes: routes,
|
@@ -23,9 +23,7 @@ module RailsApiDoc::Controller::Response
|
|
23
23
|
# end
|
24
24
|
|
25
25
|
def add(n)
|
26
|
-
if n[:attr] && n[:name] != n[:attr]
|
27
|
-
n[:name] = "#{n[:name]}(#{n[:attr]})"
|
28
|
-
end
|
26
|
+
n[:name] = "#{n[:name]}(#{n[:attr]})" if n[:attr] && n[:name] != n[:attr]
|
29
27
|
|
30
28
|
@nodes[n[:name]] = RailsApiDoc::Controller::Response::Param.new(n[:name], n[:attr], n[:nested])
|
31
29
|
end
|
@@ -159,8 +157,8 @@ module RailsApiDoc::Controller::Response
|
|
159
157
|
# merge { |item| partial("specific/#{item.to_s}", object: item) }
|
160
158
|
#
|
161
159
|
# def merge
|
162
|
-
|
163
|
-
|
160
|
+
# return unless block_given?
|
161
|
+
# yield
|
164
162
|
# end
|
165
163
|
|
166
164
|
#
|
@@ -181,13 +179,13 @@ module RailsApiDoc::Controller::Response
|
|
181
179
|
# end
|
182
180
|
#
|
183
181
|
# def condition(*)
|
184
|
-
|
185
|
-
|
186
|
-
|
182
|
+
# return unless block_given?
|
183
|
+
# template = sub_compile(nil, true) { yield }
|
184
|
+
# @attributes.extends template
|
187
185
|
# end
|
188
186
|
#
|
189
187
|
def method_missing(name, *attrs)
|
190
|
-
return p("#{name} is not implemented in railsApiDoc") if name.in?(
|
188
|
+
return p("#{name} is not implemented in railsApiDoc") if name.in?(%w(merge condtiion))
|
191
189
|
super
|
192
190
|
end
|
193
191
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# :nodoc:
|
2
|
+
class RailsApiDoc::Controller::Response::Repository
|
3
|
+
|
4
|
+
include RailsApiDoc::Controller::Repo
|
5
|
+
include RailsApiDoc::Controller::Response::Headers
|
6
|
+
|
7
|
+
def initialize(repo)
|
8
|
+
@repo = repo.clone.transform_values { |v| v.deep_dup }
|
9
|
+
end
|
10
|
+
|
11
|
+
def load_template(ctrl, action)
|
12
|
+
RailsApiDoc::Controller::Response::RablCompiler.new("#{ctrl.controller_path}/#{action}").compile_source
|
13
|
+
end
|
14
|
+
|
15
|
+
def action_route(ctrl, action)
|
16
|
+
return unless action_route = action_route_for(ctrl, action)
|
17
|
+
|
18
|
+
{
|
19
|
+
method: method(action_route),
|
20
|
+
url: url(action_route)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def method(route)
|
27
|
+
route.instance_variable_get(:@request_method_match)&.first&.name&.split('::')&.last
|
28
|
+
end
|
29
|
+
|
30
|
+
def url(a_route)
|
31
|
+
a_route.path.spec.to_s.gsub(/(\(.*\))/, '')
|
32
|
+
end
|
33
|
+
|
34
|
+
def action_route_for(ctrl, action)
|
35
|
+
@repo[ctrl][:routes].detect { |r| r.defaults[:action] == action }
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
# :nodoc:
|
4
|
+
class RailsApiDoc::Model::AttributeMerger
|
5
|
+
|
6
|
+
MODEL = RailsApiDoc::ApiDatum
|
7
|
+
MERGABLE_FIELDS = [:type, :desc, :action_type].freeze
|
8
|
+
|
9
|
+
#
|
10
|
+
# do not mutate attributes
|
11
|
+
#
|
12
|
+
# please - impossible
|
13
|
+
#
|
14
|
+
def initialize(attributes)
|
15
|
+
@attrs = attributes
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(api_type:)
|
19
|
+
@api_type = api_type
|
20
|
+
|
21
|
+
api_params = MODEL.where(api_type: api_type).all.each do |param|
|
22
|
+
_add_nested_param(param)
|
23
|
+
end
|
24
|
+
|
25
|
+
@attrs
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def _add_nested_param(param)
|
31
|
+
# nesting should be present for parameter to appear
|
32
|
+
return if param.nesting.blank?
|
33
|
+
|
34
|
+
attrs, nesting, name = _parse_settings(param)
|
35
|
+
|
36
|
+
return unless name
|
37
|
+
|
38
|
+
ctrl_param = _find_param(nesting, name, attrs)
|
39
|
+
|
40
|
+
ctrl_param.param = param
|
41
|
+
# _merge_data_to_param(ctrl_param, param)
|
42
|
+
end
|
43
|
+
|
44
|
+
def _parse_settings(param)
|
45
|
+
ctrl = param.nesting[0].constantize
|
46
|
+
nesting = param.nesting[1..-1]
|
47
|
+
name = param.name&.to_sym
|
48
|
+
attrs = @attrs[ctrl]
|
49
|
+
|
50
|
+
#
|
51
|
+
# making exception
|
52
|
+
# until request parameters are not split by actions
|
53
|
+
#
|
54
|
+
attrs = attrs[param.api_action] if @api_type == 'response'
|
55
|
+
|
56
|
+
[attrs, nesting, name]
|
57
|
+
end
|
58
|
+
|
59
|
+
def _merge_data_to_param(ctrl_param, param)
|
60
|
+
MERGABLE_FIELDS.each do |field|
|
61
|
+
next if ctrl_param_value = ctrl_param.public_send(field) # do not override data set in api ctrl
|
62
|
+
|
63
|
+
param_value = param.public_send(field)
|
64
|
+
next if param_value.blank?
|
65
|
+
|
66
|
+
ctrl_param.public_send("#{field}=", param_value)
|
67
|
+
ctrl_param.add_updated_field(field)
|
68
|
+
end
|
69
|
+
|
70
|
+
_merge_special(ctrl_param, param)
|
71
|
+
end
|
72
|
+
|
73
|
+
def _merge_special(ctrl_param, param)
|
74
|
+
return if param.special.blank?
|
75
|
+
|
76
|
+
if ctrl_param.enum? && ctrl_param.enum.blank?
|
77
|
+
ctrl_param.enum = _parse_enum(param.special)
|
78
|
+
ctrl_param.add_updated_field('enum')
|
79
|
+
elsif ctrl_param.nested? && ctrl_param.model.blank?
|
80
|
+
ctrl_param.model = special.capitalize
|
81
|
+
ctrl_param.add_updated_field('model')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def _find_param(nesting, name, attrs)
|
86
|
+
nesting.each do |model|
|
87
|
+
nested_attrs = attrs.each_value.detect { |v| v.nested? && v.model == model }
|
88
|
+
|
89
|
+
attrs = nested_attrs ? nested_attrs : _define_nesting_level(attrs, name, model)
|
90
|
+
end
|
91
|
+
|
92
|
+
if attrs[name]
|
93
|
+
attrs[name]
|
94
|
+
else
|
95
|
+
attrs[name] = _init_param_for_api_type(name)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def _define_nesting_level(attrs, name, model)
|
100
|
+
attr = _init_param_for_api_type(name, type: :object, model: model, nested: {})
|
101
|
+
|
102
|
+
attrs[name] = attr
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# common attrs:
|
107
|
+
# 1. name
|
108
|
+
# 2. type
|
109
|
+
# 3. desc
|
110
|
+
# 4. model
|
111
|
+
# 5. id
|
112
|
+
# 6. nested
|
113
|
+
#
|
114
|
+
# request attrs:
|
115
|
+
# 1. enum
|
116
|
+
# 2. value
|
117
|
+
# 3. required
|
118
|
+
#
|
119
|
+
# response attrs:
|
120
|
+
# 1. attr
|
121
|
+
#
|
122
|
+
def _init_param_for_api_type(name, options)
|
123
|
+
if @api_type == 'request'
|
124
|
+
RailsApiDoc::Controller::Request::Param.new(name, options, is_new: true)
|
125
|
+
elsif @api_type == 'response'
|
126
|
+
RailsApiDoc::Controller::Response::Param.new(name, name, options[:nested], options[:model], '', options[:type], is_new: true)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -5,16 +5,20 @@ class RailsApiDoc::Model::AttributeParser
|
|
5
5
|
|
6
6
|
# TODO : Change to I18n. Added on: 08.10.16. Added by: <@vshaveyko>
|
7
7
|
WRONG_NAME_ERROR_STRING = 'Name should consist only of letters\ciphers\underscores'
|
8
|
+
WRONG_TYPE_ERROR_STRING = 'Wrong type saved'
|
8
9
|
|
9
10
|
class << self
|
10
11
|
|
11
12
|
def parse_attributes(params)
|
12
|
-
type = :enum if params[:enum].present?
|
13
|
-
|
14
13
|
{
|
15
14
|
name: parse_name(params[:name]),
|
16
|
-
type:
|
17
|
-
|
15
|
+
type: parse_type(params[:type]),
|
16
|
+
desc: params[:desc],
|
17
|
+
special: parse_special(params[:type], params[:special]),
|
18
|
+
action_type: params[:action],
|
19
|
+
nesting: params[:nesting],
|
20
|
+
api_type: params[:api_type],
|
21
|
+
id: params[:id]
|
18
22
|
}.compact
|
19
23
|
end
|
20
24
|
|
@@ -46,9 +50,23 @@ class RailsApiDoc::Model::AttributeParser
|
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
53
|
+
def parse_special(type, special)
|
54
|
+
return unless special.present? && type
|
55
|
+
|
56
|
+
if type == :enum
|
57
|
+
parse_enum(special) # parse as enum array value
|
58
|
+
elsif RailsApiDoc::NESTED_TYPES.include?(type.to_sym)
|
59
|
+
special.capitalize # parse as model name
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
49
63
|
def parse_type(type)
|
50
64
|
return if type.blank?
|
51
|
-
|
65
|
+
|
66
|
+
type = type.to_sym
|
67
|
+
raise NameError, WRONG_TYPE_ERROR_STRING unless type.in?(RailsApiDoc::ACCEPTED_TYPES)
|
68
|
+
|
69
|
+
type
|
52
70
|
end
|
53
71
|
|
54
72
|
end
|