Skip to content

Instantly share code, notes, and snippets.

@schovi
Created January 28, 2019 09:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save schovi/841bf2dd9b331772c426d22576d7d09e to your computer and use it in GitHub Desktop.
Save schovi/841bf2dd9b331772c426d22576d7d09e to your computer and use it in GitHub Desktop.
module PostgresEnum
class MissingEnumError < ActiveRecord::ActiveRecordError
end
class MethodDefinedError < ActiveRecord::ActiveRecordError
end
extend ActiveSupport::Concern
class_methods do
def postgres_enum field,
name: field.to_s.pluralize,
method: field.to_s.pluralize
# TODO: implement sometime when this should be full replacement for rails enum
# mark_methods: true, scopes: true, _suffix: false, _prefix: false
@@postgres_enum ||= {}
@@postgres_enum[field.to_sym] = {
name: name,
values: postgres_enum_query_values(name)
}
# TODO: validate method existence.
# For some unknow reason this does not work
if self.respond_to?(method)
raise MethodDefinedError, "enum list method '#{method}' already exists."
end
# Singleton method for list of values from given enum
define_singleton_method method do
@@postgres_enum[field.to_sym][:values]
end
# Setter for enum with value check
define_method "#{field}=" do |value|
values = self.class.send(method)
unless values.index(value)
raise ArgumentError, "'#{value}' is not a valid #{field} (available values: #{values.join(", ")})"
end
super(value)
end
rescue ActiveRecord::StatementInvalid => ex
if ex.message.index("PG::UndefinedObject")
raise MissingEnumError, "Enum '#{name}' does not exist (create with: \"CREATE TYPE #{name} AS ENUM ('value1', ..., 'valueN')\")"
else
raise ex
end
end
private
def postgres_enum_query_values(name)
conn = self.connection
quoted_enum_name = conn.quote_table_name(name)
enum_values_sql = "SELECT unnest(enum_range(NULL::#{quoted_enum_name}))::text AS enum_value"
result = conn.exec_query(enum_values_sql)
result.rows.map(&:first)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment