Class: RuboCop::Cop::DevDoc::Rails::EnumMustBeSymbolized

Inherits:
Base
  • Object
show all
Defined in:
lib/rubocop/cop/dev_doc/rails/enum_must_be_symbolized.rb

Overview

Rails enum must never be declared directly — use glib-web's enum_symbolize :name, { … } declaring form instead.

Rationale

Two problems with a bare enum:

  1. Type. enum resolves its attribute type by introspecting the DB column. When the column doesn't exist yet — a not-yet-migrated column loaded on deploy, eager-loaded CI, or an un-migrated local DB — Rails raises "Undeclared attribute type for enum". Declaring the attribute up front (which enum_symbolize does, from the enum's own values) removes that dependency on the column.
  2. Reader. enum defines a reader that returns the string form, so record.status == :active silently fails (:foo == 'foo' is false). enum_symbolize overrides the reader to return a symbol.

The enum_symbolize :name, { … } form does both in one call — it declares the attribute, defines the enum, adds a presence validation, and symbolizes the reader — so neither footgun can be reintroduced by forgetting a pairing line.

See best_practices/backend/en/01a_defensive_programming.md item 7.


class Order < ApplicationRecord
enum :payment_status, { draft: 0, pending: 1, finalized: 2 }
enum_symbolize :payment_status
end


class Order < ApplicationRecord
enum_symbolize :payment_status, { draft: 0, pending: 1, finalized: 2 }
end

Enum options pass straight through:


enum_symbolize :finalize_intent, { unknown: 0, charge: 1 }, prefix: true

Enums declared inside a concern's included do block are checked the same way:


included do
enum_symbolize :discount_type, DISCOUNT_TYPES
end

Examples:

# bad
enum :status, { active: 0, archived: 1 }

# good
enum_symbolize :status, { active: 0, archived: 1 }

Constant Summary collapse

MSG =
'Declare enums with `enum_symbolize :%<name>s, { … }` instead of `enum`, so the ' \
'attribute type is set before the enum (surviving a not-yet-migrated column) and the ' \
'reader returns a symbol (see backend/01a_defensive_programming.md item 7).'.freeze

Instance Method Summary collapse

Instance Method Details

#on_block(node) ⇒ Object

Concern-declared enums: included do ... end runs in the including class's context, so its statements are checked like a class body.



77
78
79
80
81
# File 'lib/rubocop/cop/dev_doc/rails/enum_must_be_symbolized.rb', line 77

def on_block(node)
  return unless included_block?(node)

  check_body(node.body)
end

#on_class(node) ⇒ Object



71
72
73
# File 'lib/rubocop/cop/dev_doc/rails/enum_must_be_symbolized.rb', line 71

def on_class(node)
  check_body(node.body)
end