Skip to content

Commit

Permalink
Merge b49fd63 into 461f00c
Browse files Browse the repository at this point in the history
  • Loading branch information
hlegius committed Feb 1, 2016
2 parents 461f00c + b49fd63 commit 524143a
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 101 deletions.
36 changes: 36 additions & 0 deletions lib/hanami/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,42 @@ def initialize(message = "Invalid query")
end
end

# Error for Unique Constraint Violation
#
# @since x.x.x
class UniqueConstraintViolationError < Hanami::Model::Error
def initialize(message = "Unique constraint has been violated")
super
end
end

# Error for Foreign Key Constraint Violation
#
# @since x.x.x
class ForeignKeyConstraintViolationError < Hanami::Model::Error
def initialize(message = "Foreign key constraint has been violated")
super
end
end

# Error for Not Null Constraint Violation
#
# @since x.x.x
class NotNullConstraintViolationError < Hanami::Model::Error
def initialize(message = "NOT NULL constraint has been violated")
super
end
end

# Error for Check Constraint Violation raised by Sequel
#
# @since x.x.x
class CheckConstraintViolationError < Hanami::Model::Error
def initialize(message = "Check constraint has been violated")
super
end
end

include Utils::ClassAttribute

# Framework configuration
Expand Down
46 changes: 36 additions & 10 deletions lib/hanami/model/adapters/sql/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ def initialize(query)
# @api private
# @since 0.1.0
def create(entity)
@collection.insert(entity)
rescue Sequel::DatabaseError => e
raise Hanami::Model::Error.new(e.message)
_handle_database_error { @collection.insert(entity) }
end

# Updates the corresponding record for the given entity.
Expand All @@ -44,9 +42,7 @@ def create(entity)
# @api private
# @since 0.1.0
def update(entity)
@collection.update(entity)
rescue Sequel::DatabaseError => e
raise Hanami::Model::Error.new(e.message)
_handle_database_error { @collection.update(entity) }
end

# Deletes all the records for the current query.
Expand All @@ -59,15 +55,45 @@ def update(entity)
# @api private
# @since 0.1.0
def delete
@collection.delete
rescue Sequel::DatabaseError => e
raise Hanami::Model::Error.new(e.message)
_handle_database_error { @collection.delete }
end

alias_method :clear, :delete

private

# Handles any possible Adapter's Database Error
#
# @api private
# @since x.x.x
def _handle_database_error
yield
rescue Sequel::DatabaseError => e
raise _mapping_sequel_to_model(e)
end

# Maps SQL Adapter Violation's Errors into Hanami::Model Specific Errors
#
# @param adapter error [Object]
#
# @api private
# @since x.x.x
def _mapping_sequel_to_model(error)
case error
when Sequel::CheckConstraintViolation
Hanami::Model::CheckConstraintViolationError.new(error.message)
when Sequel::ForeignKeyConstraintViolation
Hanami::Model::ForeignKeyConstraintViolationError.new(error.message)
when Sequel::NotNullConstraintViolation
Hanami::Model::NotNullConstraintViolationError.new(error.message)
when Sequel::UniqueConstraintViolation
Hanami::Model::UniqueConstraintViolationError.new(error.message)
else
Hanami::Model::InvalidCommandError.new(error.message)
end
end
end
end
end
end
end

4 changes: 4 additions & 0 deletions test/error_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
Hanami::Model::NonPersistedEntityError.superclass.must_equal Hanami::Model::Error
Hanami::Model::InvalidMappingError.superclass.must_equal Hanami::Model::Error
Hanami::Model::InvalidCommandError.superclass.must_equal Hanami::Model::Error
Hanami::Model::CheckConstraintViolationError.superclass.must_equal Hanami::Model::Error
Hanami::Model::ForeignKeyConstraintViolationError.superclass.must_equal Hanami::Model::Error
Hanami::Model::NotNullConstraintViolationError.superclass.must_equal Hanami::Model::Error
Hanami::Model::UniqueConstraintViolationError.superclass.must_equal Hanami::Model::Error
Hanami::Model::InvalidQueryError.superclass.must_equal Hanami::Model::Error
Hanami::Model::Adapters::DatabaseAdapterNotFound.superclass.must_equal Hanami::Model::Error
Hanami::Model::Adapters::NotSupportedError.superclass.must_equal Hanami::Model::Error
Expand Down
132 changes: 41 additions & 91 deletions test/model/adapters/sql/command_test.rb
Original file line number Diff line number Diff line change
@@ -1,121 +1,71 @@
require 'test_helper'

SEQUEL_TO_HANAMI_MAPPING = {
'Sequel::UniqueConstraintViolation' => 'Hanami::Model::UniqueConstraintViolationError',
'Sequel::ForeignKeyConstraintViolation' => 'Hanami::Model::ForeignKeyConstraintViolationError',
'Sequel::NotNullConstraintViolation' => 'Hanami::Model::NotNullConstraintViolationError',
'Sequel::CheckConstraintViolation' => 'Hanami::Model::CheckConstraintViolationError'
}

describe Hanami::Model::Adapters::Sql::Command do
let(:collection) { Object.new }
let(:query) do
query = Object.new
query.instance_variable_set(:@collection, collection)
query.define_singleton_method(:scoped, -> { @collection })
query
end
describe 'when #create raises Sequel Database Violation Error' do
SEQUEL_TO_HANAMI_MAPPING.each do |sequel_error, hanami_error|

