Class: RuboCop::Cop::DevDoc::Rails::EnumColumnNotNull
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::DevDoc::Rails::EnumColumnNotNull
- Includes:
- ActiveRecordHelper
- Defined in:
- lib/rubocop/cop/dev_doc/rails/enum_column_not_null.rb
Overview
Enum columns must be backed by a ‘null: false` database column.
## Rationale ‘null: false` is reserved for cases where NULL has no meaningful interpretation — and an enum is the clearest such case. NULL is outside the enum’s domain (a type violation, not one of the defined values), and if “unset” is meaningful it should be modeled as an explicit enum value, never as NULL.
The line is drawn here for standardization and non-subjectivity. Whether a regular column should be present is a business decision open to debate (could ‘email` become optional once phone signup exists?), so it is left to model-layer judgment. An enum’s non-null-ness is objective — it does not depend on any business decision — so it is enforced mechanically rather than argued column-by-column.
This cop is the inverse of ‘DevDoc/Migration/AvoidNonNull`: that cop runs on the migration and strips `null: false` from regular columns; this cop runs on the model, reads `db/schema.rb`, and requires `null: false` on the column backing each `enum`.
## Interaction with AvoidNonNull An enum is a plain ‘integer` column, so `AvoidNonNull` cannot tell it apart from a regular integer and WILL flag the `null: false` you add to satisfy this cop. Disable it on that migration with a brief `– enum` reason, so the migration is self-documenting:
# rubocop:disable DevDoc/Migration/AvoidNonNull -- enum
add_column :orders, :status, :integer, null: false
# rubocop:enable DevDoc/Migration/AvoidNonNull
NOTE: This cop reads ‘db/schema.rb` and does nothing if it is absent (e.g. projects using `structure.sql`). It also relies on the schema being current, resolves the table name by Rails convention (STI, `self.table_name` overrides, or namespaced models may not resolve), recognizes only the positional `enum :name, …` form, and skips silently if the backing column cannot be found in the schema.
Constant Summary collapse
- MSG =
'Enum column `%<name>s` should be `null: false` — NULL is outside an enum\'s domain. ' \ 'Model "unset" as an explicit enum value.'.freeze
- RESTRICT_ON_SEND =
%i[enum].freeze
Instance Method Summary collapse
Instance Method Details
#on_send(node) ⇒ Object
66 67 68 69 70 71 |
# File 'lib/rubocop/cop/dev_doc/rails/enum_column_not_null.rb', line 66 def on_send(node) name = nullable_enum_name(node) return unless name add_offense(node, message: format(MSG, name: name)) end |