Skip to content

Instantly share code, notes, and snippets.

@nuxlli
Forked from fizz/rails_postgres_enum.rb
Last active July 30, 2020 15:48
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save nuxlli/1c821b5a60ef5898744a8c91ca7d3c4a to your computer and use it in GitHub Desktop.
Save nuxlli/1c821b5a60ef5898744a8c91ca7d3c4a to your computer and use it in GitHub Desktop.
Support for PostgreSQL enum types in Rails 5 (including schema dump)
module ActiveRecord
class SchemaDumper
def dump(stream)
header(stream)
extensions(stream)
enums(stream)
tables(stream)
trailer(stream)
stream
end
alias_method :old_format_colspec, :format_colspec
def format_colspec(colspec)
if colspec.kind_of?(String)
colspec
elsif colspec.kind_of?(Array)
colspec = colspec.map { |value| format_colspec(value) }
colspec.find_all { |value| value.present? }.join(', ')
else
old_format_colspec(colspec)
end
end
private
def enums(stream)
enum_types = @connection.enum_types
if enum_types.any?
stream.puts ' # These are custom enum types that must be created before they can be used in the schema definition'
enum_types.each do |enum_type|
stream.puts " create_enum :#{enum_type.first}, :#{enum_type.second.join(', :')}"
end
stream.puts
end
stream.puts
end
end
module ConnectionAdapters
class PostgreSQLAdapter
def native_database_types
NATIVE_DATABASE_TYPES.merge(enum: { name: 'enum' })
end
def column_spec(column)
type = schema_type_with_virtual(column)
spec = prepare_column_options(column)
if column.type == :enum
type = 'column'
enum_type = spec.delete(:enum_type)
spec = [":#{enum_type}", spec]
end
[type, spec]
end
def prepare_column_options(column)
spec = super
spec[:enum_type] = column.sql_type if column.type == :enum
spec[:array] = 'true' if column.respond_to?(:array) && column.array
spec[:default] = "\"#{column.default_function}\"" if column.default_function
spec
end
def migration_keys
super.insert(1, :enum_type) << :array
end
def enum_types
query = <<-SQL
SELECT t.typname AS enum_name,
string_agg(e.enumlabel, ' ') AS enum_value
FROM pg_type t
JOIN pg_enum e ON t.oid = e.enumtypid
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
WHERE n.nspname = 'public'
GROUP BY enum_name
SQL
res = exec_query(query, 'SCHEMA').cast_values
res.each do |line|
line[1] = line[1].split(' ')
end
res
end
end
module PostgreSQL::SchemaStatements
def create_enum(name, *values)
execute "CREATE TYPE #{name} AS ENUM (#{values.map { |v| "'#{v}'" }.join(', ')})"
end
def drop_enum(name)
execute "DROP TYPE #{name}"
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment