Class: RuboCop::Cop::DevDoc::Migration::RequireReferenceForeignKey
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::DevDoc::Migration::RequireReferenceForeignKey
- Defined in:
- lib/rubocop/cop/dev_doc/migration/require_reference_foreign_key.rb
Overview
Every non-polymorphic reference must carry a truthy foreign_key:.
Rationale
foreign_key: true (or a to_table: hash) is pure referential
integrity — never a business decision — so the house rule is that
every reference carries the DB-level constraint. Rails defaults
references to NO constraint, which means both an omitted option and
an explicit foreign_key: false silently ship an unconstrained
column, indistinguishable from an oversight.
This cop makes the constraint the enforced default and turns the
rare deliberate exception into a documented, greppable site: keep
foreign_key: false on the line and disable this cop with a brief
reason.
❌ Omitted — reads as "never decided"
t.belongs_to :author, null: false
❌ Explicit false without justification
t.belongs_to :author, null: false, foreign_key: false
✔️
t.belongs_to :author, null: false, foreign_key: true
✔️ Cross-table naming
add_reference :posts, :reviewer, foreign_key: { to_table: :users }
✔️ Deliberate exception, self-documenting
# rubocop:disable DevDoc/Migration/RequireReferenceForeignKey -- append-only audit table
t.belongs_to :snapshot, null: false, foreign_key: false
# rubocop:enable DevDoc/Migration/RequireReferenceForeignKey
Exception
Polymorphic references (polymorphic: true) are skipped — a
polymorphic column points at many tables, so no single FK constraint
can exist.
NOTE: A reference whose constraint is added separately via
add_foreign_key later in the migration (the pattern engine-vendored
migrations use) is still flagged — the cop cannot reliably match the
two up. Prefer the inline foreign_key: option; for vendored engine
migrations, disable with a reason or exclude the file.
NOTE: This cop enforces the constraint's presence; the sibling cop
DevDoc/Migration/ExplicitNullOnReference enforces that the
column's optionality is stated, 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 =
'Add `foreign_key: true` (or a `to_table:` hash); a deliberate exception ' \ 'keeps `foreign_key: false` plus a reasoned disable of this cop.'.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
74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/rubocop/cop/dev_doc/migration/require_reference_foreign_key.rb', line 74 def on_send(node) = node.arguments.find(&:hash_type?) return if polymorphic?() pair = foreign_key_pair() if pair.nil? add_offense(node.loc.selector) elsif pair.value.false_type? || pair.value.nil_type? add_offense(pair) end end |