Summary
Since 4.24.0, annotations for integer columns backing a Rails enum render the raw
integer default instead of the enum label. Regenerating annotations on an unchanged
schema produces a diff on every such column:
-# signing_provider_service :integer default("idnow"), not null
+# signing_provider_service :integer default(0), not null
-# base_portfolio :integer default("classic"), not null
+# base_portfolio :integer default(0), not null
-# role :integer default("user"), not null
+# role :integer default(0), not null
4.23.0 produced the label form. In our app this touched ~45 files (models, serializers
and factories) purely from the gem bump, which fails the CI job that runs
rails db:migrate:reset and asserts a clean working tree.
Environment
- annotaterb 4.24.0 (4.23.0 is fine)
- Rails 8.1, Ruby 4.0
- PostgreSQL 17
Cause
I believe this is unintended fallout from #358 ("Respect DB column defaults over
attribute :foo, default: X overrides"), which rewrote ModelWrapper#column_defaults:
@column_defaults ||= @klass.column_defaults.each_with_object({}) do |(name, value), result|
column = @klass.columns_hash[name]
schema_value = schema_default_for(column)
result[name] = (value == schema_value) ? value : schema_value
end
Model#column_defaults casts the DB default through the attribute type, so for an
enum column it yields the label ("idnow"). schema_default_for casts through the
column type, yielding 0. They differ, so the branch treats it as an
attribute-level override and substitutes the raw schema value — but for enums the
difference is inherent to how ActiveRecord::Enum decorates the attribute type, not
an attribute ... default: override.
Declaring an enum is enough to trigger it:
class AccountRequest < ApplicationRecord
enum :signing_provider_service, {idnow: 0, fidentity: 1}, prefix: true
end
# 4.23.0: default("idnow")
# 4.24.0: default(0)
Expected
Enum-backed columns keep rendering their symbolic default, since that is the
meaningful value for a reader — while attribute :foo, default: X overrides continue
to show the DB schema default as #358 intended.
A possible discriminator: skip the substitution when the attribute type is an
ActiveRecord::Enum::EnumType (or, more generally, when the attribute type is a
decorator around the column's cast type rather than an independent default), instead
of inferring an override from value inequality alone.
I'm happy to put up a PR if you agree with that direction.
Summary
Since 4.24.0, annotations for integer columns backing a Rails
enumrender the rawinteger default instead of the enum label. Regenerating annotations on an unchanged
schema produces a diff on every such column:
4.23.0 produced the label form. In our app this touched ~45 files (models, serializers
and factories) purely from the gem bump, which fails the CI job that runs
rails db:migrate:resetand asserts a clean working tree.Environment
Cause
I believe this is unintended fallout from #358 ("Respect DB column defaults over
attribute :foo, default: Xoverrides"), which rewroteModelWrapper#column_defaults:Model#column_defaultscasts the DB default through the attribute type, so for anenum column it yields the label (
"idnow").schema_default_forcasts through thecolumn type, yielding
0. They differ, so the branch treats it as anattribute-level override and substitutes the raw schema value — but for enums the
difference is inherent to how
ActiveRecord::Enumdecorates the attribute type, notan
attribute ... default:override.Declaring an enum is enough to trigger it:
Expected
Enum-backed columns keep rendering their symbolic default, since that is the
meaningful value for a reader — while
attribute :foo, default: Xoverrides continueto show the DB schema default as #358 intended.
A possible discriminator: skip the substitution when the attribute type is an
ActiveRecord::Enum::EnumType(or, more generally, when the attribute type is adecorator around the column's cast type rather than an independent default), instead
of inferring an override from value inequality alone.
I'm happy to put up a PR if you agree with that direction.