EnumErrorsAway
Fix Rails 7.2+ enum migration failures - EnumErrorsAway eliminates "Undeclared attribute type for enum" errors that break migrations when enum columns are added in separate migrations.
The Problem
Rails 7.2+ requires enum attributes to be declared with a type when they don't have a corresponding database column. This causes migration failures in a common scenario:
# Your model defines multiple enums
class Organization < ApplicationRecord
enum :status, { active: 0, inactive: 1 }
enum :billing_plan, { free: 0, paid: 1 }
end
# db/migrate/20240101_add_status_to_organizations.rb
add_column :organizations, :status, :integer
# db/migrate/20240201_add_billing_plan_to_organizations.rb
add_column :organizations, :billing_plan, :integerWhen running migrations from scratch, the first migration fails:
ArgumentError: Undeclared attribute type for enum 'billing_plan' in Organization.
Enums must be backed by a database column or declared with an explicit type via `attribute`.
This happens because:
- The first migration runs and loads the Organization model
- The model defines both
statusANDbilling_planenums - But the
billing_plancolumn doesn't exist yet (it's added in the second migration) - Rails 7.2+ throws an error
This is especially problematic when:
- Running migrations from scratch (new development environments, CI/CD, test databases)
- Adding new enums over time in separate migrations
- Migrating legacy applications to Rails 7.2+
- Working with large teams where different developers add enums in different migrations
The Solution
EnumErrorsAway automatically handles this for you. Just add the gem and your enums work again - no code changes required!
The gem:
- ✅ Automatically declares missing enum attributes as integers
- ✅ Preserves legitimate enum errors (method collisions, invalid values, etc.)
- ✅ Zero configuration required - works out of the box
- ✅ Type-safe - includes RBS type definitions for Steep/TypeProf
- ✅ Fully tested - comprehensive test suite
- ✅ Rails 7.2+ compatible
Installation
Add this line to your application's Gemfile:
gem 'enum_errors_away'And then execute:
$ bundle installOr install it yourself as:
$ gem install enum_errors_awayUsage
Once installed, the gem automatically works for all ActiveRecord models. No configuration needed!
Before (Rails 7.2+ without this gem):
class Organization < ApplicationRecord
enum :status, { active: 0, inactive: 1 } # Added in first migration
enum :billing_plan, { free: 0, paid: 1 } # Added in second migration
end
# Running the first migration fails because billing_plan column doesn't exist yet!
# You'd need to manually add: attribute :billing_plan, :integerAfter (with this gem):
class Organization < ApplicationRecord
enum :status, { active: 0, inactive: 1 }
enum :billing_plan, { free: 0, paid: 1 }
end
# Migrations run successfully - attributes are automatically declared as integers
# No manual attribute declarations needed!Features
All Enum Syntaxes Supported
class User < ApplicationRecord
# Hash syntax
enum :status, { active: 0, inactive: 1 }
# Array syntax
enum :role, [:admin, :user, :guest]
# With options
enum :access_level, { basic: 0, premium: 1 }, prefix: true
# With Rails 7.2+ options
enum :visibility, { public: 0, private: 1 }, scopes: false
endLegitimate Errors Still Raised
The gem only suppresses the "Undeclared attribute type" error. Other enum errors are preserved:
class Organization < ApplicationRecord
enum :status, { available: 0, unavailable: 1 }
# This will correctly raise an error about method collision
enum :availability, { available: 0, unavailable: 1 }
# ArgumentError: already defined by another enum
endType Safety with RBS
The gem includes RBS type definitions for Steep and TypeProf:
# Your types are automatically available
module EnumErrorsAway
def self.enabled?: () -> bool
def self.configure: () { (singleton(EnumErrorsAway)) -> void } -> void
endConfiguration
Disabling the Gem
You can disable the gem globally or conditionally:
# config/initializers/enum_errors_away.rb
EnumErrorsAway.configure do |config|
config.enabled = false
end
# Or conditionally
EnumErrorsAway.enabled = Rails.env.production?Checking Status
EnumErrorsAway.enabled? # => trueReal-World Example
Here's the typical scenario this gem solves:
# app/models/organization.rb
class Organization < ApplicationRecord
enum :status, { active: 0, inactive: 1 } # Added May 2024
enum :billing_plan, { free: 0, paid: 1 } # Added June 2024
enum :notification_preference, { email: 0, sms: 1, both: 2 } # Added July 2024
end
# db/migrate/20240501_add_status_to_organizations.rb
class AddStatusToOrganizations < ActiveRecord::Migration[7.2]
def change
add_column :organizations, :status, :integer, default: 0
end
end
# db/migrate/20240601_add_billing_plan_to_organizations.rb
class AddBillingPlanToOrganizations < ActiveRecord::Migration[7.2]
def change
add_column :organizations, :billing_plan, :integer, default: 0
end
end
# db/migrate/20240701_add_notification_preference_to_organizations.rb
class AddNotificationPreferenceToOrganizations < ActiveRecord::Migration[7.2]
def change
add_column :organizations, :notification_preference, :integer, default: 0
end
endWithout this gem: When a new developer runs rails db:migrate from scratch, the first migration fails because the model references enums that don't have columns yet.
With this gem: All migrations run successfully. The gem automatically declares the missing enum attributes, and everything works seamlessly.
How It Works
- Hooks into ActiveRecord - Uses a Railtie to integrate with Rails initialization
-
Extends enum method - Wraps the standard
enummethod viaActiveSupport::Concern -
Pre-declares attributes - Before defining an enum, checks if the attribute exists in the database; if not, declares it as
:integer - Handles migration timing - Gracefully handles database connection errors during migrations
- Preserves error handling - Only catches "Undeclared attribute type" errors; all other enum errors pass through
- Zero runtime performance impact - Only runs during class definition, not during model usage
Compatibility
- Ruby: 3.1+
- Rails: 7.2+
Development
# Install dependencies
bundle install
# Run tests
bundle exec rake test
# Run type checks
bundle exec steep checkTesting
The gem includes a comprehensive test suite covering:
- Enums without database columns
- Enums with database columns
- Multiple enum syntaxes (hash, array, with options)
- Error handling (ensures legitimate errors aren't suppressed)
- Configuration and enable/disable functionality
- Integration with real Rails models
Run tests with:
bundle exec rake testContributing
Bug reports and pull requests are welcome on GitHub at https://github.com/sampokuokkanen/enum_errors_away.
License
The gem is available as open source under the terms of the MIT License.
Credits
Created to solve the Rails 7.2+ enum declaration requirement while maintaining backward compatibility with existing codebases.