# 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