rails_api_documentation 0.1.9 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4c67c2cdc12a310b1972630df13fa2a7dbab76e5
4
- data.tar.gz: cd2a6c178374ad1a8e9e9870fca0efe7f00c0796
3
+ metadata.gz: 2eafaedd7e6a3665cb3f17763d5204a80ec35e84
4
+ data.tar.gz: 3b09eb7ddd55e8300a60a0a4514084c8b03a2496
5
5
  SHA512:
6
- metadata.gz: 00397485504bad53e5ea83f3414165d59a9328375461cfb78355d650ced0e2615be6f7aaf4a4691686a059885f8eacb3e16642f36e00f2473139364675a391b8
7
- data.tar.gz: e533be586b8eb26d39ce266ecccb45d37e911e20afda1f8d6eaf058d6108bc50ae48398d238611bf2fa8440879c71aaea7ebacd4b5225b2b489ebd1c707232c1
6
+ metadata.gz: 8aff4ed365764127675f1150047a0ef21f05da820834a77a5459607bb0c7319b60123643f24398cd9003bda449a304ce8de2b094420a51409d321c347970ef17
7
+ data.tar.gz: 4d095bb8a1ea783f09820189c7c96544d21e1adf57b6d8a8065b24701fdef752e7cc9461cc755aec682c6fe7849cf29621157580fde4a39c86e149208c5590e6
data/Gemfile CHANGED
@@ -21,6 +21,7 @@ gem 'therubyrhino', '2.0.4', platforms: :jruby
21
21
 
22
22
  gem 'pry'
23
23
  gem 'pry-stack_explorer'
24
+ gem 'pry-rescue'
24
25
 
25
26
  group :development, :test do
26
27
  gem 'rspec-rails', '~> 3.5'
data/README.md CHANGED
@@ -17,6 +17,13 @@ Or install it yourself as:
17
17
 
18
18
  $ gem install rails_api_doc
19
19
 
