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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -8
  3. data/app/assets/javascripts/table.js.coffee +10 -1
  4. data/app/assets/stylesheets/rails_api_doc/default.sass +39 -16
  5. data/app/assets/stylesheets/rails_api_doc/table.sass +21 -4
  6. data/app/controllers/rails_api_doc/api_docs_controller.rb +17 -20
  7. data/app/helpers/rails_api_doc/application_helper.rb +11 -0
  8. data/app/models/rails_api_doc/api_datum.rb +17 -0
  9. data/app/views/layouts/rails_api_doc/application.slim +2 -5
  10. data/app/views/rails_api_doc/api_docs/_request_api_table.slim +1 -1
  11. data/app/views/rails_api_doc/api_docs/_response_api_table.slim +4 -3
  12. data/app/views/rails_api_doc/api_docs/{index.slim → show.slim} +0 -0
  13. data/app/views/shared/_param_inputs.slim +2 -3
  14. data/app/views/shared/_table.slim +8 -6
  15. data/app/views/shared/_title.slim +8 -0
  16. data/config/routes.rb +1 -1
  17. data/lib/generators/rails_api_doc/templates/api_datum_migration.rb +4 -1
  18. data/lib/rails_api_doc.rb +16 -1
  19. data/lib/rails_api_doc/controller/headers.rb +59 -0
  20. data/lib/rails_api_doc/controller/param.rb +113 -0
  21. data/lib/rails_api_doc/controller/repo.rb +35 -0
  22. data/lib/rails_api_doc/controller/request/dsl.rb +1 -3
  23. data/lib/rails_api_doc/controller/request/factory.rb +29 -0
  24. data/lib/rails_api_doc/controller/request/headers.rb +11 -42
  25. data/lib/rails_api_doc/controller/request/param.rb +26 -33
  26. data/lib/rails_api_doc/controller/request/repository.rb +5 -26
  27. data/lib/rails_api_doc/controller/response/factory.rb +20 -2
  28. data/lib/rails_api_doc/controller/response/headers.rb +10 -21
  29. data/lib/rails_api_doc/controller/response/param.rb +38 -1
  30. data/lib/rails_api_doc/controller/response/rabl.rb +8 -19
  31. data/lib/rails_api_doc/controller/response/rabl_compiler.rb +7 -9
  32. data/lib/rails_api_doc/controller/response/repository.rb +38 -0
  33. data/lib/rails_api_doc/model/attribute_merger.rb +130 -0
  34. data/lib/rails_api_doc/model/attribute_parser.rb +23 -5
  35. data/lib/rails_api_doc/version.rb +1 -1
  36. 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
- ::RailsApiDoc::Controller::Response::Rabl.new(controllers)
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::Repository.registered_controllers
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
- RESPONSE_HEADERS = {
5
- 'Parameter' => {
6
- value: -> (row_name, row_values) { row_name },
7
- fill_type: :input,
8
- param: :name
9
- },
10
- 'Value' => {
11
- value: -> (row_name, row_values) {
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
- class Param < Struct.new(:name, :attr, :nested, :model, :desc)
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 :map
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
- def load_template(ctrl, action)
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
- def action_route(ctrl, action)
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, map|
44
- map[ctrl] = ctrl_actions(ctrl)
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
- # return unless block_given?
163
- # yield
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
- # return unless block_given?
185
- # template = sub_compile(nil, true) { yield }
186
- # @attributes.extends template
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?(['merge', 'condtiion'])
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: type || parse_type(params[:type]),
17
- enum: parse_enum(params[:enum])
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
- type.constantize
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  # author: Vadim Shaveiko <@vshaveyko>
3
3
  module RailsApiDoc
4
- VERSION = '0.2.3'
4
+ VERSION = '0.3.0'
5
5
  end