Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Validation error messages #1295

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* [#1255](https://github.com/ruby-grape/grape/pull/1255): Allow param type definition in `route_param` - [@namusyaka](https://github.com/namusyaka).
* [#1257](https://github.com/ruby-grape/grape/pull/1257): Allow Proc, Symbol or String in `rescue_from with: ...` - [@namusyaka](https://github.com/namusyaka).
* [#1285](https://github.com/ruby-grape/grape/pull/1285): Add a warning for errors appearing in `after` callbacks - [@gregormelhorn](https://github.com/gregormelhorn).
* Your contribution here.
* [#1295](https://github.com/ruby-grape/grape/pull/1295): Add custom validation messages for parameter exceptions - [@railsmith](https://github.com/railsmith).

#### Fixes

Expand Down
114 changes: 114 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
- [Custom Validators](#custom-validators)
- [Validation Errors](#validation-errors)
- [I18n](#i18n)
- [Custom Validation Messages](#custom-validation-messages)
- [Headers](#headers)
- [Routes](#routes)
- [Helpers](#helpers)
Expand Down Expand Up @@ -1237,6 +1238,119 @@ end
Grape supports I18n for parameter-related error messages, but will fallback to English if
translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.

### Custom Validation messages

Grape supports custom validation messages for parameter-related and coerce-related error messages.

#### `presence`, `allow_blank`, `values`, `regexp`

```ruby
params do
requires :name, values: { value: 1..10, message: 'not in range from 1 to 10' }, allow_blank: { value: false, message: 'cannot be blank' }, regexp: { value: /^[a-z]+$/, message: 'format is invalid' }, message: 'is required'
end
```
#### `all_or_none_of`

```ruby
params do
optional :beer
optional :wine
optional :juice
all_or_none_of :beer, :wine, :juice, message: "all params are required or none is required"
end
```

#### `mutually_exclusive`

```ruby
params do
optional :beer
optional :wine
optional :juice
mutually_exclusive :beer, :wine, :juice, message: "are mutually exclusive cannot pass both params"
end
```
#### `exactly_one_of`

```ruby
params do
optional :beer
optional :wine
optional :juice
exactly_one_of :beer, :wine, :juice, message: {exactly_one: "are missing, exactly one parameter is required", mutual_exclusion: "are mutually exclusive, exactly one parameter is required"}
end
```
#### `at_least_one_of`

```ruby
params do
optional :beer
optional :wine
optional :juice
at_least_one_of :beer, :wine, :juice, message: "are missing, please specify at least one param"
end
```
#### `Coerce`

```ruby
params do
requires :int, type: {value: Integer, message: "type cast is invalid" }
end
```
#### `With Lambdas`

```ruby
params do
requires :name, values: { value: -> { (1..10).to_a }, message: 'not in range from 1 to 10' }
end
```
#### `Pass symbols for i18n translations`

You can pass a symbol if you want i18n translations for your custom validation messages.

```ruby
params do
requires :name, message: :name_required
end
```
```ruby
# en.yml

en:
grape:
errors:
format: ! '%{attributes} %{message}'
messages:
name_required: 'must be present'
```

#### `Overriding attribute names`

You can also override attribute names.

```ruby
# en.yml

en:
grape:
errors:
format: ! '%{attributes} %{message}'
messages:
name_required: 'must be present'
attributes:
name: 'Oops! Name'
```
Will produce 'Oops! Name must be present'

#### `With Default`

You cannot set a custom message option for Default as it requires interpolation `%{option1}: %{value1} is incompatible with %{option2}: %{value2}`. You can change the default error message for Default by changing the `incompatible_option_values` message key inside [en.yml](lib/grape/locale/en.yml)

```ruby
params do
requires :name, values: { value: -> { (1..10).to_a }, message: 'not in range from 1 to 10' }, default: 5
end
```
## Headers

Request headers are available through the `headers` helper or from `env` in their original form.
Expand Down
10 changes: 5 additions & 5 deletions lib/grape/dsl/parameters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def requires(*attrs, &block)
orig_attrs = attrs.clone

opts = attrs.extract_options!.clone
opts[:presence] = true
opts[:presence] = { value: true, message: opts[:message] }

if opts[:using]
require_required_and_optional_fields(attrs.first, opts)
Expand Down Expand Up @@ -137,25 +137,25 @@ def optional(*attrs, &block)
# Disallow the given parameters to be present in the same request.
# @param attrs [*Symbol] parameters to validate
def mutually_exclusive(*attrs)
validates(attrs, mutual_exclusion: true)
validates(attrs, mutual_exclusion: { value: true, message: extract_message_option(attrs) })
end

# Require exactly one of the given parameters to be present.
# @param (see #mutually_exclusive)
def exactly_one_of(*attrs)
validates(attrs, exactly_one_of: true)
validates(attrs, exactly_one_of: { value: true, message: extract_message_option(attrs) })
end

# Require at least one of the given parameters to be present.
# @param (see #mutually_exclusive)
def at_least_one_of(*attrs)
validates(attrs, at_least_one_of: true)
validates(attrs, at_least_one_of: { value: true, message: extract_message_option(attrs) })
end

# Require that either all given params are present, or none are.
# @param (see #mutually_exclusive)
def all_or_none_of(*attrs)
validates(attrs, all_or_none_of: true)
validates(attrs, all_or_none_of: { value: true, message: extract_message_option(attrs) })
end

# Define a block of validations which should be applied if and only if
Expand Down
15 changes: 11 additions & 4 deletions lib/grape/exceptions/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ def compose_message(key, attributes = {})
end

def problem(key, attributes)
translate_message("#{key}.problem", attributes)
translate_message("#{key}.problem".to_sym, attributes)
end

def summary(key, attributes)
translate_message("#{key}.summary", attributes)
translate_message("#{key}.summary".to_sym, attributes)
end

def resolution(key, attributes)
translate_message("#{key}.resolution", attributes)
translate_message("#{key}.resolution".to_sym, attributes)
end

def translate_attributes(keys, options = {})
Expand All @@ -60,7 +60,14 @@ def translate_attribute(key, options = {})
end

def translate_message(key, options = {})
translate("#{BASE_MESSAGES_KEY}.#{key}", options.reverse_merge(default: ''))
case key
when Symbol
translate("#{BASE_MESSAGES_KEY}.#{key}", options.reverse_merge(default: ''))
when Proc
key.call
else
key
end
end

def translate(key, options = {})
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/incompatible_option_values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class IncompatibleOptionValues < Base
def initialize(option1, value1, option2, value2)
super(message: compose_message('incompatible_option_values', option1: option1, value1: value1, option2: option2, value2: value2))
super(message: compose_message(:incompatible_option_values, option1: option1, value1: value1, option2: option2, value2: value2))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_accept_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidAcceptHeader < Base
def initialize(message, headers)
super(message: compose_message('invalid_accept_header', message: message), status: 406, headers: headers)
super(message: compose_message(:invalid_accept_header, message: message), status: 406, headers: headers)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidFormatter < Base
def initialize(klass, to_format)
super(message: compose_message('invalid_formatter', klass: klass, to_format: to_format))
super(message: compose_message(:invalid_formatter, klass: klass, to_format: to_format))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_message_body.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidMessageBody < Base
def initialize(body_format)
super(message: compose_message('invalid_message_body', body_format: body_format), status: 400)
super(message: compose_message(:invalid_message_body, body_format: body_format), status: 400)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_version_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidVersionHeader < Base
def initialize(message, headers)
super(message: compose_message('invalid_version_header', message: message), status: 406, headers: headers)
super(message: compose_message(:invalid_version_header, message: message), status: 406, headers: headers)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_versioner_option.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidVersionerOption < Base
def initialize(strategy)
super(message: compose_message('invalid_versioner_option', strategy: strategy))
super(message: compose_message(:invalid_versioner_option, strategy: strategy))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/invalid_with_option_for_represent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class InvalidWithOptionForRepresent < Base
def initialize
super(message: compose_message('invalid_with_option_for_represent'))
super(message: compose_message(:invalid_with_option_for_represent))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/missing_group_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class MissingGroupTypeError < Base
def initialize
super(message: compose_message('missing_group_type'))
super(message: compose_message(:missing_group_type))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/missing_mime_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class MissingMimeType < Base
def initialize(new_format)
super(message: compose_message('missing_mime_type', new_format: new_format))
super(message: compose_message(:missing_mime_type, new_format: new_format))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/missing_option.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class MissingOption < Base
def initialize(option)
super(message: compose_message('missing_option', option: option))
super(message: compose_message(:missing_option, option: option))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/missing_vendor_option.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class MissingVendorOption < Base
def initialize
super(message: compose_message('missing_vendor_option'))
super(message: compose_message(:missing_vendor_option))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/unknown_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class UnknownOptions < Base
def initialize(options)
super(message: compose_message('unknown_options', options: options))
super(message: compose_message(:unknown_options, options: options))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/unknown_parameter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class UnknownParameter < Base
def initialize(param)
super(message: compose_message('unknown_parameter', param: param))
super(message: compose_message(:unknown_parameter, param: param))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/unknown_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class UnknownValidator < Base
def initialize(validator_type)
super(message: compose_message('unknown_validator', validator_type: validator_type))
super(message: compose_message(:unknown_validator, validator_type: validator_type))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/exceptions/unsupported_group_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Grape
module Exceptions
class UnsupportedGroupTypeError < Base
def initialize
super(message: compose_message('unsupported_group_type'))
super(message: compose_message(:unsupported_group_type))
end
end
end
Expand Down
3 changes: 1 addition & 2 deletions lib/grape/exceptions/validation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ class Validation < Grape::Exceptions::Base
def initialize(args = {})
fail 'Params are missing:' unless args.key? :params
@params = args[:params]
@message_key = args[:message_key]
args[:message] = translate_message(args[:message_key]) if args.key? :message_key
args[:message] = translate_message(args[:message]) if args.key? :message
super
end

Expand Down
Loading