File: instance_hooks.rb

package info (click to toggle)
ruby-sequel 5.63.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,408 kB
  • sloc: ruby: 113,747; makefile: 3
file content (115 lines) | stat: -rw-r--r-- 4,352 bytes parent folder | download | duplicates (4)
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
111
112
113
114
115
# frozen-string-literal: true

module Sequel
  module Plugins
    # The instance_hooks plugin allows you to add hooks to specific instances,
    # by passing a block to a _hook method (e.g. before_save_hook{do_something}).
    # The block is executed when the hook is called (e.g. before_save).
    #
    # All of the standard hooks are supported.
    # Instance level before hooks are executed in reverse order of addition before
    # calling super.  Instance level after hooks are executed in order of addition
    # after calling super.
    #
    # Instance level hooks for before and after are cleared after all related
    # after level instance hooks have run.  This means that if you add a before_create
    # and before_update instance hooks to a new object, the before_create hook will
    # be run the first time you save the object (creating it), and the before_update
    # hook will be run the second time you save the object (updating it), and no
    # hooks will be run the third time you save the object.
    #
    # Validation hooks are not cleared until after a successful save.
    # 
    # Usage:
    #
    #   # Add the instance hook methods to all model subclass instances (called before loading subclasses)
    #   Sequel::Model.plugin :instance_hooks
    #
    #   # Add the instance hook methods just to Album instances
    #   Album.plugin :instance_hooks
    module InstanceHooks
      module InstanceMethods 
        Sequel::Model::HOOKS.each{|h| class_eval(<<-END , __FILE__, __LINE__+1)}
          def #{h}_hook(&block)
            raise Sequel::Error, "can't add hooks to frozen object" if frozen?
            add_instance_hook(:#{h}, &block)
            self
          end
        END
        
        [:before_create, :before_update, :before_validation].each{|h| class_eval("def #{h}; run_before_instance_hooks(:#{h}) if @instance_hooks; super end", __FILE__, __LINE__)}
        [:after_create, :after_update].each{|h| class_eval(<<-END, __FILE__, __LINE__ + 1)}
          def #{h}
            super
            return unless @instance_hooks
            run_after_instance_hooks(:#{h})
            @instance_hooks.delete(:#{h})
            @instance_hooks.delete(:#{h.to_s.sub('after', 'before')})
          end
        END

        # Run after destroy instance hooks.
        def after_destroy
          super
          return unless @instance_hooks
          run_after_instance_hooks(:after_destroy)
          @instance_hooks.delete(:after_destroy)
          @instance_hooks.delete(:before_destroy)
        end

        # Run after validation instance hooks.
        def after_validation
          super
          return unless @instance_hooks
          run_after_instance_hooks(:after_validation)
        end
        
        # Run after save instance hooks.
        def after_save
          super
          return unless @instance_hooks
          run_after_instance_hooks(:after_save)
          @instance_hooks.delete(:after_save)
          @instance_hooks.delete(:before_save)
          @instance_hooks.delete(:after_validation)
          @instance_hooks.delete(:before_validation)
        end

        # Run before_destroy instance hooks.
        def before_destroy
          return super unless @instance_hooks
          run_before_instance_hooks(:before_destroy)
          super
        end

        # Run before_save instance hooks.
        def before_save
          return super unless @instance_hooks
          run_before_instance_hooks(:before_save)
          super
        end
        
        private
        
        # Add the block as an instance level hook.  For before hooks, add it to
        # the beginning of the instance hook's array.  For after hooks, add it
        # to the end.
        def add_instance_hook(hook, &block)
          instance_hooks(hook).public_send(hook.to_s.start_with?('before') ? :unshift : :push, block)
        end
        
        # An array of instance level hook blocks for the given hook type.
        def instance_hooks(hook)
          @instance_hooks ||= {}
          @instance_hooks[hook] ||= []
        end
        
        # Run all hook blocks of the given hook type.
        def run_after_instance_hooks(hook)
          instance_hooks(hook).each(&:call)
        end
        alias run_before_instance_hooks run_after_instance_hooks
      end
    end
  end
end