Posted 10 days ago. Visible to the public. Linked content. Auto-destruct in 50 days

Updated: Rails: How to check if a certain validation failed

  • Added Errors#of_kind?.

Changes

  • -If validations failed for a record, and you want to find out if a specific one failed, you can do this via Rails.
  • -You should rarely need this, as your error messages are usually good enough to display all necessary information to users.
  • +If validations failed for a record, and you want to find out if a *specific* validation failed, you can leverage ActiveModel's error objects.
  • +You rarely need this in application code (you usually just want to print error messages), but it can be useful when writing tests.
  • As an example, consider the following model which uses two validations on the `email` attribute.
  • -```
  • +```ruby
  • class User < ApplicationRecord
  • validates :email, presence: true, uniqueness: true
  • end
  • ```
  • -And let's assume we have a blank user:
  • +## Accessing errors
  • -```
  • +Let's assume we have a blank user:
  • +
  • +```ruby
  • user = User.new
  • user.valid? # => false
  • ```
  • -The record's `errors` then contains all validation errors:
  • +The record's `errors` then contains all validation errors.
  • -```
  • +```ruby
  • >> user.errors
  • -=> #<ActiveModel::Errors @base=#<User ...>, @messages={:email=>["can't be blank"]}, @details={:email=>[{:error=>:blank}]}>
  • +=> #<ActiveModel::Errors [#<ActiveModel::Error attribute=email, type=blank, options={}>]>
  • ```
  • -Instead dissecting `error.details` yourself to figure out which `email` validation failed, use the `added?` method:
  • +You can access `errors[:email]` to get the error message itself (`"can't be blank"`).
  • +However, your code may be more robust and readable if you check for the error type itself.
  • -```
  • +You can use the [`added?`](https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-added-3F) or [`of_kind?`](https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-of_kind-3F) methods for that.
  • +
  • +```ruby
  • >> user.errors.added?(:email, :blank)
  • => true
  • +```
  • +```ruby
  • +>> user.errors.of_kind?(:email, :blank)
  • +=> true
  • +```
  • +
  • +Note how error types don't necessarily match the validation name (e.g. `:blank` for `:presence`, `:taken` for `:uniqueness`).
  • +
  • +## Difference between `added?` and `of_kind?`
  • +
  • +While `added?` might feel more natural to use, it behaves differently than `of_kind?` because it also compares options from the validation error. `of_kind?` does not do that.
  • +
  • +This is is relevant for validations that provide extra context, like length or uniqueness validations.
  • +
  • +Consider a `User` that can not be saved because there already is a record with the same email address.
  • +
  • +```ruby
  • +user = User.new(email: "user@example.com")
  • +user.valid? # => false
  • +```
  • +```ruby
  • +>> user.errors
  • +=> #<ActiveModel::Errors [#<ActiveModel::Error attribute=email, type=taken, options={:value=>"user@example.com"}>]>
  • +```
  • +
  • +If you do not care about the options, use `of_kind?`:
  • +```ruby
  • +>> user.errors.of_kind?(:email, :taken)
  • +=> true
  • +```
  • +
  • +Using `added?` without specifying options will return false:
  • +```ruby
  • >> user.errors.added?(:email, :taken)
  • => false
  • ```
  • +```ruby
  • +>> user.errors.added?(:email, :taken, value: "user@example.com")
  • +=> true
  • +```
  • -Note how error types don't necessarily match the validation name (`:blank` for `:presence`, `:taken` for `:uniqueness`).
  • +Most often, you probably want to use `of_kind?`.

By refactoring problematic code and creating automated tests, makandra can vastly improve the maintainability of your Rails application.

Owner of this card:

Avatar
Arne Hartherz
Last edit:
10 days ago
by Arne Hartherz
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Arne Hartherz to makandra dev
This website uses short-lived cookies to improve usability.
Accept or learn more