A quick guide to model errors in Rails
When initialized, every Rails model inheriting from ActiveRecord::Base implicitly instantiates an ActiveModel::Errors object. That object is accessible via the #errors
method on the model’s instance and contains the errors generated when the model is validated on a create or update action. The presence of any errors prevents the model from being saved successfully, returning false from #save
or an exception (ActiveRecord::RecordInvalid) from #save!
.
Let’s dive into the structure of the ActiveModel::Errors object, how we can add and customize errors, and then format them for display.
For the example, we have a Shipment
model with some standard validations:
class Shipment < ActiveRecord::Base
... validates_presence_of :weight, :height, :depth, :width,
message: “needs to exist in order to have a box!” validates_numericality_of :weight, :height, :depth, :width ...
end
The errors are formatted as a hash with attributes as the keys and arrays of error messages as the hash values. We can access the unformatted messages hash by using the #messages
method on the errors object. Note that we used the :message option to customize the message coming from the presence validation.
shipment = Shipment.create(weight: 2, height: 2, width: “boo”)
shipment.errors.messages
=> {
:depth=>[“needs to exist in order to have a box!”, “is not a number”],
:width=>[“is not a number”]
}
For custom validations (here’s a post detailing custom validation strategies), my preferred way to add errors manually with the #add
method. If the error is not confined to an attribute, we may choose to assign another descriptive term or default to :base. Here are ways to add errors manually:
shipment.errors.add(:base, “is a not valid generic message”)
shipment.errors[:base] = “is a not valid generic message”
After the basic validation and these new errors, we get the following #messages
hash:
shipment.errors.messages
=> {
:depth=>[“needs to exist in order to have a box!”, “is not a number”],
:width=>[“is not a number”],
:base=>[“is a not valid generic message”]
}
Now that we have the messages as a hash, the errors object exposes a few useful methods to sanitize and access additional error information. Here are some examples based on the errors above:
# full_messages
shipment.errors.full_messages
=> [“Depth needs to exist in order to have a box!”, “Depth is not a number”, “Width is not a number”, “is a not valid generic message”]# full_messages_for
shipment.errors.full_messages_for(:depth)
=> [“Depth needs to exist in order to have a box!”, “Depth is not a number”]# get — gets only the messages without the keys
shipment.errors.get(:depth)
=> [“needs to exist in order to have a box!”, “is not a number”]# include?
shipment.errors.include(:depth)
=> true# empty?
shipment.errors.empty?
=> false# clear — clears all errors
shipment.errors.clear
shipment.error.messages
=> []
Thank you for reading. I appreciate any feedback that helps be a better writer and communicator, so please don’t hesitate to comment. Also open to future topics. Find my other posts on my Medium page.