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.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +168 -0
  3. data/lib/rails_simple_params/coercion/array_param.rb +20 -0
  4. data/lib/rails_simple_params/coercion/big_decimal_param.rb +21 -0
  5. data/lib/rails_simple_params/coercion/boolean_param.rb +17 -0
  6. data/lib/rails_simple_params/coercion/float_param.rb +13 -0
  7. data/lib/rails_simple_params/coercion/hash_param.rb +20 -0
  8. data/lib/rails_simple_params/coercion/integer_param.rb +13 -0
  9. data/lib/rails_simple_params/coercion/string_param.rb +11 -0
  10. data/lib/rails_simple_params/coercion/time_param.rb +21 -0
  11. data/lib/rails_simple_params/coercion/virtual_param.rb +26 -0
  12. data/lib/rails_simple_params/coercion.rb +43 -0
  13. data/lib/rails_simple_params/invalid_parameter_error.rb +19 -0
  14. data/lib/rails_simple_params/param.rb +7 -0
  15. data/lib/rails_simple_params/param_evaluator.rb +95 -0
  16. data/lib/rails_simple_params/parameter.rb +39 -0
  17. data/lib/rails_simple_params/validator/blank.rb +26 -0
  18. data/lib/rails_simple_params/validator/custom.rb +15 -0
  19. data/lib/rails_simple_params/validator/format.rb +34 -0
  20. data/lib/rails_simple_params/validator/in.rb +22 -0
  21. data/lib/rails_simple_params/validator/is.rb +17 -0
  22. data/lib/rails_simple_params/validator/max.rb +17 -0
  23. data/lib/rails_simple_params/validator/max_length.rb +17 -0
  24. data/lib/rails_simple_params/validator/min.rb +17 -0
  25. data/lib/rails_simple_params/validator/min_length.rb +17 -0
  26. data/lib/rails_simple_params/validator/required.rb +17 -0
  27. data/lib/rails_simple_params/validator.rb +65 -0
  28. data/lib/rails_simple_params/version.rb +5 -0
  29. data/lib/rails_simple_params.rb +10 -0
  30. 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,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleParams
4
+ class Coercion
5
+ class FloatParam < VirtualParam
6
+ def coerce
7
+ return nil if param == '' # e.g. from an empty field in an HTML form
8
+
9
+ Float(param)
10
+ end
11
+ end
12
+ end
13
+ 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,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleParams
4
+ class Coercion
5
+ class IntegerParam < VirtualParam
6
+ def coerce
7
+ return nil if param == '' # e.g. from an empty field in an HTML form
8
+
9
+ Integer(param)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleParams
4
+ class Coercion
5
+ class StringParam < VirtualParam
6
+ def coerce
7
+ String(param)
8
+ end
9
+ end
10
+ end
11
+ 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleParams
4
+ def param!(name, type, options = {}, &)
5
+ ParamEvaluator.new(params).param!(name, type, options, &)
6
+ end
7
+ 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,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleParams
4
+ class Validator
5
+ class Custom < Validator
6
+ def valid_value?
7
+ !options[:custom].call(value)
8
+ end
9
+
10
+ private
11
+
12
+ def error_message; end
13
+ end
14
+ end
15
+ 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsSimpleParams
4
+ VERSION = '1.4.0'
5
+ 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: []