Last active
July 18, 2019 15:37
-
-
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 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
## | |
# 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 |
Have a look at this @eliduke,
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
Looks like the
simplified_type
method was deprecated back in Rails 3.0.9:http://apidock.com/rails/v2.3.8/ActiveRecord/ConnectionAdapters/Column/simplified_type
Unfortunately, I don't know enough about how to fix the initializer to still function correctly.
Any thoughts, @clarkdave?