Created
January 28, 2019 09:07
-
-
Save schovi/841bf2dd9b331772c426d22576d7d09e to your computer and use it in GitHub Desktop.
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
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