All Futures supports the same error handling pipeline as Active Record,
errors, which is an enumerable instance of
When validations fail,
ActiveModel::Errorinstances will be added to the
objectsis actually just an alias for
errors.errors, which is just not pretty enough for Rails.
In the interest of brevity and readability, the receiver and
errorsobject 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
When a model is initialized, its
objectsArray is empty until you either call
falseif at least one validation failed, and there will now be at least one
valid?method clears the
objectsArray, which means that adding errors will not make a model invalid. Instead, it's failed validations that typically add the errors.
class Teenager < AllFutures::Base
errors.add(:base, "is drunk") unless sober?
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
Many Rails developers think of
errorsas "the thing generated resources use to show validation failure messages". All Futures offers developers several compelling reasons to learn what
ActiveModel::Errorshas to offer someone building a reactive UI.
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 gem actually tests for valid MX servers, which could be slow. Or perhaps your validations connect with a paid API? (Don't actually do this!)
You can use the
any?method to check for the presence of errors in the
sizeto get a count. You can use
attribute_namesto access an Array of Symbols representing - you guessed it - attribute names that have errors associated with them.
There's also a
detailsmethod that returns a Hash structure which conveys all error types for all attributes, eg.
objectsis an Array, so you should be able to just add and remove Error objects, right?
objectsArray, but that won't make your model valid. To do that, you have to call
valid?again - hopefully with the errors fixed.
The error messages generated by
ActiveModel::Errorsare available with and without the attribute name prefixed, offering you a choice between "Name is invalid" (
full_messages) and "is invalid" (
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 files for the languages that you support. Of course, you can also specify messages on a per-validation basis, but that can add complexity to your internationalization strategy.
The payoff for this chapter is that you can access the errors for a specific attribute, or for the
:basemodel instance. When paired with All Future's ability to tell you if a given attribute is 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
has_key?) and receive a Boolean indicating whether that attribute has at least one error.
Pass a Symbol to
full_messages_for(:attribute)and get the errors for that attribute.
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
:nameyou would do something like this:
record.errors.where(:name).first.full_message # => "Name can't be blank"
Most applications have two exception layers: the "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.
added?is designed to use the same syntax as the
addmethod. 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
trueif 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.