before do
@command = Hanami::Model::Adapters::Sql::Command.new(query)
end
it "raises #{hanami_error} instead of #{sequel_error}" do
query = SqlQueryFake.new
command = Hanami::Model::Adapters::Sql::Command.new(query)

describe '#create' do
describe 'when a Sequel::ForeignKeyConstraintViolation is raised' do
it 'raises Hanami::Model::Error exception' do
collection.define_singleton_method(:insert) do |_|
raise ::Sequel::ForeignKeyConstraintViolation.new('fkey constraint error')
query.scoped.stub(:insert, ->(entity) { raise Object.const_get(sequel_error).new }) do
assert_raises(Object.const_get(hanami_error)) { command.create(Object.new) }
end
exception = -> { @command.create(Object.new) }.must_raise(Hanami::Model::Error)
exception.message.must_equal('fkey constraint error')
end
end

describe 'when a Sequel::CheckConstraintViolation is raised' do
it 'raises Hanami::Model::Error exception' do
collection.define_singleton_method(:insert) do |_|
raise ::Sequel::CheckConstraintViolation.new('check constraint error')
end
exception = -> { @command.create(Object.new) }.must_raise(Hanami::Model::Error)
exception.message.must_equal('check constraint error')
end
end
end

describe 'when a Sequel::NotNullConstraintViolation is raised' do
it 'raises Hanami::Model::Error exception' do
collection.define_singleton_method(:insert) do |_|
raise ::Sequel::NotNullConstraintViolation.new('not null constraint error')
end
exception = -> { @command.create(Object.new) }.must_raise(Hanami::Model::Error)
exception.message.must_equal('not null constraint error')
end
end
describe 'when #update raises Sequel Database Violation Error' do
SEQUEL_TO_HANAMI_MAPPING.each do |sequel_error, hanami_error|

describe 'when a Sequel::UniqueConstraintViolation is raised' do
it 'raises Hanami::Model::Error exception' do
collection.define_singleton_method(:insert) do |_|
raise ::Sequel::UniqueConstraintViolation.new('unique constraint error')
end
exception = -> { @command.create(Object.new) }.must_raise(Hanami::Model::Error)
exception.message.must_equal('unique constraint error')
end
end
it "raises #{hanami_error} instead of #{sequel_error}" do
query = SqlQueryFake.new
command = Hanami::Model::Adapters::Sql::Command.new(query)

describe 'when a Sequel::DatabaseError is raised' do
it 'raises Hanami::Model::Error exception' do
collection.define_singleton_method(:insert) do |_|
raise ::Sequel::DatabaseError.new('db error')
query.scoped.stub(:update, ->(entity) { raise Object.const_get(sequel_error).new }) do
assert_raises(Object.const_get(hanami_error)) { command.update(Object.new) }
end
exception = -> { @command.create(Object.new) }.must_raise(Hanami::Model::Error)
exception.message.must_equal('db error')
end
end

describe 'when an error that is not inherited from Sequel::DatabaseError is raised' do
it 'bubbles the error up' do
collection.define_singleton_method(:insert) do |_|
raise Sequel::Error.new('constraint error')
end
exception = -> { @command.create(Object.new) }.must_raise(Sequel::Error)
exception.message.must_equal('constraint error')
end
end
end

describe '#update' do
describe 'when a Sequel::DatabaseError is raised' do
it 'raises Hanami::Model::Error exception' do
collection.define_singleton_method(:update) do |_|
raise Sequel::DatabaseError.new('db error')
describe 'when #delete raises Sequel Database Violation Error' do
SEQUEL_TO_HANAMI_MAPPING.each do |sequel_error, hanami_error|

it "raises #{hanami_error} instead of #{sequel_error}" do
query = SqlQueryFake.new
command = Hanami::Model::Adapters::Sql::Command.new(query)

query.scoped.stub(:delete, -> { raise Object.const_get(sequel_error).new }) do
assert_raises(Object.const_get(hanami_error)) { command.delete }
end
exception = -> { @command.update(Object.new) }.must_raise(Hanami::Model::Error)
exception.message.must_equal('db error')
end
end
end

describe 'when a different error is raised' do
it 'bubbles the error up' do
collection.define_singleton_method(:update) do |_|
raise Sequel::Error.new('constraint error')
end
exception = -> { @command.update(Object.new) }.must_raise(Sequel::Error)
exception.message.must_equal('constraint error')
end
class SqlQueryFake
def scoped
@collection ||= CollectionFake.new
end
end

describe '#delete' do
describe 'when a Sequel::DatabaseError is raised' do
it 'raises Hanami::Model::Error exception' do
collection.define_singleton_method(:delete) do
raise Sequel::DatabaseError.new('db error')
end
exception = -> { @command.delete }.must_raise(Hanami::Model::Error)
exception.message.must_equal('db error')
end
class CollectionFake
def insert(entity)
end

describe 'when a different error is raised' do
it 'bubbles the error up' do
collection.define_singleton_method(:delete) do
raise Sequel::Error.new('constraint error')
end
exception = -> { @command.delete }.must_raise(Sequel::Error)
exception.message.must_equal('constraint error')
end
def update(entity)
end

def delete
end
end
end

0 comments on commit 524143a

Please sign in to comment.