1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
# frozen-string-literal: true
module Sequel
module Plugins
# The throw_failures plugin throws HookFailed and ValidationFailed exceptions instead
# of raising them. If there is no matching catch block, the UncaughtThrowError will be rescued
# and the HookFailed or ValidationFailed exception will be raised normally.
#
# If you are setting up the catch blocks to handle these failures, in the failure case this
# plugin is about 10-15% faster on CRuby and 10x faster on JRuby. If you are not
# setting up the catch blocks, in the failure case this plugin is about 30% slower on CRuby
# and 2x slower on JRuby. So this plugin should only be used if you are setting up catch
# blocks manually.
#
# This plugin will setup catch blocks automatically for internally rescued HookFailed
# exceptions when the model is configured to not raise exceptions on failure (by default,
# the exceptions are internally rescued in that case.
#
# To set up the catch blocks, use the class of the exception:
#
# ret = catch(Sequel::ValidationFailed) do
# model_instance.save
# end
# if ret.is_a?(Sequel::ValidationFailed)
# # handle failure
# else
# # handle success
# end
#
# Usage:
#
# # Make all model subclass instances throw HookFailed and ValidationFailed exceptions
# # (called before loading subclasses)
# Sequel::Model.plugin :throw_failures
#
# # Make the Album class throw HookFailed and ValidationFailed exceptions
# Album.plugin :throw_failures
module ThrowFailures
module InstanceMethods
# Catch any thrown HookFailed exceptions.
def valid?(opts = OPTS)
catch_hook_failures{super} || false
end
private
# Catch any HookFailed exceptions thrown inside the block, and return
# nil if there were any.
def catch_hook_failures
called = ret = nil
catch(HookFailed) do
ret = yield
called = true
end
ret if called
end
# Catch any thrown HookFailed exceptions if not raising on failure.
def checked_save_failure(opts)
if raise_on_failure?(opts)
super
else
catch_hook_failures{super}
end
end
if RUBY_VERSION >= '2.2' && (!defined?(JRUBY_VERSION) || JRUBY_VERSION > '9.1')
# Throw HookFailed with the generated error. If the throw is not
# caught, just return the originally generated error.
def hook_failed_error(msg)
e = super
throw HookFailed, e
rescue UncaughtThrowError
e
end
# Throw ValidationFailed with the generated error. If the throw is not
# caught, just return the originally generated error.
def validation_failed_error
e = super
throw ValidationFailed, e
rescue UncaughtThrowError
e
end
else
# UncaughtThrowError was added in Ruby 2.2. Older Ruby versions
# used ArgumentError with "uncaught throw" at the start of the message
# :nocov:
def hook_failed_error(msg)
e = super
throw HookFailed, e
rescue ArgumentError => e2
raise e2 unless e2.message.start_with?('uncaught throw')
e
end
def validation_failed_error
e = super
throw ValidationFailed, e
rescue ArgumentError => e2
raise e2 unless e2.message.start_with?('uncaught throw')
e
end
# :nocov:
end
end
end
end
end
|