20
+ ## Features
21
+
22
+ + displaying application api if used in one of correct ways
23
+ ![alt tag](https://raw.githubusercontent.com/vshaveyko/rails_api_doc/master/preview.png)
24
+ + Integration with Rabl if it is bundled
25
+ + ```resource_params``` method that will filter incoming params for you
26
+
20
27
  ## Usage
21
28
 
22
29
  To display api documentation on route '/api_doc' you need to:
@@ -49,12 +56,89 @@ To display api documentation on route '/api_doc' you need to:
49
56
  end
50
57
  ```
51
58
 
52
- Parameter type may be one of these:
59
+ ## Strong params
60
+
61
+ You may want to use your defined request api to filter incoming parameters.
62
+ Usually we use something like `params.permit(:name, :age)`, but no more!
63
+ With this gem bundled you can do this:
53
64
 
54
65
  ```ruby
55
- ACCEPTED_TYPES = [Bool, String, Integer, Object, Array, DateTime, :enum, :model].freeze
66
+
67
+ parameter :body, type: :string
68
+ parameter :title, type: :string
69
+
70
+ # controller action
71
+ def create
72
+ Comment.create!(resource_params)
73
+ end
74
+
75
+ ```
76
+
77
+ and if request is `POST '/comments', params: { body: 'Comment body', title: 'Comment title', age: 34 }`
78
+
79
+ Comment will be created with: `Comment(body='Comment body', title='Comment title', age=nil)`
80
+
81
+ ## Types
82
+
83
+ Parameter type may be one of these:
84
+
85
+ ```ruby
86
+
87
+ # Non nested
88
+ :bool - Boolean type, accepts true, false, 'true', 'false'
89
+ :string - will accept anything beside nested type
90
+ :integer - accepts numbers as string value, and usual numbers
91
+ :array - array of atomic values (integer, strings, etc)
92
+ :datetime - string with some datetime representation accepted by DateTime.parse
93
+ :enum - one of predefined values of enum: option (only atomic types)
94
+
95
+ # nested
96
+ :object - usual nested type. comes very handy with rails nested_attributes feature
97
+ :ary_object - array of :object type, rails nested_attributes on has_many
98
+
56
99
  ```
57
100
 
101
+ ## TODO's
102
+ + type for id reference with model field to display associated model and CONTROLLER in params for linking
103
+
104
+ + native DSL for defining response
105
+ ```ruby
106
+ action :show do
107
+ response :age, type: Integer
108
+ response :name, type String
109
+ response :data, type: :model, model: Datum do
110
+ response :creation_date, type: DateTime
111
+ response :comment, type: String
112
+ end
113
+ end
114
+ ```
115
+ + native DSL for defining scopes
116
+ ```ruby
117
+ scope :age, desc: 'Filters authors by given age'
118
+ ```
119
+ + dsl for extending response parameters
120
+ ```ruby
121
+ response :data, type: :model, model: Datum do
122
+ extends DataController, action: :show
123
+ end
124
+ ```
125
+ + dsl for extending request parameters
126
+ ```ruby
127
+ parameter :data, type: :model, model: Datum do
128
+ extends DataController, action: :show
129
+ end
130
+ ```
131
+ + ability to split request params to actions(low prior)
132
+ + CRUD for all parameters
133
+ + merging parameters from all sources before display
134
+ + pull everything that's possible to config
135
+
136
+ ## Development
137
+
138
+ + add specs for everything done
139
+ + inline documentation
140
+ + README FAQ on added functionality with examples
141
+
58
142
  ## License
59
143
 
60
144
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ # author: Vadim Shaveiko <@vshaveyko>
3
+ class RailsApiDoc::Config::ValidateAryObject
4
+
5
+ #
6
+ # @api_param_data - RailsApiDoc::Controller::Parameter::Repository::Param
7
+ # @controller_param - ActionController::Parameter
8
+ #
9
+ # check validation of current type by given data
10
+ #
11
+ # ary_object: check that parameter is array of parameters
12
+ #
13
+ def valid?(controller_param, api_param_data)
14
+ return true unless api_param_data.ary_object?
15
+
16
+ controller_param.is_a?(Array) && begin
17
+ controller_param.all? { |param| param.is_a?(::ActionController::Parameters) }
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ # author: Vadim Shaveiko <@vshaveyko>
3
+ # :nodoc:
4
+ class RailsApiDoc::Config::ValidateEnum
5
+
6
+ #
7
+ # @api_param_data - RailsApiDoc::Controller::Parameter::Repository::Param
8
+ # @controller_param - ActionController::Parameter
9
+ #
10
+ # check validation of current type by given data
11
+ #
12
+ # enum: check that enum array includes given value
13
+ #
14
+ def valid?(controller_param, api_param_data)
15
+ return true unless api_param_data.enum?
16
+
17
+ api_param_data[:enum].include?(controller_param)
18
+ end
19
+
20
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ # author: Vadim Shaveiko <@vshaveyko>
3
+ # :nodoc:
4
+ class RailsApiDoc::Config::ValidateType
5
+
6
+ #
7
+ # @api_param_data - RailsApiDoc::Controller::Parameter::Repository::Param
8
+ # @controller_param - ActionController::Parameter
9
+ #
10
+ # check validation of current type by given data
11
+ #
12
+ # type: check that param value is of requested type
13
+ # Examples:
14
+ #
15
+ # 1. controller_param == '1' , api_param_data[:type] == :integer > ok
16
+ # 2. controller_param == 'string' , api_param_data[:type] == :integer > not_ok
17
+ # 3. controller_param == {a: 'b'} , api_param_data[:type] == :object > ok
18
+ # 4. controller_param == [{a:'b'}, {c: 'd'}] , api_param_data[:type] == :ary_object > ok
19
+ # 5. controller_param == "2016-11-26 13:55:30 +0200"(or any other singature , use DateTime.parse to check) , api_param_data[:type] == :datetime > ok
20
+ # 6. controller_param == 'true' , api_param_data[:type] == :bool > ok
21
+ # 7. controller_param == true , api_param_data[:type] == :bool > ok
22
+ # 8. controller_param == 'ok' , api_param_data[:type] == :bool > not_ok
23
+ # 9. controller_param == ['1', '2', 3] , api_param_data[:type] == :array > ok
24
+ # 10. controller_param == [{a:'b'}] , api_param_data[:type] == :array > not_ok
25
+ #
26
+ # TODO: write rspec for this cases and implement
27
+ def valid?(_controller_param, _api_param_data)
28
+ true
29
+ end
30
+
31
+ end
@@ -1,14 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
  # author: Vadim Shaveiko <@vshaveyko>
3
+ require_relative 'validate_ary_object'
4
+ require_relative 'validate_enum'
5
+ require_relative 'validate_type'
6
+
3
7
  class RailsApiDoc::Config::Validator
4
8
 
5
- cattr_accessor :checkers
6
- self.checkers = []
9
+ class << self
10
+
11
+ attr_accessor :checkers
12
+
13
+ def add_checker(klass)
14
+ return if checkers.detect { |c| c.is_a?(klass) }
15
+
16
+ checkers << klass.new
17
+ end
7
18
 
8
- def self.valid_param?(controller_param, api_param_data)
9
- checkers.all? do |checker|
10
- checker.valid?(controller_param, api_param_data)
19
+ def remove_checker(klass)
20
+ checkers.delete_if { |c| c.is_a?(klass) }
11
21
  end
22
+
23
+ def valid_param?(controller_param, api_param_data)
24
+ checkers.all? do |checker|
25
+ checker.valid?(controller_param, api_param_data)
26
+ end
27
+ end
28
+
12
29
  end
13
30
 
31
+ self.checkers = [RailsApiDoc::Config::ValidateEnum.new, RailsApiDoc::Config::ValidateAryObject.new]
32
+
14
33
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ # author: Vadim Shaveiko <@vshaveyko>
3
+ # :nodoc:
4
+ class RailsApiDoc::Config
5
+
6
+ attr_accessor :check_params_type
7
+
8
+ def check_params_type=(value)
9
+ if value
10
+ Validator.add_checker(ValidateType)
11
+ else
12
+ Validator.remove_checker(ValidateType)
13
+ end
14
+ end
15
+
16
+ end
17
+
18
+ require_relative 'config/validator'
@@ -2,21 +2,26 @@
2
2
  # author: Vadim Shaveiko <@vshaveyko>
3
3
  class RailsApiDoc::Controller::Parameter::Repository::Param
4
4
 
5
- ACCEPTED_TYPES = [::Bool, String, Integer, Object, Array, DateTime, :enum, :model].freeze
5
+ NESTED_TYPES = [:ary_object, :object, :model, Object].freeze
6
+
7
+ STRAIGHT_TYPES = [:bool, :string, :integer, :array, :datetime, :enum, String, Object, Integer, Array, DateTime].freeze
8
+
9
+ ACCEPTED_TYPES = (NESTED_TYPES + STRAIGHT_TYPES).freeze
6
10
 
7
11
  # @type - type to check
8
12
  def self.accepted_nested_type?(type)
9
- type == Object || type == :model
13
+ type.in?(NESTED_TYPES)
10
14
  end
11
15
 
12
16
  def self.valid_type?(type)
13
- return if type.in?(ACCEPTED_TYPES)
17
+ return if type.nil? || type.in?(ACCEPTED_TYPES)
14
18
  raise ArgumentError, "Wrong type: #{type}. " \
15
19
  "Correct types are: #{ACCEPTED_TYPES}."
16
20
  end
17
21
 
18
- def self.valid_enum?(enum)
19
- return if enum.nil? || enum.is_a?(Array)
22
+ def self.valid_enum?(type, enum)
23
+ return false unless type == :enum
24
+ return if enum.is_a?(Array)
20
25
  raise ArgumentError, 'Enum must be an array.'
21
26
  end
22
27
 
@@ -31,16 +36,28 @@ class RailsApiDoc::Controller::Parameter::Repository::Param
31
36
  @store = store
32
37
  end
33
38
 
39
+ def enum?
40
+ @store[:type] == :enum && @store[:enum].present?
41
+ end
42
+
43
+ def ary_object?
44
+ @store[:type] == :ary_object
45
+ end
46
+
34
47
  def nested?
35
48
  self.class.accepted_nested_type?(@store[:type])
36
49
  end
37
50
 
51
+ def nesting
52
+ @store[:nested]
53
+ end
54
+
38
55
  def required?
39
56
  @store[:required]
40
57
  end
41
58
 
42
59
  def method_missing(name, *args)
43
- return @store.send(name, *args) if respond_to_missing?(name)
60
+ return @store.public_send(name, *args) if respond_to_missing?(name)
44
61
  super
45
62
  end
46
63
 
@@ -6,7 +6,7 @@ module RailsApiDoc::Controller::Parameter
6
6
 
7
7
  # Use parameter in controller to defined REQUEST parameter.
8
8
  # Adds it to repository: RailsApiDoc::Controller::Parameter::Repository
9
- def parameter(name, options, &block)
9
+ def parameter(name, options = {}, &block)
10
10
  raise ArgumentError, 'Parameter already defined.' if repo.key?(name)
11
11
 
12
12
  validate_options(options, block_given?)
@@ -17,17 +17,9 @@ module RailsApiDoc::Controller::Parameter
17
17
  private
18
18
 
19
19
  def validate_options(options, block_given)
20
- if options.nil? || options.empty?
21
- raise ArgumentError, 'Empty options passed.'
22
- end
23
-
24
20
  options.assert_valid_keys(VALID_KEYS)
25
21
 
26
22
  Repository::Param.valid_type?(options[:type])
27
-
28
- Repository::Param.valid_enum?(options[:enum])
29
-
30
- Repository::Param.valid_nested?(options[:type], block_given)
31
23
  end
32
24
 
33
25
  # default repo can be reassigned to deal with nested parameters
@@ -36,13 +28,18 @@ module RailsApiDoc::Controller::Parameter
36
28
  @repo || Repository[self]
37
29
  end
38
30
 
31
+ # adjust parameter values depending on parameter type
32
+ # 1. if nested - add nested values to parameter_data on :nested key
33
+ # 2. if enum - transform all values to_s
34
+ # bcs all incoming controller parameters will be strings and there can be errors
39
35
  def define_parameter(name, parameter_data, &block)
40
- repo[name] = \
41
- if Repository::Param.valid_nested?(parameter_data[:type], block_given?)
42
- Repository::Param.new(name, nested_parameter(parameter_data, &block))
43
- else
44
- Repository::Param.new(name, parameter_data)
45
- end
36
+ if Repository::Param.valid_nested?(parameter_data[:type], block_given?)
37
+ parameter_data = nested_parameter(parameter_data, &block)
38
+ elsif Repository::Param.valid_enum?(parameter_data[:type], parameter_data[:enum])
39
+ parameter_data[:enum].map!(:to_s)
40
+ end
41
+
42
+ repo[name] = Repository::Param.new(name, parameter_data)
46
43
  end
47
44
 
48
45
  def nested_parameter(parameter_data)
@@ -2,45 +2,70 @@
2
2
  # frozen_string_literal: true
3
3
  module RailsApiDoc::Controller::StrongParams
4
4
 
5
+ ParamIsRequired = Class.new(StandardError)
6
+
5
7
  def resource_params
6
8
  # accepted_params for permit
7
- accepted_params = [{}]
8
- loop_params(params, permitted_params, accepted_params)
9
- params.permit(accepted_params)
9
+ params.permit(params_to_permit)
10
10
  end
11
11
 
12
12
  private
13
13
 
14
- # loop through current level of params and add to permit level if
15
- # all requirements met
16
- # requirements are: 1) if required is set - param must be present and not empty
17
- # 2) if enum is set - param must equal predefined value
14
+ def params_to_permit
15
+ accepted_params = [{}]
16
+
17
+ loop_params(params, permitted_params, accepted_params)
18
+
19
+ accepted_params
20
+ end
21
+
22
+ # loop through current level of params and add to permit level if all requirements are met
23
+ #
24
+ # requirements are: 1) if required is set - param must be present and not empty => or error is raised
25
+ # 2) if enum is set - param must equal predefined value => see Config::ValidateEnum
26
+ # 3) if ary_object => see Config::ValidateAryObject
18
27
  # 3) if config.check_params_type is set - param must be of required type
28
+ #
19
29
  # @accepted_params = [{}] - array with last member hash for nesting
20
30
  # @level_params - current nesting level params
21
31
  # @level_permitted_params - data for params permission
22
- def loop_params(nested_controller_params, level_permitted_params, accepted_params)
32
+ def loop_params(params, level_permitted_params, accepted_params)
23
33
  level_permitted_params.each do |param_name, api_param_data|
24
- controller_param = nested_controller_params[param_name]
34
+ controller_param = params[param_name]
35
+
36
+ check_required(param_name, controller_param, api_param_data)
37
+ #
38
+ # no value present && not required => go next
39
+ next unless controller_param
25
40
 
26
41
  next unless RailsApiDoc::Config::Validator.valid_param?(controller_param, api_param_data)
27
42
 
28
- if api_param_data.nested?
29
- level_accepted_params = accepted_params.last[param_name] = [{}]
30
- next loop_params(controller_param, api_param_data, level_accepted_params)
43
+ # if settings is array
44
+ if api_param_data.ary_object?
45
+ controller_param.each do |single_controller_param|
46
+ _next_nesting_level(single_controller_param, param_data: api_param_data, current_accepted_params: accepted_params, param_name: param_name)
47
+ end
48
+ elsif api_param_data.nested?
49
+ _next_nesting_level(controller_param, param_data: api_param_data, current_accepted_params: accepted_params, param_name: param_name)
31
50
  else
32
51
  accepted_params.unshift(param_name)
33
52
  end
34
53
  end
35
54
  end
36
55
 
56
+ def _next_nesting_level(controller_param, param_data:, current_accepted_params:, param_name:)
57
+ level_accepted_params = current_accepted_params.last[param_name] = [{}]
58
+
59
+ loop_params(controller_param, param_data.nesting, level_accepted_params)
60
+ end
61
+
37
62
  def permitted_params
38
- Parameter::Repository[self]
63
+ ::RailsApiDoc::Controller::Parameter::Repository[self.class]
39
64
  end
40
65
 
41
- def check_required_ok?(param_data, param_config)
42
- return true unless param_config.required?
43
- !param_data.blank?
66
+ def check_required(param_name, param_data, param_config)
67
+ return unless param_config.required? && param_data.blank?
68
+ raise RailsApiDoc::Exception::ParamRequired, param_name
44
69
  end
45
70
 
46
71
  end
@@ -5,9 +5,12 @@ require 'action_view'
5
5
  require 'jquery-rails'
6
6
  require 'slim'
7
7
 
8
+ require_relative 'exception/param_required'
9
+
8
10
  require_relative 'controller'
9
11
  require_relative 'controller/strong_params'
10
12
  require_relative 'controller/attribute_parser'
13
+
11
14
  require_relative 'controller/parameter'
12
15
  require_relative 'controller/parameter/repository'
13
16
  require_relative 'controller/parameter/repository/param'
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ # author: Vadim Shaveiko <@vshaveyko>
3
+ # :nodoc:
4
+ module RailsApiDoc
5
+ module Exception
6
+ class ParamRequired < StandardError
7
+
8
+ def initialize(param_name)
9
+ @message = "#{param_name} is marked as required, but has no value."
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  # author: Vadim Shaveiko <@vshaveyko>
3
3
  module RailsApiDoc
4
- VERSION = '0.1.9'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/rails_api_doc.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # author: Vadim Shaveiko <@vshaveyko>
2
2
  # frozen_string_literal: true
3
- module RailsApiDoc
3
+ require 'rails_api_doc/config'
4
4
 
5
- extend ActiveSupport::Autoload
5
+ module RailsApiDoc
6
6
 
7
7
  class << self
8
8
 
@@ -11,7 +11,7 @@ module RailsApiDoc
11
11
  end
12
12
 
13
13
  def configuration
14
- @_configuration ||= Configuration.new
14
+ @_configuration ||= Config.new
15
15
  end
16
16
 
17
17
  def reset_configuration
@@ -20,19 +20,6 @@ module RailsApiDoc
20
20
 
21
21
  end
22
22
 
23
- autoload :Controller
24
-
25
- end
26
-
27
- # constants for defining in controllers
28
- # TODO: move to namespace ?
29
- class Bool
30
- end
31
-
32
- class Enum
33
- end
34
-
35
- class Nested
36
23
  end
37
24
 
38
25
  require 'rails_api_doc/version'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_api_documentation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - vs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-11-23 00:00:00.000000000 Z
11
+ date: 2016-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -158,8 +158,11 @@ files:
158
158
  - app/views/shared/_table.slim
159
159
  - config/routes.rb
160
160
  - lib/rails_api_doc.rb
161
+ - lib/rails_api_doc/config.rb
162
+ - lib/rails_api_doc/config/validate_ary_object.rb
163
+ - lib/rails_api_doc/config/validate_enum.rb
164
+ - lib/rails_api_doc/config/validate_type.rb
161
165
  - lib/rails_api_doc/config/validator.rb
162
- - lib/rails_api_doc/configuration.rb
163
166
  - lib/rails_api_doc/controller.rb
164
167
  - lib/rails_api_doc/controller/attribute_parser.rb
165
168
  - lib/rails_api_doc/controller/parameter.rb
@@ -170,6 +173,7 @@ files:
170
173
  - lib/rails_api_doc/controller/response_factory.rb
171
174
  - lib/rails_api_doc/controller/strong_params.rb
172
175
  - lib/rails_api_doc/engine.rb
176
+ - lib/rails_api_doc/exception/param_required.rb
173
177
  - lib/rails_api_doc/version.rb
174
178
  homepage: https://github.com/vshaveyko/rails_api_doc
175
179
  licenses:
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
- # author: Vadim Shaveiko <@vshaveyko>
3
- # :nodoc:
4
- class RailsApiDoc::Configuration
5
-
6
- def initialize
7
- end
8
-
9
- end