Skip to content

Commit

Permalink
Add length validator (#2437)
Browse files Browse the repository at this point in the history
  • Loading branch information
dhruvCW committed May 12, 2024
1 parent 3f6a70a commit fa18860
Show file tree
Hide file tree
Showing 7 changed files with 380 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* [#2436](https://github.com/ruby-grape/grape/pull/2436): Update coverallsapp github-action - [@ericproulx](https://github.com/ericproulx).
* [#2434](https://github.com/ruby-grape/grape/pull/2434): Implement nested `with` support in parameter dsl - [@numbata](https://github.com/numbata).
* [#2438](https://github.com/ruby-grape/grape/pull/2438): Fix some Rack::Lint - [@ericproulx](https://github.com/ericproulx).
* [#2437](https://github.com/ruby-grape/grape/pull/2437): Add length validator - [@dhruvCW](https://github.com/dhruvCW).
* Your contribution here.

#### Fixes
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
- [values](#values)
- [except_values](#except_values)
- [same_as](#same_as)
- [length](#length)
- [regexp](#regexp)
- [mutually_exclusive](#mutually_exclusive)
- [exactly_one_of](#exactly_one_of)
Expand All @@ -69,6 +70,7 @@
- [Custom Validation messages](#custom-validation-messages)
- [presence, allow_blank, values, regexp](#presence-allow_blank-values-regexp)
- [same_as](#same_as-1)
- [length](#length-1)
- [all_or_none_of](#all_or_none_of-1)
- [mutually_exclusive](#mutually_exclusive-1)
- [exactly_one_of](#exactly_one_of-1)
Expand Down Expand Up @@ -1709,6 +1711,20 @@ params do
end
```

#### `length`

Parameters with types that support `#length` method can be restricted to have a specific length with the `:length` option.

The validator accepts `:min` or `:max` or both options to validate that the value of the parameter is within the given limits.

```ruby
params do
requires :str, type: String, length: { min: 3 }
requires :list, type: [Integer], length: { min: 3, max: 5 }
requires :hash, type: Hash, length: { max: 5 }
end
```

#### `regexp`

Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value does not match the regular expression an error will be returned. Note that this is true for both `requires` and `optional` parameters.
Expand Down Expand Up @@ -2026,6 +2042,15 @@ params do
end
```

#### `length`

```ruby
params do
requires :str, type: String, length: { min: 5, message: 'str is expected to be atleast 5 characters long' }
requires :list, type: [Integer], length: { min: 2, max: 3, message: 'list is expected to have between 2 and 3 elements' }
end
```

#### `all_or_none_of`

```ruby
Expand Down
3 changes: 3 additions & 0 deletions lib/grape/locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ en:
values: 'does not have a valid value'
except_values: 'has a value not allowed'
same_as: 'is not the same as %{parameter}'
length: 'is expected to have length within %{min} and %{max}'
length_min: 'is expected to have length greater than or equal to %{min}'
length_max: 'is expected to have length less than or equal to %{max}'
missing_vendor_option:
problem: 'missing :vendor option'
summary: 'when version using header, you must specify :vendor option'
Expand Down
3 changes: 3 additions & 0 deletions lib/grape/validations/attributes_doc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ def extract_details(validations)
details[:documentation] = documentation if documentation

details[:default] = validations[:default] if validations.key?(:default)

details[:min_length] = validations[:length][:min] if validations.key?(:length) && validations[:length].key?(:min)
details[:max_length] = validations[:length][:max] if validations.key?(:length) && validations[:length].key?(:max)
end

def document(attrs)
Expand Down
42 changes: 42 additions & 0 deletions lib/grape/validations/validators/length_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

module Grape
module Validations
module Validators
class LengthValidator < Base
def initialize(attrs, options, required, scope, **opts)
@min = options[:min]
@max = options[:max]

super

raise ArgumentError, 'min must be an integer greater than or equal to zero' if !@min.nil? && (!@min.is_a?(Integer) || @min.negative?)
raise ArgumentError, 'max must be an integer greater than or equal to zero' if !@max.nil? && (!@max.is_a?(Integer) || @max.negative?)
raise ArgumentError, "min #{@min} cannot be greater than max #{@max}" if !@min.nil? && !@max.nil? && @min > @max
end

def validate_param!(attr_name, params)
param = params[attr_name]

raise ArgumentError, "parameter #{param} does not support #length" unless param.respond_to?(:length)

return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max)

raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: build_message)
end

def build_message
if options_key?(:message)
@option[:message]
elsif @min && @max
format I18n.t(:length, scope: 'grape.errors.messages'), min: @min, max: @max
elsif @min
format I18n.t(:length_min, scope: 'grape.errors.messages'), min: @min
else
format I18n.t(:length_max, scope: 'grape.errors.messages'), max: @max
end
end
end
end
end
end
7 changes: 5 additions & 2 deletions spec/grape/validations/attributes_doc_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
presence: true,
desc: 'Age of...',
documentation: 'Age is...',
default: 1
default: 1,
length: { min: 1, max: 13 }
}
end

Expand Down Expand Up @@ -77,7 +78,9 @@
documentation: validations[:documentation],
default: validations[:default],
type: 'Integer',
values: valid_values
values: valid_values,
min_length: validations[:length][:min],
max_length: validations[:length][:max]
}
end

Expand Down

0 comments on commit fa18860

Please sign in to comment.