Class: RuboCop::Cop::DevDoc::Migration::ExplicitNullOnReference
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::DevDoc::Migration::ExplicitNullOnReference
- Defined in:
- lib/rubocop/cop/dev_doc/migration/explicit_null_on_reference.rb
Overview
Every reference must state null: explicitly.
Rationale
For regular columns the house rule is nullable
(DevDoc/Migration/AvoidNonNull), so an absent null: is
unambiguous — silence IS the decision. Foreign-key columns are the
exception that AvoidNonNull deliberately carves out: optional and
mandatory are both completely legitimate, a genuine per-reference
business decision. But a legitimate decision that is never written down is
indistinguishable from an omission — a bare reference reads as "did
the developer forget?".
This cop requires the decision to be stated. Either value passes;
null: true is not redundant here, it is the self-documenting
spelling of "optional by design".
❌ Ambiguous — optional by design, or forgotten?
t.belongs_to :organization, foreign_key: true
✔️ Mandatory, stated
t.belongs_to :organization, foreign_key: true, null: false
✔️ Optional, stated
t.belongs_to :organization, foreign_key: true, null: true
Polymorphic references are held to the same rule — their optionality is just as much a decision.
Relationship with Rails/NotNullColumn
The stock cop flags add_reference ... null: false (NOT NULL added
to a possibly-populated table without a default) — the exact line
complying with THIS cop produces for a mandatory FK, so the two
collide. Consumers disable Rails/NotNullColumn: its add_column
half is covered more strictly by DevDoc/Migration/AvoidNonNull,
and its add_reference half contradicts house doctrine (a
populated-table migration fails loudly at migrate time and uses the
add-nullable -> backfill -> tighten pattern instead). This gem's
default config disables it, but that propagation is load-order
dependent (see NoBulkChangeTable's docstring) — disable it in the
project's .rubocop.yml too.
NOTE: This cop enforces that optionality is stated; the sibling cop
DevDoc/Migration/RequireReferenceForeignKey enforces the
constraint's presence, and
DevDoc/Migration/RedundantReferenceIndex removes the one reference
option whose default is already correct. Together: everything
written on a reference is a decision, everything omitted is
doctrine.
Constant Summary collapse
- MSG =
'State `null:` explicitly — optional and mandatory are both legitimate ' \ 'for a reference, so omission reads as an unmade decision.'.freeze
- RESTRICT_ON_SEND =
%i[belongs_to references add_reference add_belongs_to].freeze
Instance Method Summary collapse
Instance Method Details
#on_send(node) ⇒ Object
69 70 71 72 73 74 |
# File 'lib/rubocop/cop/dev_doc/migration/explicit_null_on_reference.rb', line 69 def on_send(node) = node.arguments.find(&:hash_type?) return if && null_stated?() add_offense(node.loc.selector) end |