Skip to content

Instantly share code, notes, and snippets.

@mdesantis
Last active August 29, 2015 14:11
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 mdesantis/fca57d9efa4f2f4d73ab to your computer and use it in GitHub Desktop.
Save mdesantis/fca57d9efa4f2f4d73ab to your computer and use it in GitHub Desktop.
PostgreSQL unique constraint handler: it sets the record as invalid if a PostgreSQL unique constraint error is raised on record saving
# Usage example:
#
# class User < ActiveRecord::Base
# include UniqueConstraintHandler
# handle_unique_constraint_on :email
# end
#
# User.create email: 'test@example.com'
# user = User.create email: 'test@example.com'
# user.new_record? #=> true
# user.errors.added? :email, :taken #=> true
module UniqueConstraintHandler
UNIQUE_CONSTRAINT_ERROR_REGEX_TEMPLATE =
%[\\Aduplicate key value violates unique constraint "%s"\\z]
extend ActiveSupport::Concern
module ClassMethods
def handle_unique_constraint_on(attribute, options = {})
index_name = options[:index_name] || "index_#{table_name}_on_#{attribute}"
regexp = Regexp.new UNIQUE_CONSTRAINT_ERROR_REGEX_TEMPLATE % index_name
prepend Module.new {
private
define_method :create_or_update do
begin
super()
rescue ActiveRecord::RecordNotUnique => exception
raise exception unless _unique_constraint_error? exception, regexp
errors.add attribute, :taken
false
end
end
}
end
end
private
def _unique_constraint_error?(exception, regexp)
original_exception = exception.try(:original_exception) and
original_exception.is_a?(PG::UniqueViolation) and
result = original_exception.try(:result) and
error_primary_message = result.error_field(PG::Result::PG_DIAG_MESSAGE_PRIMARY) and
error_primary_message =~ regexp
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment