rails_simple_params 1.4.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.
- checksums.yaml +7 -0
- data/README.md +168 -0
- data/lib/rails_simple_params/coercion/array_param.rb +20 -0
- data/lib/rails_simple_params/coercion/big_decimal_param.rb +21 -0
- data/lib/rails_simple_params/coercion/boolean_param.rb +17 -0
- data/lib/rails_simple_params/coercion/float_param.rb +13 -0
- data/lib/rails_simple_params/coercion/hash_param.rb +20 -0
- data/lib/rails_simple_params/coercion/integer_param.rb +13 -0
- data/lib/rails_simple_params/coercion/string_param.rb +11 -0
- data/lib/rails_simple_params/coercion/time_param.rb +21 -0
- data/lib/rails_simple_params/coercion/virtual_param.rb +26 -0
- data/lib/rails_simple_params/coercion.rb +43 -0
- data/lib/rails_simple_params/invalid_parameter_error.rb +19 -0
- data/lib/rails_simple_params/param.rb +7 -0
- data/lib/rails_simple_params/param_evaluator.rb +95 -0
- data/lib/rails_simple_params/parameter.rb +39 -0
- data/lib/rails_simple_params/validator/blank.rb +26 -0
- data/lib/rails_simple_params/validator/custom.rb +15 -0
- data/lib/rails_simple_params/validator/format.rb +34 -0
- data/lib/rails_simple_params/validator/in.rb +22 -0
- data/lib/rails_simple_params/validator/is.rb +17 -0
- data/lib/rails_simple_params/validator/max.rb +17 -0
- data/lib/rails_simple_params/validator/max_length.rb +17 -0
- data/lib/rails_simple_params/validator/min.rb +17 -0
- data/lib/rails_simple_params/validator/min_length.rb +17 -0
- data/lib/rails_simple_params/validator/required.rb +17 -0
- data/lib/rails_simple_params/validator.rb +65 -0
- data/lib/rails_simple_params/version.rb +5 -0
- data/lib/rails_simple_params.rb +10 -0
- metadata +227 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f5d43833fa90c412a87b2c9fbb770b772fa4108bea779f48d26585b5f71cac96
|
4
|
+
data.tar.gz: 59780a29ba6d6e06cc69129d7c55ec0d7207213e00db452cfa547957b3df9f41
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e4dcec56bd8a60f7e86c66c44aee6e0f69e632fca2ab77c251421c68f254fbebf23c0d8eb909a2d014af9ca51a2348c24103f19613028c67a0d65522218a3ec6
|
7
|
+
data.tar.gz: 4d07862e2593a5ffb3567e7cf04203690186c5fba6aaab74bcc4c8b7d1d2f6c5cc8c5283990e2ee912fe7895b4b1c54b9e4d1eae5ba390f4ed62b375fc2c4c3e
|
data/README.md
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
# rails_simple_params
|
2
|
+
_Parameter Validation & Type Coercion for Rails_
|
3
|
+
|
4
|
+
## Introduction
|
5
|
+
|
6
|
+
This library is handy if you want to validate a few numbers of parameters
|
7
|
+
directly inside your controller.
|
8
|
+
|
9
|
+
For example: you are building a search action and want to validate that the
|
10
|
+
`sort` parameter is set and only set to something like `desc` or `asc`.
|
11
|
+
|
12
|
+
## Important
|
13
|
+
|
14
|
+
This library should not be used to validate a large number of parameters or
|
15
|
+
parameters sent via a form or namespaced (like `params[:user][:first_name]`).
|
16
|
+
There is already a great framework included in Rails (ActiveModel::Model) which
|
17
|
+
can be used to create virtual classes with all the validations you already know
|
18
|
+
and love from Rails. Remember to always try to stay in the “thin controller” rule.
|
19
|
+
|
20
|
+
[See an example on how to build a contact form using ActiveModel::Model.][active-model-example]
|
21
|
+
|
22
|
+
Sometimes it’s not practical to create an external class just to validate and
|
23
|
+
convert a few parameters. This gem allows you to easily validate and convert
|
24
|
+
parameters directly in your controller actions using a simple method call.
|
25
|
+
|
26
|
+
## Credits
|
27
|
+
|
28
|
+
This is a fork of the gem [rails_param][gem-rails-param], which was itself a
|
29
|
+
port of the gem [sinatra-param][gem-sinatra-param] for the Rails framework.
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
|
33
|
+
As usual, in your Gemfile...
|
34
|
+
|
35
|
+
``` ruby
|
36
|
+
gem 'rails_simple_params'
|
37
|
+
```
|
38
|
+
|
39
|
+
## Example
|
40
|
+
|
41
|
+
``` ruby
|
42
|
+
# GET /search?q=example
|
43
|
+
# GET /search?q=example&categories=news
|
44
|
+
# GET /search?q=example&sort=created_at&order=ASC
|
45
|
+
def search
|
46
|
+
param! :q, String, required: true
|
47
|
+
param! :categories, Array
|
48
|
+
param! :sort, String, default: 'title'
|
49
|
+
param! :order, String, in: %w(asc desc), transform: :downcase, default: 'asc'
|
50
|
+
param! :price, String, format: /[<\=>]\s*\$\d+/
|
51
|
+
|
52
|
+
# Access the parameters using the params object (e.g. `params[:q]`) as you usually do...
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
### Parameter Types
|
58
|
+
|
59
|
+
By declaring parameter types, incoming parameters will automatically be
|
60
|
+
transformed into an object of that type. For instance, if a param is `:boolean`,
|
61
|
+
values of `'1'`, `'true'`, `'t'`, `'yes'`, and `'y'` will be automatically
|
62
|
+
transformed into `true`. `BigDecimal` defaults to a precision of 14, and this
|
63
|
+
can be changed by passing in the optional `precision:` argument. Any `$` and `,`
|
64
|
+
are automatically stripped when converting to `BigDecimal`.
|
65
|
+
|
66
|
+
- `String`
|
67
|
+
- `Integer`
|
68
|
+
- `Float`
|
69
|
+
- `:boolean/TrueClass/FalseClass` _('1/0', 'true/false', 't/f', 'yes/no', 'y/n')_
|
70
|
+
- `Array` _('1,2,3,4,5')_
|
71
|
+
- `Hash` _('key1:value1,key2:value2')_
|
72
|
+
- `Date`, `Time`, & `DateTime`
|
73
|
+
- `BigDecimal` _('$1,000,000')_
|
74
|
+
|
75
|
+
### Validations
|
76
|
+
|
77
|
+
Encapsulate business logic in a consistent way with validations. If a parameter
|
78
|
+
does not satisfy a particular condition, an exception
|
79
|
+
(RailsSimpleParams::InvalidParameterError) is raised. You may use the
|
80
|
+
[rescue_from][method-rescue-from] method in your controller to catch this kind
|
81
|
+
of exception.
|
82
|
+
|
83
|
+
- `required`
|
84
|
+
- `blank`
|
85
|
+
- `is`
|
86
|
+
- `in`, `within`, `range`
|
87
|
+
- `min` / `max`
|
88
|
+
- `min_length` / `max_length`
|
89
|
+
- `format`
|
90
|
+
|
91
|
+
Customize exception message with option `:message`
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
param! :q, String, required: true, message: 'Query not specified'
|
95
|
+
```
|
96
|
+
|
97
|
+
### Defaults and Transformations
|
98
|
+
|
99
|
+
Passing a `default` option will provide a default value for a parameter if none
|
100
|
+
is passed. A `default` can be defined as either a default value or as a `Proc`:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
param! :attribution, String, default: "©"
|
104
|
+
param! :year, Integer, default: lambda { Time.now.year }
|
105
|
+
```
|
106
|
+
|
107
|
+
Use the `transform` option to take even more of the business logic of parameter
|
108
|
+
I/O out of your code. Anything that responds to `to_proc` (including `Proc` and
|
109
|
+
symbols) will do.
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
param! :order, String, in: ['ASC', 'DESC'], transform: :upcase, default: 'ASC'
|
113
|
+
param! :offset, Integer, min: 0, transform: lambda {|n| n - (n % 10)}
|
114
|
+
```
|
115
|
+
|
116
|
+
### Nested Attributes
|
117
|
+
|
118
|
+
rails_simple_params allows you to apply any of the above mentioned validations
|
119
|
+
to attributes nested in hashes:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
param! :book, Hash do |b|
|
123
|
+
b.param! :title, String, blank: false
|
124
|
+
b.param! :price, BigDecimal, precision: 4, required: true
|
125
|
+
b.param! :author, Hash, required: true do |a|
|
126
|
+
a.param! :first_name, String
|
127
|
+
a.param! :last_name, String, blank: false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
### Arrays
|
133
|
+
|
134
|
+
Validate every element of your array, including nested hashes and arrays:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
# primitive datatype syntax
|
138
|
+
param! :integer_array, Array do |array,index|
|
139
|
+
array.param! index, Integer, required: true
|
140
|
+
end
|
141
|
+
|
142
|
+
# complex array
|
143
|
+
param! :books_array, Array, required: true do |b|
|
144
|
+
b.param! :title, String, blank: false
|
145
|
+
b.param! :author, Hash, required: true do |a|
|
146
|
+
a.param! :first_name, String
|
147
|
+
a.param! :last_name, String, required: true
|
148
|
+
end
|
149
|
+
b.param! :subjects, Array do |s,i|
|
150
|
+
s.param! i, String, blank: false
|
151
|
+
end
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
## Many thanks to:
|
156
|
+
|
157
|
+
- [Nicolas Blanco](http://twitter.com/nblanco_fr)
|
158
|
+
- [Mattt Thompson (@mattt)](https://twitter.com/mattt)
|
159
|
+
- [Vincent Ollivier (@vinc686)](https://twitter.com/vinc686)
|
160
|
+
|
161
|
+
## License
|
162
|
+
|
163
|
+
rails_simple_params is available under the MIT license. See the LICENSE file for more info.
|
164
|
+
|
165
|
+
[active-model-example]: http://blog.remarkablelabs.com/2012/12/activemodel-model-rails-4-countdown-to-2013
|
166
|
+
[gem-rails-param]: https://github.com/nicolasblanco/rails_param
|
167
|
+
[gem-sinatra-param]: https://github.com/mattt/sinatra-param
|
168
|
+
[method-rescue-from]: http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html#method-i-rescue_from
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Coercion
|
5
|
+
class ArrayParam < VirtualParam
|
6
|
+
def coerce
|
7
|
+
return param if param.is_a?(Array)
|
8
|
+
|
9
|
+
Array(param.split(options[:delimiter] || ','))
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def argument_validation
|
15
|
+
raise ArgumentError unless type == Array
|
16
|
+
raise ArgumentError unless param.respond_to?(:split)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Coercion
|
5
|
+
class BigDecimalParam < VirtualParam
|
6
|
+
DEFAULT_PRECISION = 14
|
7
|
+
|
8
|
+
def coerce
|
9
|
+
return nil if param == '' # e.g. from an empty field in an HTML form
|
10
|
+
|
11
|
+
stripped_param = if param.is_a?(String)
|
12
|
+
param.delete('$,').strip.to_f
|
13
|
+
else
|
14
|
+
param
|
15
|
+
end
|
16
|
+
|
17
|
+
BigDecimal(stripped_param, options[:precision] || DEFAULT_PRECISION)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Coercion
|
5
|
+
class BooleanParam < VirtualParam
|
6
|
+
FALSEY = /^(false|f|no|n|0)$/i
|
7
|
+
TRUTHY = /^(true|t|yes|y|1)$/i
|
8
|
+
|
9
|
+
def coerce
|
10
|
+
return false if FALSEY === param.to_s
|
11
|
+
return true if TRUTHY === param.to_s
|
12
|
+
|
13
|
+
raise ArgumentError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Coercion
|
5
|
+
class HashParam < VirtualParam
|
6
|
+
def coerce
|
7
|
+
return param if param.is_a?(ActionController::Parameters)
|
8
|
+
raise ArgumentError unless param.respond_to?(:split)
|
9
|
+
|
10
|
+
Hash[param.split(options[:delimiter] || ',').map { |c| c.split(options[:separator] || ':') }] # rubocop:disable Style/HashConversion
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def argument_validation
|
16
|
+
raise ArgumentError unless type == Hash
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Coercion
|
5
|
+
class TimeParam < VirtualParam
|
6
|
+
def coerce
|
7
|
+
return nil if param == '' # e.g. from an empty field in an HTML form
|
8
|
+
|
9
|
+
return type.strptime(param, options[:format]) if options[:format].present?
|
10
|
+
|
11
|
+
type.parse(param)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def argument_validation
|
17
|
+
raise ArgumentError unless type.respond_to?(:parse)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Coercion
|
5
|
+
class VirtualParam
|
6
|
+
attr_reader :param, :options, :type
|
7
|
+
|
8
|
+
def initialize(param:, options: nil, type: nil)
|
9
|
+
@param = param
|
10
|
+
@options = options
|
11
|
+
@type = type
|
12
|
+
argument_validation
|
13
|
+
end
|
14
|
+
|
15
|
+
def coerce
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def argument_validation
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Coercion
|
5
|
+
attr_reader :coercion, :param
|
6
|
+
|
7
|
+
PARAM_TYPE_MAPPING = {
|
8
|
+
Integer => IntegerParam,
|
9
|
+
Float => FloatParam,
|
10
|
+
String => StringParam,
|
11
|
+
Array => ArrayParam,
|
12
|
+
Hash => HashParam,
|
13
|
+
BigDecimal => BigDecimalParam,
|
14
|
+
Date => TimeParam,
|
15
|
+
DateTime => TimeParam,
|
16
|
+
Time => TimeParam,
|
17
|
+
TrueClass => BooleanParam,
|
18
|
+
FalseClass => BooleanParam,
|
19
|
+
boolean: BooleanParam
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
TIME_TYPES = [Date, DateTime, Time].freeze
|
23
|
+
BOOLEAN_TYPES = [TrueClass, FalseClass, :boolean].freeze
|
24
|
+
|
25
|
+
def initialize(param, type, options)
|
26
|
+
@param = param
|
27
|
+
@coercion = klass_for(type).new(param: param, options: options, type: type)
|
28
|
+
end
|
29
|
+
|
30
|
+
def klass_for(type)
|
31
|
+
klass = PARAM_TYPE_MAPPING[type]
|
32
|
+
return klass if klass
|
33
|
+
|
34
|
+
raise TypeError
|
35
|
+
end
|
36
|
+
|
37
|
+
def coerce
|
38
|
+
return nil if param.nil?
|
39
|
+
|
40
|
+
coercion.coerce
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class InvalidParameterError < StandardError
|
5
|
+
attr_accessor :param, :options
|
6
|
+
|
7
|
+
def initialize(message, param: nil, options: {})
|
8
|
+
self.param = param
|
9
|
+
self.options = options
|
10
|
+
super(message)
|
11
|
+
end
|
12
|
+
|
13
|
+
def message
|
14
|
+
return options[:message] if options.is_a?(Hash) && options.key?(:message)
|
15
|
+
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class ParamEvaluator
|
5
|
+
attr_accessor :params
|
6
|
+
|
7
|
+
def initialize(params, context = nil)
|
8
|
+
@params = params
|
9
|
+
@context = context
|
10
|
+
end
|
11
|
+
|
12
|
+
def param!(name, type, options = {}, &) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
13
|
+
name = name.to_s unless name.is_a?(Integer)
|
14
|
+
return unless params.include?(name) || check_param_presence?(options[:default]) || options[:required]
|
15
|
+
|
16
|
+
parameter_name = @context ? "#{@context}[#{name}]" : name
|
17
|
+
coerced_value = coerce(parameter_name, params[name], type, options)
|
18
|
+
|
19
|
+
parameter = RailsSimpleParams::Parameter.new(
|
20
|
+
name: parameter_name,
|
21
|
+
value: coerced_value,
|
22
|
+
type: type,
|
23
|
+
options: options,
|
24
|
+
&
|
25
|
+
)
|
26
|
+
|
27
|
+
parameter.set_default if parameter.should_set_default?
|
28
|
+
|
29
|
+
# validate presence
|
30
|
+
if params[name].nil? && options[:required]
|
31
|
+
raise InvalidParameterError.new(
|
32
|
+
"Parameter #{parameter_name} is required",
|
33
|
+
param: parameter_name,
|
34
|
+
options: options
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
recurse_on_parameter(parameter, &) if block_given?
|
39
|
+
|
40
|
+
# apply transformation
|
41
|
+
parameter.transform if options[:transform]
|
42
|
+
|
43
|
+
# validate
|
44
|
+
validate!(parameter)
|
45
|
+
|
46
|
+
# set params value
|
47
|
+
params[name] = parameter.value
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def recurse_on_parameter(parameter, &) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
53
|
+
return if parameter.value.nil?
|
54
|
+
|
55
|
+
if parameter.type == Array
|
56
|
+
parameter.value.each_with_index do |element, i|
|
57
|
+
if element.is_a?(Hash) || element.is_a?(ActionController::Parameters)
|
58
|
+
recurse(element, "#{parameter.name}[#{i}]", &)
|
59
|
+
else
|
60
|
+
parameter.value[i] = recurse({ i => element }, parameter.name, i, &) # supply index as key unless value is hash
|
61
|
+
end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
recurse(parameter.value, parameter.name, &)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def recurse(element, context, index = nil)
|
69
|
+
raise InvalidParameterError, 'no block given' unless block_given?
|
70
|
+
|
71
|
+
yield(ParamEvaluator.new(element, context), index)
|
72
|
+
end
|
73
|
+
|
74
|
+
def check_param_presence?(param)
|
75
|
+
!param.nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
def coerce(param_name, param, type, options = {})
|
79
|
+
return nil if param.nil?
|
80
|
+
return param if begin
|
81
|
+
param.is_a?(type)
|
82
|
+
rescue StandardError
|
83
|
+
false
|
84
|
+
end
|
85
|
+
|
86
|
+
Coercion.new(param, type, options).coerce
|
87
|
+
rescue ArgumentError, TypeError
|
88
|
+
raise InvalidParameterError.new("'#{param}' is not a valid #{type}", param: param_name)
|
89
|
+
end
|
90
|
+
|
91
|
+
def validate!(param)
|
92
|
+
param.validate
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Parameter
|
5
|
+
attr_accessor :name, :value, :options, :type
|
6
|
+
|
7
|
+
TIME_TYPES = [Date, DateTime, Time].freeze
|
8
|
+
STRING_OR_TIME_TYPES = ([String] + TIME_TYPES).freeze
|
9
|
+
|
10
|
+
def initialize(name:, value:, options: {}, type: nil)
|
11
|
+
@name = name
|
12
|
+
@value = value
|
13
|
+
@options = options
|
14
|
+
@type = type
|
15
|
+
end
|
16
|
+
|
17
|
+
def should_set_default?
|
18
|
+
value.nil? && check_param_presence?(options[:default])
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_default
|
22
|
+
self.value = options[:default].respond_to?(:call) ? options[:default].call : options[:default]
|
23
|
+
end
|
24
|
+
|
25
|
+
def transform
|
26
|
+
self.value = options[:transform].to_proc.call(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate
|
30
|
+
Validator.new(self).validate!
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def check_param_presence?(param)
|
36
|
+
!param.nil?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Validator
|
5
|
+
class Blank < Validator
|
6
|
+
def valid_value?
|
7
|
+
return false if parameter.options[:blank]
|
8
|
+
|
9
|
+
case value
|
10
|
+
when String
|
11
|
+
(/\S/ === value) # rubocop:disable Style/CaseEquality
|
12
|
+
when Array, Hash, ActionController::Parameters
|
13
|
+
!value.empty?
|
14
|
+
else
|
15
|
+
!value.nil?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def error_message
|
22
|
+
"Parameter #{name} cannot be blank"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Validator
|
5
|
+
class Format < Validator
|
6
|
+
TIME_TYPES = [Date, DateTime, Time].freeze
|
7
|
+
STRING_OR_TIME_TYPES = ([String] + TIME_TYPES).freeze
|
8
|
+
|
9
|
+
def valid_value?
|
10
|
+
matches_time_types? || string_in_format?
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def error_message
|
16
|
+
return "Parameter #{name} must be a string if using the format validation" unless matches_string_or_time_types?
|
17
|
+
|
18
|
+
"Parameter #{name} must match format #{options[:format]}" unless string_in_format?
|
19
|
+
end
|
20
|
+
|
21
|
+
def matches_time_types?
|
22
|
+
TIME_TYPES.any? { |cls| value.is_a? cls }
|
23
|
+
end
|
24
|
+
|
25
|
+
def matches_string_or_time_types?
|
26
|
+
STRING_OR_TIME_TYPES.any? { |cls| value.is_a? cls }
|
27
|
+
end
|
28
|
+
|
29
|
+
def string_in_format?
|
30
|
+
value =~ options[:format] && value.is_a?(String)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Validator
|
5
|
+
class In < Validator
|
6
|
+
def valid_value?
|
7
|
+
value.nil? || case options[:in]
|
8
|
+
when Range
|
9
|
+
options[:in].include?(value)
|
10
|
+
else
|
11
|
+
Array(options[:in]).include?(value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def error_message
|
18
|
+
"Parameter #{parameter.name} must be within #{parameter.options[:in]}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Validator
|
5
|
+
class Is < Validator
|
6
|
+
def valid_value?
|
7
|
+
value === options[:is] # rubocop:disable Style/CaseEquality
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def error_message
|
13
|
+
"Parameter #{name} must be #{options[:is]}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Validator
|
5
|
+
class Max < Validator
|
6
|
+
def valid_value?
|
7
|
+
value.nil? || options[:max] >= value
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def error_message
|
13
|
+
"Parameter #{name} cannot be greater than #{options[:max]}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Validator
|
5
|
+
class MaxLength < Validator
|
6
|
+
def valid_value?
|
7
|
+
value.nil? || options[:max_length] >= value.length
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def error_message
|
13
|
+
"Parameter #{name} cannot have length greater than #{options[:max_length]}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Validator
|
5
|
+
class Min < Validator
|
6
|
+
def valid_value?
|
7
|
+
value.nil? || options[:min] <= value
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def error_message
|
13
|
+
"Parameter #{name} cannot be less than #{options[:min]}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Validator
|
5
|
+
class MinLength < Validator
|
6
|
+
def valid_value?
|
7
|
+
value.nil? || options[:min_length] <= value.length
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def error_message
|
13
|
+
"Parameter #{name} cannot have length less than #{options[:min_length]}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsSimpleParams
|
4
|
+
class Validator
|
5
|
+
class Required < Validator
|
6
|
+
private
|
7
|
+
|
8
|
+
def valid_value?
|
9
|
+
!(value.nil? && options[:required])
|
10
|
+
end
|
11
|
+
|
12
|
+
def error_message
|
13
|
+
"Parameter #{name} is required"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module RailsSimpleParams
|
6
|
+
class Validator
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
attr_reader :parameter
|
10
|
+
|
11
|
+
def_delegators :parameter, :name, :options, :value
|
12
|
+
|
13
|
+
VALIDATABLE_OPTIONS = %i[
|
14
|
+
blank
|
15
|
+
custom
|
16
|
+
format
|
17
|
+
in
|
18
|
+
is
|
19
|
+
max_length
|
20
|
+
max
|
21
|
+
min_length
|
22
|
+
min
|
23
|
+
required
|
24
|
+
].freeze
|
25
|
+
|
26
|
+
def initialize(parameter)
|
27
|
+
@parameter = parameter
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate!
|
31
|
+
options.each_key do |key|
|
32
|
+
next unless VALIDATABLE_OPTIONS.include? key
|
33
|
+
|
34
|
+
class_name = camelize(key)
|
35
|
+
Validator.const_get(class_name).new(parameter).valid!
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def valid!
|
40
|
+
return if valid_value?
|
41
|
+
|
42
|
+
raise InvalidParameterError.new(
|
43
|
+
error_message,
|
44
|
+
param: name,
|
45
|
+
options: options
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def camelize(term)
|
52
|
+
string = term.to_s
|
53
|
+
string.split('_').collect(&:capitalize).join
|
54
|
+
end
|
55
|
+
|
56
|
+
def error_message
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def valid_value?
|
61
|
+
# Should be overwritten in subclass
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_simple_params/param'
|
4
|
+
Dir[File.join(__dir__, 'rails_simple_params/validator', '*.rb')].each { |file| require file }
|
5
|
+
Dir[File.join(__dir__, 'rails_simple_params/coercion', '*.rb')].reverse_each { |file| require file }
|
6
|
+
Dir[File.join(__dir__, 'rails_simple_params', '*.rb')].each { |file| require file }
|
7
|
+
|
8
|
+
ActiveSupport.on_load(:action_controller) do
|
9
|
+
include RailsSimpleParams
|
10
|
+
end
|
metadata
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_simple_params
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeremy Weathers
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-04-13 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: actionpack
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: 7.0.1
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 7.0.1
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: activesupport
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 7.0.1
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 7.0.1
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: appraisal
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: bundler
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: fuubar
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: pry-byebug
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: rspec
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '3.4'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '3.4'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rspec-rails
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '3.4'
|
117
|
+
type: :development
|
118
|
+
prerelease: false
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '3.4'
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: rubocop
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
type: :development
|
132
|
+
prerelease: false
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
- !ruby/object:Gem::Dependency
|
139
|
+
name: rubocop-performance
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
type: :development
|
146
|
+
prerelease: false
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
- !ruby/object:Gem::Dependency
|
153
|
+
name: simplecov
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
type: :development
|
160
|
+
prerelease: false
|
161
|
+
version_requirements: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
description: Simple DSL to validate params (and coerce them to the desired type) that
|
167
|
+
fall outside of Rails' default parameter whitelisting and ActiveModel validations
|
168
|
+
system. The common use cases are searching and sorting, whether from an HTML form
|
169
|
+
or via a JSON API.
|
170
|
+
email:
|
171
|
+
- jeremy@codekindly.com
|
172
|
+
executables: []
|
173
|
+
extensions: []
|
174
|
+
extra_rdoc_files: []
|
175
|
+
files:
|
176
|
+
- README.md
|
177
|
+
- lib/rails_simple_params.rb
|
178
|
+
- lib/rails_simple_params/coercion.rb
|
179
|
+
- lib/rails_simple_params/coercion/array_param.rb
|
180
|
+
- lib/rails_simple_params/coercion/big_decimal_param.rb
|
181
|
+
- lib/rails_simple_params/coercion/boolean_param.rb
|
182
|
+
- lib/rails_simple_params/coercion/float_param.rb
|
183
|
+
- lib/rails_simple_params/coercion/hash_param.rb
|
184
|
+
- lib/rails_simple_params/coercion/integer_param.rb
|
185
|
+
- lib/rails_simple_params/coercion/string_param.rb
|
186
|
+
- lib/rails_simple_params/coercion/time_param.rb
|
187
|
+
- lib/rails_simple_params/coercion/virtual_param.rb
|
188
|
+
- lib/rails_simple_params/invalid_parameter_error.rb
|
189
|
+
- lib/rails_simple_params/param.rb
|
190
|
+
- lib/rails_simple_params/param_evaluator.rb
|
191
|
+
- lib/rails_simple_params/parameter.rb
|
192
|
+
- lib/rails_simple_params/validator.rb
|
193
|
+
- lib/rails_simple_params/validator/blank.rb
|
194
|
+
- lib/rails_simple_params/validator/custom.rb
|
195
|
+
- lib/rails_simple_params/validator/format.rb
|
196
|
+
- lib/rails_simple_params/validator/in.rb
|
197
|
+
- lib/rails_simple_params/validator/is.rb
|
198
|
+
- lib/rails_simple_params/validator/max.rb
|
199
|
+
- lib/rails_simple_params/validator/max_length.rb
|
200
|
+
- lib/rails_simple_params/validator/min.rb
|
201
|
+
- lib/rails_simple_params/validator/min_length.rb
|
202
|
+
- lib/rails_simple_params/validator/required.rb
|
203
|
+
- lib/rails_simple_params/version.rb
|
204
|
+
homepage: http://github.com/jlw/rails-simple-param
|
205
|
+
licenses:
|
206
|
+
- MIT
|
207
|
+
metadata:
|
208
|
+
rubygems_mfa_required: 'true'
|
209
|
+
rdoc_options:
|
210
|
+
- "--charset=UTF-8"
|
211
|
+
require_paths:
|
212
|
+
- lib
|
213
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
214
|
+
requirements:
|
215
|
+
- - ">="
|
216
|
+
- !ruby/object:Gem::Version
|
217
|
+
version: 3.2.0
|
218
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: 1.3.6
|
223
|
+
requirements: []
|
224
|
+
rubygems_version: 3.6.2
|
225
|
+
specification_version: 4
|
226
|
+
summary: Parameter validation and type coercion for Rails
|
227
|
+
test_files: []
|