aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model/callbacks.rb
blob: f66a1ddcaa8472e15f780e46457a73c120081190 (plain) (blame)
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
require 'active_support/callbacks'

module ActiveModel
  module Callbacks
    def self.extended(base)
      base.class_eval do
        include ActiveSupport::Callbacks
      end
    end

    # Define callbacks similar to ActiveRecord ones. It means:
    #
    # * The callback chain is aborted whenever the block given to
    #   _run_callbacks returns false.
    #
    # * If a class is given to the fallback, it will search for
    #   before_create, around_create and after_create methods.
    #
    # == Usage
    #
    # First you need to define which callbacks your model will have:
    #
    #   class MyModel
    #     define_model_callbacks :create
    #   end
    #
    # This will define three class methods: before_create, around_create,
    # and after_create. They accept a symbol, a string, an object or a block.
    #
    # After you create a callback, you need to tell when they are executed.
    # For example, you could do:
    #
    #   def create
    #     _run_create_callbacks do
    #       super
    #     end
    #   end
    # 
    # == Options
    #
    # define_model_callbacks accepts all options define_callbacks does, in
    # case you want to overwrite a default. Besides that, it also accepts
    # an :only option, where you can choose if you want all types (before,
    # around or after) or just some:
    #
    #   define_model_callbacks :initializer, :only => :after
    #
    def define_model_callbacks(*callbacks)
      options = callbacks.extract_options!
      options = { :terminator => "result == false", :scope => [:kind, :name] }.merge(options)

      types = Array(options.delete(:only))
      types = [:before, :around, :after] if types.empty?

      callbacks.each do |callback|
        define_callbacks(callback, options)

        types.each do |type|
          send(:"_define_#{type}_model_callback", self, callback)
        end
      end
    end

    def _define_before_model_callback(klass, callback) #:nodoc:
      klass.class_eval <<-CALLBACK, __FILE__, __LINE__
        def self.before_#{callback}(*args, &block)
          set_callback(:#{callback}, :before, *args, &block)
        end
      CALLBACK
    end

    def _define_around_model_callback(klass, callback) #:nodoc:
      klass.class_eval <<-CALLBACK, __FILE__, __LINE__
        def self.around_#{callback}(*args, &block)
          set_callback(:#{callback}, :around, *args, &block)
        end
      CALLBACK
    end

    def _define_after_model_callback(klass, callback) #:nodoc:
      klass.class_eval <<-CALLBACK, __FILE__, __LINE__
        def self.after_#{callback}(*args, &block)
          options = args.extract_options!
          options[:prepend] = true
          options[:if] = Array(options[:if]) << "!halted && value != false"
          set_callback(:#{callback}, :after, *(args << options), &block)
        end
      CALLBACK
    end
  end
end