The Evolution of ActiveModel::Error in the Rails Framework
Rails progression emphasizes simplicity and productivity. Through versions, Rails integrated tools, enhanced performance, and adapted to industry standards, keeping a focus on developer happiness and efficiency. ActiveModel::Error
is an example of that. On this blog post, we’ll dive into the evolution of this object.
If you find yourself trying to upgrade your Rails application, it’s very likely that you will have to deal with changes that were made to ActiveModel::Error
over time.
There are some key differences between versions 5.0 and 6.0 of Rails on how it deals with validation errors in the model layer.
Back in Rails 3.0 ActiveModel::Error
was inheriting from the ActiveSupport::OrderedHash
class, and it responded to Hash
methods. It already included methods like full_messages
, details
, getters and setters.
# Rails 3.0 - activemodel/lib/active_model/errors.rb
class Errors < ActiveSupport::OrderedHash
This ActiveSupport::OrderedHash
inheritance was changed to an initializer later on Rails 3.1 to instantiate the error’s messages attribute.
# Rails 3.1 - activemodel/lib/active_model/errors.rb
def initialize(base)
@base = base
@messages = ActiveSupport::OrderedHash.new
end
Rails 4.0 changed the messages
attribute initialization from ActiveSupport::OrderedHash
to use the Hash
class, changing the behavior of the to_xml
, as_json
, and to_hash
methods. This change was made to adjust to Ruby version 1.9 or higher as Rails 4 had a minimum requirement of Ruby 1.9++ by that time.
You can check the pull request here .
# Rails 4.0 - activemodel/lib/active_model/errors.rb
def initialize(base)
@base = base
@messages = {}
end
Lately, in Rails 5.0 they included the details
attributes in the initializer to determine what validator has failed. You can check the pull request here .
def initialize(base)
@base = base
@messages = {}
@details = Hash.new { |details, attribute| details[attribute] = [] }
end
Once this commit got in the Rails repository, they started deprecating getters
, setters
and []=
methods because of inconsistent behavior when dealing with then:
errors.messages[:key]
anderrors.get(:key)
can be accessed only by symbol, buterrors["key"]
can be access by both string or symbol
errors.set(:key, ["error"])
is overwriting all errors, buterrors[:key] = "error"
(which should do the same thing) pushes error to existing ones.
errors.set("key", ["error"])
will not convert key to symbol, buterrors["key"] = "error"
anderrors.add("key", :invalid)
will convert it.
You can check the discussion in this pull request .
Rails 5.0 also deprecated the add_on_empty
and add_on_blank
methods in a following commit .
Rails 6.0 brings new methods to ActiveModel::Error
:
slice
, that was already provided toHash
viaActiveSupport
of_kind?
that allow us to check presence of a specific error
and adds a new configuration option to customize format of the
full_message
output
Rails 6.1 changes ActiveModel::Error
to improve its object orientation behavior in this pull request . The changes include a query interface, enable more precise testing, and access to error details.
model.errors.where(:name, :foo, bar: 3).first
Also making it easier to find the message with corresponding details of one particular error:
# model.errors.details
{:name=>[{error: :foo_error, count: 1}, {error: :bar_error}, {error: :foo_error, count: 3}]}
This change also opened the possibility of advancing modding, as errors are now objects so we can add functionality on top of them. For example, we can create custom methods to disable global attribute prefixes on an error’s full messages.
Some methods like as_json
, add
, and include?
remain unchanged after this Rails 6.1 change. Other methods were deprecated (full_message
, generate_message
, has_key
) and new methods were added to this class: messages_for
, where
, and import
.
The change tries its best at maintaining backward compatibility, however some edge cases won’t be covered. For example, errors#first
will return ActiveModel::Error
and manipulating errors.messages
and errors.details
hashes directly will have no effect.
Rails 7.0 removes all previous deprecated methods from ActiveModel::Error
from the code.
Conclusion
In conclusion, the ActiveModel::Error
emerges as an essential component within the Rails environment, playing a crucial role in the robustness and adaptability of modern Rails applications. Its ability to seamlessly handle and manage validation errors not only enhances the user experience by providing clear and concise error messages but also contributes significantly to the overall maintainability of the code. By encapsulating error details within this specialized object, developers can streamline error handling processes, ensuring a more efficient and organized approach to troubleshooting and debugging.
ActiveModel::Error
stands as a testament to Rails’ commitment to simplicity, convention over configuration, and the creation of applications that are not only powerful but also resilient and developer-friendly.
Running an end-of-life Rails version in production and in need of an upgrade? Send us a message, we can help!