File: throw_failures.rb

package info (click to toggle)
ruby-sequel 5.63.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 10,408 kB
  • sloc: ruby: 113,747; makefile: 3
file content (110 lines) | stat: -rw-r--r-- 3,774 bytes parent folder | download | duplicates (3)
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