Skip to content

Instantly share code, notes, and snippets.

@clarkdave
Last active July 18, 2019 15:37
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save clarkdave/5936375 to your computer and use it in GitHub Desktop.
Save clarkdave/5936375 to your computer and use it in GitHub Desktop.
basic support for PostgreSQL Enums in ActiveRecord - to be placed in an initializer
##
# This provides `create_enum` and `drop_enum` methods for migrations, which creates
# a Postgres ENUM type with the provided values.
#
# It'll also dump these into the schema.rb to be loaded into another DB (e.g. rake db:test:load)
#
# In order to reference the new enums as actual types, ActiveRecord needs to know about them, so
# make sure they are all represented in the `types` array below
#
# Then you can reference them in your migrations, e.g.
#
# create_table :example do
# t.state :state
# t.location :location
# t.method :method
# end
##
types = %w(state location method)
module PgEnum
# provide the enum DDL methods
module PostgreSQLAdapter
def create_enum(name, values)
values = values.map {|v| "'#{v}'"}
execute "CREATE TYPE #{name} AS ENUM (#{values.join(', ')})"
end
def drop_enum(name)
execute "DROP TYPE #{name}"
end
end
# provide support for writing out the 'create_enum' calls in schema.rb
module SchemaDumper
extend ActiveSupport::Concern
included do
alias_method_chain :tables, :enums
end
def tables_with_enums(stream)
enums(stream)
tables_without_enums(stream)
end
private
def enums(stream)
enums = @connection.select_all("SELECT typname FROM pg_type WHERE typtype = 'e'").map{|r| r['typname']}
statements = []
enums.each do |enum|
values = @connection.select_all(
"SELECT e.enumlabel FROM pg_enum e JOIN pg_type t ON e.enumtypid = t.oid WHERE t.typname = '#{enum}'"
).map {|v| v['enumlabel'].inspect }.join(', ')
statements << " create_enum(#{enum.inspect}, [#{values}])"
end
stream.puts statements.join("\n")
stream.puts
end
end
end
class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
include PgEnum::PostgreSQLAdapter
end
module ActiveRecord::ConnectionAdapters
include PgEnum::PostgreSQLAdapter
end
class ActiveRecord::SchemaDumper
include PgEnum::SchemaDumper
end
# make activerecord aware of our enum types
types.each do |type|
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[type.to_sym] = { name: type }
ActiveRecord::ConnectionAdapters::Column.class_eval do
define_method("simplified_type_with_#{type}") do |field_type|
if field_type == type
field_type.to_sym
else
send("simplified_type_without_#{type}", field_type)
end
end
alias_method_chain :simplified_type, type
end
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::TableDefinition.class_eval do
define_method(type) do |*args|
options = args.extract_options!
column(args[0], type, options)
end
end
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID.alias_type type, 'text'
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.tap do |clazz|
clazz::OID.register_type(type, clazz::OID::Identity.new)
end
end
@haikalhussein
Copy link

@khamusa
Copy link

khamusa commented May 11, 2018

I had the same issue regarding the simplified_type method absense. i've perfectioned the implementation for rails 4.2 and improved support for schema.rb when dumping tables with pg enum columns. I hope this saves someone time:
https://gist.github.com/khamusa/77ada9895db15dbee4e57cb715830a48

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment