Class: OnlineMigrations::DataMigrations::BackfillReceptionTypeWildValues

Inherits:
OnlineMigrations::DataMigration
  • Object
show all
Defined in:
lib/online_migrations/data_migrations/backfill_reception_type_wild_values.rb

Overview

Cleans up the four "wild" reception-type values still on production
so the columns can move to a Postgres native enum type in a follow-up
PR without violations:

  • room_configurations.reception_type = 'MyProjects' (~2,325 rows)
    'IQ' ('MyProjects' was the legacy name for what is now the
    quote-builder / instant-quote flow)
  • room_configurations.reception_type = 'Other' (~1,833 rows)
    'Unspecified' (semantic match — the row was never categorized)
  • room_configurations.reception_type = '' (~2 rows)
    'Unspecified' (junk; the column is NOT NULL so empty string
    is the closest thing to "missing")
  • opportunities.opportunity_reception_type = '' (1 row)
    'CRM' (the column default — 'Unspecified' is not a valid
    opportunity reception type, only a room-configuration one)

All four values were ad-hoc literals from earlier eras of the codebase.
Cleanup mappings confirmed by the team (cbillen) on 2026-05-10.

See Also:

Constant Summary collapse

BATCH_SIZE =

Batch size — small enough to keep each transaction quick.

1_000
MAPPINGS =

Each entry is [model, column, old_value, new_value]. The order
is informational; the four updates are independent.

[
  [::RoomConfiguration, :reception_type,             'MyProjects', 'IQ'],
  [::RoomConfiguration, :reception_type,             'Other',      'Unspecified'],
  [::RoomConfiguration, :reception_type,             '',           'Unspecified'],
  [::Opportunity,       :opportunity_reception_type, '',           'CRM']
].freeze

Instance Method Summary collapse

Instance Method Details

#collectionArray<Array(Class, Symbol, String, String, Array<Integer>)>

Returns one [model, column, old_value, new_value, ids] tuple
per batch across every (model, column, old_value) combination.
old_value is threaded through so process can use it in the
conditional WHERE without re-deriving it (two of the MAPPINGS
rows collapse to the same (model, column, new_value) key —
'Other' → 'Unspecified' and '' → 'Unspecified' — which
would make a MAPPINGS.find ambiguous).

Returned eagerly as an Array (not a streaming Enumerator)
because online_migrations' MigrationJob#build_enumerator
only handles ActiveRecord::Relation, BatchEnumerator, and
Array from #collection — a plain Enumerator raises
ArgumentError before on_start runs and leaves the row
wedged at status='enqueued'. The eager pass is cheap here:
the four mappings cover ~4.2k matching rows total, which
produces only ~6 tuples at BATCH_SIZE=1_000.

Returns:

  • (Array<Array(Class, Symbol, String, String, Array<Integer>)>)


60
61
62
63
64
65
66
67
68
69
# File 'lib/online_migrations/data_migrations/backfill_reception_type_wild_values.rb', line 60

def collection
  batches = []
  MAPPINGS.each do |model, column, old_value, new_value|
    model.where(column => old_value)
         .in_batches(of: BATCH_SIZE) do |batch|
      batches << [model, column, old_value, new_value, batch.ids]
    end
  end
  batches
end

#countInteger

Total un-migrated rows across all four mappings. Drives the
online_migrations progress bar.

Returns:

  • (Integer)


88
89
90
# File 'lib/online_migrations/data_migrations/backfill_reception_type_wild_values.rb', line 88

def count
  MAPPINGS.sum { |model, column, old_value, _| model.where(column => old_value).count }
end

#process(batch) ⇒ void

This method returns an undefined value.

Rewrites one batch from the wild value to the canonical replacement.
The conditional where(column => old_value) makes the write
idempotent — already-migrated rows are no-ops.

Parameters:

  • batch (Array(Class, Symbol, String, String, Array<Integer>))

    [model, column, old_value, new_value, ids] produced by #collection.



78
79
80
81
82
# File 'lib/online_migrations/data_migrations/backfill_reception_type_wild_values.rb', line 78

def process(batch)
  model, column, old_value, new_value, ids = batch
  model.where(id: ids, column => old_value)
       .update_all(column => new_value, updated_at: Time.current)
end