Raise a descriptive error when a store column is misconfigured

If a developer has neglected to use a structured column type (hstore
or json) or to declare a serializer with `ActiveRecord.store`:

```ruby
  class User < ActiveRecord::Base
    store_accessor :settings, :notifications
  end
```

then a `ConfigurationError` will now be raised with a descriptive
error message when the accessor is read or written:

```ruby
  puts user.notifications
  # ActiveRecord::ConfigurationError: the column 'settings' has not
  # been configured as a store.  Please make sure the column is
  # declared serializable via 'ActiveRecord.store' or, if your
  # database supports it, use a structured column type like hstore or
  # json.
```

Previously, in this situation, a `NoMethodError` was raised when the
accessor was read or written:

```ruby
  puts user.notifications
  # NoMethodError: undefined method `accessor' for an instance of ActiveRecord::Type::Text
```

Raising a descriptive exception should help developers understand more
quickly what's wrong and how to fix it.

Closes #51699
This commit is contained in:
Mike Dalessio 2024-05-23 14:53:57 -04:00
parent 43e4916483
commit 2bf7e25243
No known key found for this signature in database
3 changed files with 35 additions and 1 deletions

View File

@ -1,3 +1,19 @@
* Improve `ActiveRecord::Store` to raise a descriptive exception if the column is not either
structured (e.g., PostgreSQL +hstore+/+json+, or MySQL +json+) or declared serializable via
`ActiveRecord.store`.
Previously, a `NoMethodError` would be raised when the accessor was read or written:
NoMethodError: undefined method `accessor' for an instance of ActiveRecord::Type::Text
Now, a descriptive `ConfigurationError` is raised:
ActiveRecord::ConfigurationError: the column 'metadata' has not been configured as a store.
Please make sure the column is declared serializable via 'ActiveRecord.store' or, if your
database supports it, use a structured column type like hstore or json.
*Mike Dalessio*
* Fix inference of association model on nested models with the same demodularized name.
E.g. with the following setup:

View File

@ -217,7 +217,11 @@ module ActiveRecord
end
def store_accessor_for(store_attribute)
type_for_attribute(store_attribute).accessor
type_for_attribute(store_attribute).tap do |type|
unless type.respond_to?(:accessor)
raise ConfigurationError, "the column '#{store_attribute}' has not been configured as a store. Please make sure the column is declared serializable via 'ActiveRecord.store' or, if your database supports it, use a structured column type like hstore or json."
end
end.accessor
end
class HashAccessor # :nodoc:

View File

@ -359,4 +359,18 @@ class StoreTest < ActiveRecord::TestCase
test "prefix/suffix do not affect stored attributes" do
assert_equal [:secret_question, :two_factor_auth, :login_retry], Admin::User.stored_attributes[:configs]
end
test "store_accessor raises an exception if the column is not either serializable or a structured type" do
user = Class.new(Admin::User) do
store_accessor :name, :color
end.new
assert_raises ActiveRecord::ConfigurationError do
user.color
end
assert_raises ActiveRecord::ConfigurationError do
user.color = "blue"
end
end
end