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 |