request_params_validation 0.1.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.
Files changed (31) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +557 -0
  4. data/Rakefile +32 -0
  5. data/lib/request_params_validation.rb +346 -0
  6. data/lib/request_params_validation/definitions.rb +57 -0
  7. data/lib/request_params_validation/definitions/action.rb +23 -0
  8. data/lib/request_params_validation/definitions/param.rb +182 -0
  9. data/lib/request_params_validation/definitions/request.rb +31 -0
  10. data/lib/request_params_validation/definitions/resource.rb +30 -0
  11. data/lib/request_params_validation/engine.rb +18 -0
  12. data/lib/request_params_validation/exceptions/base_errors.rb +10 -0
  13. data/lib/request_params_validation/exceptions/definitions_errors.rb +31 -0
  14. data/lib/request_params_validation/exceptions/validator_errors.rb +49 -0
  15. data/lib/request_params_validation/handler.rb +26 -0
  16. data/lib/request_params_validation/helpers.rb +17 -0
  17. data/lib/request_params_validation/params.rb +60 -0
  18. data/lib/request_params_validation/params/constants.rb +22 -0
  19. data/lib/request_params_validation/params/converter.rb +33 -0
  20. data/lib/request_params_validation/params/types/conversions.rb +41 -0
  21. data/lib/request_params_validation/params/types/validations.rb +57 -0
  22. data/lib/request_params_validation/params/validator.rb +74 -0
  23. data/lib/request_params_validation/params/validators/custom.rb +18 -0
  24. data/lib/request_params_validation/params/validators/format.rb +27 -0
  25. data/lib/request_params_validation/params/validators/inclusion.rb +27 -0
  26. data/lib/request_params_validation/params/validators/length.rb +37 -0
  27. data/lib/request_params_validation/params/validators/presence.rb +13 -0
  28. data/lib/request_params_validation/params/validators/type.rb +59 -0
  29. data/lib/request_params_validation/params/validators/value.rb +37 -0
  30. data/lib/request_params_validation/version.rb +3 -0
  31. metadata +87 -0
