# Errors

All Futures supports the same error handling pipeline as Active Record, `errors`, which is an enumerable instance of `ActiveModel::Errors`.

When validations fail, `ActiveModel::Error` instances will be added to the `errors.objects` Array. `objects` is actually just an alias for `errors.errors`, which is *just not pretty enough* for Rails. :see\_no\_evil:

{% hint style="success" %}
In the interest of brevity and readability, the receiver and `errors` object have been omitted from every reference to a method in this chapter.

When you read `full_messages_for :name`, it is a stand-in for `record.errors.full_messages_for :name`.
{% endhint %}

When a model is initialized, its `objects` Array is empty until you either call `save` / `update` or invoke `valid?` directly. `valid?` returns `false` if at least one validation failed, and there will now be at least one `ActiveModel::Error` in the `objects` Array.

### Successful failure

The `valid?` method clears the `objects` Array, which means that **adding errors will not make a model invalid**. Instead, it's failed validations that typically add the errors.

```ruby
class Teenager < AllFutures::Base
  validate :designated_driver?

  def :designated_driver?
    errors.add(:base, "is drunk") unless sober?
  end
end

```

Our goal is to respond to this invalid state as part of the normal user experience, *without actually raising an application level exception*. To achieve this "successful failure", Rails gives us a family of methods that operate on the `objects` Array.&#x20;

Many Rails developers think of `errors` as "the thing generated resources use to show validation failure messages". All Futures offers developers several compelling reasons to learn what `ActiveModel::Errors` has to offer someone building a reactive UI.

### So, you think you have some errors

Let's start with a basic assumption: if your model is `valid?` - that is, `true` - then you shouldn't have any errors.

However, you might not want to run `valid?` because it runs all of your validations again, and that might be undesirable. The [valid\_email2](https://github.com/lisinge/valid_email2) gem actually tests for valid MX servers, which could be slow. Or perhaps your validations connect with a [paid API](https://github.com/stripe-samples/identity)? (**Don't** actually do this!)

You can use the `any?` method to check for the presence of errors in the `objects` Array, and `size` to get a count. You can use `attribute_names` to access an Array of Symbols representing - you guessed it - attribute names that have errors associated with them.

There's also a `details` method that returns a Hash structure which conveys all error types for all attributes, eg. `{:name=>[{:error=>:blank}]}`

### Manipulating errors

`objects` is an Array, so you should be able to just add and remove Error objects, right?

Actually, no - we don't want to manually instantiate `ActiveModel::Error` objects. Instead, we can make use of [`add`](https://api.rubyonrails.org/v7.0.0/classes/ActiveModel/Errors.html#method-i-add) , [`delete`](https://api.rubyonrails.org/v7.0.0/classes/ActiveModel/Errors.html#method-i-delete) which make it easy to work with errors, even across multiple versions of Rails when the internal structure of `Error` objects might change over time.

You can `clear` your `objects` Array, but that won't make your model valid. To do that, you have to call `valid?` again - hopefully with the errors fixed.

### `full_messages` vs `messages`

The error messages generated by `ActiveModel::Errors` are available with and without the attribute name prefixed, offering you a choice between "Name is invalid" (`full_messages`) and "is invalid" (`messages`).

Both are useful in different situations; you might not want an awkwardly-named attribute being converted into a proper noun, such as "State Province can't be empty".

The key to enlightenment is to spend time studying (and potentially modifying) [the locale file](https://github.com/rails/rails/blob/main/activemodel/lib/active_model/locale/en.yml)s for the languages that you support. Of course, you can also [specify messages](https://guides.rubyonrails.org/active_record_validations.html#message) on a per-validation basis, but that can add complexity to your internationalization strategy.

### Errors, for a specific attribute

The payoff for this chapter is that you can access the errors for a specific attribute, or for the `:base` model instance. When paired with All Future's ability to tell you [if a given attribute is valid](https://allfutures.leastbad.com/validations#attribute_valid-attribute-attr_valid) - even when the model itself might not be - this is a major level-up for reactive UI developers looking to give real-time feedback on an input field.

You can pass an attribute name as a Symbol to `include?` (aliased as both `key?` and `has_key?`) and receive a Boolean indicating whether that attribute has at least one error.

Pass a Symbol to `messages_for(:attribute)` or `full_messages_for(:attribute)` and get the errors for that attribute.

![](https://70018364-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MYvadIZ2i2M8o8gLctH%2Fuploads%2FQk1N0M1eHPZv7KQWpGNn%2Fgas.jpg?alt=media\&token=1ad1e1cf-610b-4002-b70d-7f8a9689d9de)

You can also access the `ActiveModel::Error` instances using the [`where`](https://api.rubyonrails.org/v7.0.0/classes/ActiveModel/Errors.html#method-i-where) method:

```ruby
where(:name) # all name errors
where(:name, :too_short) # all name errors being too short
where(:name, :too_short, minimum: 2) # all name errors being too short and minimum is 2
```

To get the first `full_message` error for `:name` you would do something like this:

```ruby
record.errors.where(:name).first.full_message # => "Name can't be blank"
```

### Acting on specific errors

Most applications have two exception layers: the "[successful failure](#successful-failure)" that comes from processing user input within predetermined constraints, and everything else. You need to determine which category of error that you're dealing with so you remain in control of the user experience, even when things go wrong.

There's actually *two* methods that test for the presence of specific errors. In the beginning, there was [`added?`](https://api.rubyonrails.org/v7.0.0/classes/ActiveModel/Errors.html#method-i-added-3F), while [`of_kind?`](https://api.rubyonrails.org/v7.0.0/classes/ActiveModel/Errors.html#method-i-of_kind-3F) was added in Rails 6.

`added?` is designed to use the same syntax as the [`add`](https://api.rubyonrails.org/v7.0.0/classes/ActiveModel/Errors.html#method-i-add) method. It matches against the **specific** options used to generate an error, or the final string generated by the error. It will **not** detect errors that do not match the *exact* signature of the query.

`of_kind?` is much more forgiving, in that it only accepts the attribute and error type but will return `true` if that error occurs, regardless of the options.

We find it confusing that there are two methods for this, too. Think of it like a John Hughes movie: `added?` is the uptight math teacher, while `of_kind?` is the coach who doles out tough love, but won't rat you out to the Vice Principal.
