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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
require 'active_support/core_ext/array/wrap'
require 'active_support/callbacks'
module ActiveModel
# == Active Model Callbacks
#
# Provides an interface for any class to have Active Record like callbacks.
#
# Like the Active Record methods, the call back chain is aborted as soon as
# one of the methods in the chain returns false.
#
# First, extend ActiveModel::Callbacks from the class you are creating:
#
# class MyModel
# extend ActiveModel::Callbacks
# end
#
# Then define a list of methods that you want call backs attached to:
#
# define_model_callbacks :create, :update
#
# This will provide all three standard callbacks (before, around and after) around
# both the :create and :update methods. To implement, you need to wrap the methods
# you want call backs on in a block so that the call backs get a chance to fire:
#
# def create
# _run_create_callbacks do
# # Your create action methods here
# end
# end
#
# The _run_<method_name>_callbacks methods are dynamically created when you extend
# the <tt>ActiveModel::Callbacks</tt> module.
#
# Then in your class, you can use the +before_create+, +after_create+ and +around_create+
# methods, just as you would in an Active Record module.
#
# before_create :action_before_create
#
# def action_before_create
# # Your code here
# end
#
# You can choose not to have all three callbacks by passing an hash to the
# define_model_callbacks method.
#
# define_model_callbacks :create, :only => :after, :before
#
# Would only create the after_create and before_create callback methods in your
# class.
module Callbacks
def self.extended(base)
base.class_eval do
include ActiveSupport::Callbacks
end
end
# 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
#
# Note, the <tt>:only => <type></tt> hash will apply to all callbacks defined on
# that method call. To get around this you can call the define_model_callbacks
# method as many times as you need.
#
# define_model_callbacks :create, :only => :after
# define_model_callbacks :update, :only => :before
# define_model_callbacks :destroy, :only => :around
#
# Would create +after_create+, +before_update+ and +around_destroy+ methods only.
#
# You can pass in a class to before_<type>, after_<type> and around_<type>, in which
# case the call back will call that class's <action>_<type> method passing the object
# that the callback is being called on.
#
# class MyModel
# extend ActiveModel::Callbacks
# define_model_callbacks :create
#
# before_create AnotherClass
# end
#
# class AnotherClass
# def self.before_create( obj )
# # obj is the MyModel instance that the callback is being called on
# end
# end
#
def define_model_callbacks(*callbacks)
options = callbacks.extract_options!
options = { :terminator => "result == false", :scope => [:kind, :name] }.merge(options)
types = Array.wrap(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.wrap(options[:if]) << "!halted && value != false"
set_callback(:#{callback}, :after, *(args << options), &block)
end
CALLBACK
end
end
end
|