@@ -0,0 +1,182 @@
1
+ require 'request_params_validation/params'
2
+ require 'request_params_validation/exceptions/definitions_errors'
3
+
4
+ module RequestParamsValidation
5
+ module Definitions
6
+ class Param
7
+ attr_reader :key, :required, :allow_blank, :type, :transform, :decimal_precision,
8
+ :inclusion, :length, :value, :format, :custom_validation, :elements
9
+
10
+ def initialize(options, &block)
11
+ @key = options[:key]
12
+ @required = options[:required]
13
+ @allow_blank = options[:allow_blank]
14
+ @type = options[:type].try(:to_sym)
15
+ @default = options[:default]
16
+
17
+ @transform = options[:transform]
18
+ @decimal_precision = options[:precision]
19
+ @element_of_array = options[:element_of_array]
20
+
21
+ @inclusion = build_inclusion_option(options[:inclusion])
22
+ @length = build_length_option(options[:length])
23
+ @value = build_value_option(options[:value])
24
+ @format = build_format_option(options[:format])
25
+ @custom_validation = build_custom_validation_option(options[:validate])
26
+
27
+ @elements = build_elements_option(options[:elements], &block)
28
+ @sub_definition = build_sub_definition(&block)
29
+ end
30
+
31
+ def has_default?
32
+ !@default.nil? # default value could be `false`
33
+ end
34
+
35
+ def default
36
+ @default.respond_to?(:call) ? @default.call : @default
37
+ end
38
+
39
+ def sub_definition
40
+ @sub_definition || @elements.try(:sub_definition)
41
+ end
42
+
43
+ def element_of_array?
44
+ !!@element_of_array
45
+ end
46
+
47
+ def validate_presence?
48
+ !!@required
49
+ end
50
+
51
+ def validate_type?
52
+ !!@type
53
+ end
54
+
55
+ def validate_inclusion?
56
+ !!@inclusion
57
+ end
58
+
59
+ def validate_length?
60
+ !!@length
61
+ end
62
+
63
+ def validate_value?
64
+ !!@value
65
+ end
66
+
67
+ def validate_format?
68
+ return false if [Params::DATE_TYPE, Params::DATETIME_TYPE].include?(@type)
69
+
70
+ !!@format
71
+ end
72
+
73
+ def validate_custom_validation?
74
+ !!@custom_validation
75
+ end
76
+
77
+ private
78
+
79
+ def build_inclusion_option(inclusion)
80
+ case inclusion
81
+ when Array
82
+ include_in = inclusion
83
+ when Hash
84
+ include_in = inclusion[:in]
85
+ message = inclusion[:message]
86
+ end
87
+
88
+ return unless include_in
89
+
90
+ Struct.new(:in, :message).new(include_in, message)
91
+ end
92
+
93
+ def build_length_option(length)
94
+ case length
95
+ when Integer
96
+ min = length
97
+ max = length
98
+ when Hash
99
+ min = length[:min]
100
+ max = length[:max]
101
+ message = length[:message]
102
+ end
103
+
104
+ return unless min || max
105
+
106
+ Struct.new(:min, :max, :message).new(min, max, message)
107
+ end
108
+
109
+ def build_value_option(value)
110
+ case value
111
+ when Hash
112
+ min = value[:min]
113
+ max = value[:max]
114
+ message = value[:message]
115
+ end
116
+
117
+ return unless min || max
118
+
119
+ Struct.new(:min, :max, :message).new(min, max, message)
120
+ end
121
+
122
+ def build_format_option(format)
123
+ case format
124
+ when Regexp
125
+ regexp = format
126
+ when String
127
+ strptime = format
128
+ when Hash
129
+ strptime = format[:strptime]
130
+ regexp = format[:regexp]
131
+ message = format[:message]
132
+ end
133
+
134
+ return if regexp.nil? && !strptime
135
+
136
+ Struct.new(:regexp, :strptime, :message).new(regexp, strptime, message)
137
+ end
138
+
139
+ def build_custom_validation_option(validation)
140
+ case validation
141
+ when Proc
142
+ function = validation
143
+ when Hash
144
+ function = validation[:function]
145
+ message = validation[:message]
146
+ end
147
+
148
+ return unless function
149
+
150
+ Struct.new(:function, :message).new(function, message)
151
+ end
152
+
153
+ def build_elements_option(elements, &block)
154
+ return unless @type == Params::ARRAY_TYPE
155
+
156
+ elements_options = {
157
+ key: @key,
158
+ element_of_array: true
159
+ }
160
+
161
+ case elements
162
+ when Hash
163
+ elements_options.merge!(elements)
164
+ when String, Symbol
165
+ elements_options.merge!(type: elements)
166
+ end
167
+
168
+ self.class.new(elements_options, &block)
169
+ end
170
+
171
+ def build_sub_definition(&block)
172
+ return unless @type == Params::HASH_TYPE
173
+
174
+ request = Request.new
175
+
176
+ block.call(request) if block_given?
177
+
178
+ request.params
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,31 @@
1
+ require 'request_params_validation/definitions/param'
2
+
3
+ module RequestParamsValidation
4
+ module Definitions
5
+ class Request
6
+ attr_reader :params
7
+
8
+ def initialize
9
+ @params = []
10
+ end
11
+
12
+ def required(param_name, options = {}, &block)
13
+ options = options.merge({ required: true })
14
+ add_parameter(param_name, options, &block)
15
+ end
16
+
17
+ def optional(param_name, options = {}, &block)
18
+ options = options.merge({ required: false })
19
+ add_parameter(param_name, options, &block)
20
+ end
21
+
22
+ private
23
+
24
+ def add_parameter(name, options, &block)
25
+ options = options.merge({ key: name })
26
+
27
+ @params << Param.new(options, &block)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ require 'request_params_validation/definitions/action'
2
+ require 'request_params_validation/exceptions/definitions_errors'
3
+
4
+ module RequestParamsValidation
5
+ module Definitions
6
+ class Resource
7
+ attr_reader :name, :actions
8
+
9
+ def initialize(name)
10
+ @name = name
11
+ @actions = {}
12
+ end
13
+
14
+ def action(action_name)
15
+ unless block_given?
16
+ raise DefinitionArgumentError.new("Expecting block for action '#{action_name}'")
17
+ end
18
+
19
+ action_definition = Action.new(action_name.to_s)
20
+
21
+ yield action_definition
22
+
23
+ @actions[action_name.to_s] = action_definition
24
+ rescue DefinitionArgumentError => e
25
+ e.resource = name
26
+ raise
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ module RequestParamsValidation
2
+ autoload :Definitions, 'request_params_validation/definitions'
3
+ autoload :Helpers, 'request_params_validation/helpers'
4
+
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace RequestParamsValidation
7
+
8
+ initializer 'request_params_validation.load_definitions' do
9
+ RequestParamsValidation::Definitions.load_all
10
+ end
11
+
12
+ initializer 'request_params_validation.add_helpers' do
13
+ ActiveSupport.on_load :action_controller do
14
+ include RequestParamsValidation::Helpers
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ module RequestParamsValidation
2
+ class GeneralError < StandardError
3
+ end
4
+
5
+ class RequestParamError < GeneralError
6
+ end
7
+
8
+ class DefinitionsError < GeneralError
9
+ end
10
+ end
@@ -0,0 +1,31 @@
1
+ require 'request_params_validation/exceptions/base_errors'
2
+
3
+ module RequestParamsValidation
4
+ class DefinitionNotFoundError < DefinitionsError
5
+ def initialize(resource, action)
6
+ msg = "The request definition for the resource '#{resource}' and action '#{action}' " \
7
+ "couldn't be found"
8
+
9
+ super(msg)
10
+ end
11
+ end
12
+
13
+ class DefinitionArgumentError < DefinitionsError
14
+ attr_accessor :resource, :action
15
+
16
+ def initialize(error_msg, options = {})
17
+ @error_msg = error_msg
18
+ @resource = options[:resource]
19
+
20
+ super(message)
21
+ end
22
+
23
+ def message
24
+ if resource
25
+ "Argument error for resource '#{resource}'. #{@error_msg}"
26
+ else
27
+ @error_msg
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ require 'request_params_validation/exceptions/base_errors'
2
+
3
+ module RequestParamsValidation
4
+ class MissingParameterError < RequestParamError
5
+ attr_reader :param_key, :param_type
6
+
7
+ def initialize(options)
8
+ @param_key = options[:param_key]
9
+ @param_type = options[:param_type]
10
+
11
+ super(message)
12
+ end
13
+
14
+ def message
15
+ "The parameter '#{param_key}' is missing"
16
+ end
17
+ end
18
+
19
+ class InvalidParameterValueError < RequestParamError
20
+ attr_reader :param_key, :param_value, :param_type, :details
21
+
22
+ def initialize(options)
23
+ @param_key = options[:param_key]
24
+ @param_value = options[:param_value]
25
+ @param_type = options[:param_type]
26
+ @details = options[:details]
27
+
28
+ super(message)
29
+ end
30
+
31
+ def message
32
+ message = "The value for the parameter '#{param_key}' is invalid"
33
+
34
+ if details.present?
35
+ "#{message}. #{details}"
36
+ else
37
+ message
38
+ end
39
+ end
40
+ end
41
+
42
+ class UnsupportedTypeError < GeneralError
43
+ def initialize(options)
44
+ msg = "Unsupported type '#{options[:param_type]}' for the parameter '#{options[:param_key]}'"
45
+
46
+ super(msg)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,26 @@
1
+ require 'request_params_validation/definitions'
2
+ require 'request_params_validation/params'
3
+ require 'request_params_validation/exceptions/definitions_errors'
4
+
5
+ module RequestParamsValidation
6
+ module Handler
7
+ def self.handle_request_params(resource, action, params)
8
+ request_definition = Definitions.get_request(resource, action)
9
+
10
+ unless request_definition
11
+ case RequestParamsValidation.on_definition_not_found
12
+ when :raise
13
+ raise DefinitionNotFoundError.new(resource, action)
14
+ else
15
+ return
16
+ end
17
+ end
18
+
19
+ RequestParamsValidation.remove_keys_from_params.each { |key| params.delete(key) }
20
+
21
+ Params.validate!(request_definition.params, params)
22
+
23
+ Params.filter!(request_definition.params, params) if RequestParamsValidation.filter_params
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ require 'request_params_validation/handler'
2
+
3
+ module RequestParamsValidation
4
+ module Helpers
5
+ define_method RequestParamsValidation.helper_method_name do
6
+ resource = params[:controller]
7
+ action = params[:action]
8
+
9
+ if RequestParamsValidation.save_original_params
10
+ original_params = params.deep_dup
11
+ instance_variable_set(RequestParamsValidation.save_original_params, original_params)
12
+ end
13
+
14
+ Handler.handle_request_params(resource, action, params)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,60 @@
1
+ require 'request_params_validation/params/constants'
2
+ require 'request_params_validation/params/validator'
3
+
4
+ module RequestParamsValidation
5
+ module Params
6
+ include Constants
7
+
8
+ def self.validate!(definition, params)
9
+ definition.each do |param_definition|
10
+ validate_and_coerce_param(param_definition, params)
11
+ end
12
+
13
+ params
14
+ end
15
+
16
+ def self.filter!(definition, params)
17
+ extra_keys = [:controller, :action] # Keys added by Rails
18
+
19
+ filter_params(definition, params, extra_keys).tap do |params|
20
+ params.permit! if params.respond_to?(:permit!)
21
+ end
22
+ end
23
+
24
+ def self.validate_and_coerce_param(param_definition, params)
25
+ key = param_definition.key
26
+ value = params[key]
27
+
28
+ value = Validator.new(param_definition, value).validate_and_coerce
29
+
30
+ params[key] = value
31
+ end
32
+ private_class_method :validate_and_coerce_param
33
+
34
+ def self.filter_params(definition, params, extra_keys = [])
35
+ return unless params
36
+ return params if definition.empty?
37
+
38
+ params_keys = definition.map do |param_definition|
39
+ key = param_definition.key
40
+
41
+ if param_definition.sub_definition
42
+ filter_params(param_definition.sub_definition, params[key])
43
+ end
44
+
45
+ key
46
+ end.compact
47
+
48
+ params_keys += extra_keys
49
+
50
+ if params.is_a?(Array)
51
+ params.map { |param| param.slice!(*params_keys) }
52
+ else
53
+ params.slice!(*params_keys)
54
+ end
55
+
56
+ params
57
+ end
58
+ private_class_method :filter_params
59
+ end
60
+ end