Last active
December 27, 2021 22:17
-
-
Save jaydorsey/b24a8429342781cda385d7c72e8d895e to your computer and use it in GitHub Desktop.
Converting a boolean column to an enum in ActiveRecord/Rails
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# db/migrate/20211227213338_create_users.rb | |
# | |
# This is an example of what the Users table might have been created as | |
# or would look like before the migration from a boolean field to an enum | |
class CreateUsers < ActiveRecord::Migration[7.0] | |
def change | |
create_table :users do |t| | |
t.string :name, null: false | |
t.string :email, null: false, unique: true | |
# Realistically, this would probably be "active" not status; you'll want to do | |
# a rename at the appropriate time. Assuming all the code changes & migration | |
# have been run before doing the actual conversion which is run in the other migration | |
t.boolean :status, null: false, default: false, comment: 'true is active, false is inactive' | |
t.timestamps | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# db/migrate/20211227213604_convert_user_status_to_enum.rb | |
# | |
# Scenario: | |
# - You have a Users table with a boolean active column | |
# - You realize you need an extra "state" of pending | |
# - You want to convert your boolean to an enum & coerce the existing data | |
# | |
# Not in scope: | |
# - Renaming the original field from active -> status | |
class ConvertUserStatusToEnum < ActiveRecord::Migration[7.0] | |
def up | |
execute <<-SQL | |
-- Creates a new postgres type named user_status_fields which allows | |
-- for additional states aside from true/false | |
CREATE TYPE user_status_fields AS ENUM ('active', 'inactive', 'pending') | |
SQL | |
change_column_default :users, :status, nil | |
change_column :users, :status, <<~SQL | |
user_status_fields | |
USING | |
( | |
CASE status | |
WHEN true THEN 'active'::user_status_fields | |
WHEN false THEN 'inactive'::user_status_fields | |
END | |
) | |
SQL | |
change_column_comment :users, :status, from: 'true is active, false is inactive', | |
to: 'user_status_fields enum value' | |
change_column_default :users, :status, :inactive | |
end | |
def down | |
change_column_default :users, :status, nil | |
change_column :users, :status, <<~SQL | |
boolean | |
USING | |
( | |
CASE status | |
WHEN 'active'::user_status_fields THEN true | |
WHEN 'inactive'::user_status_fields THEN false | |
-- You just have to decide what pending means when you roll back | |
WHEN 'pending'::user_status_fields THEN false | |
END | |
) | |
SQL | |
change_column_default :users, :status, false | |
change_column_comment :users, :status, from: 'user_status_fields enum value', | |
to: 'true is active, false is inactive' | |
execute <<-SQL | |
DROP TYPE user_status_fields | |
SQL | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# app/models/user.rb | |
class User < ApplicationRecord | |
# The keys must be strings, not symbols, for ActiveRecord to recognize | |
# them. You get the standard scopes just like a regular enum, like | |
# User.active & User.pending | |
enum status: { active: 'active', inactive: 'inactive', pending: 'pending' } | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment