# File lib/active_support/callbacks.rb, line 106
106: def initialize(chain, filter, kind, options, klass)
107: @chain, @kind, @klass = chain, kind, klass
108: normalize_options!(options)
109:
110: @per_key = options.delete(:per_key)
111: @raw_filter, @options = filter, options
112: @filter = _compile_filter(filter)
113: @compiled_options = _compile_options(options)
114: @callback_id = next_id
115:
116: _compile_per_key_options
117: end
# File lib/active_support/callbacks.rb, line 168
168: def _compile_per_key_options
169: key_options = _compile_options(@per_key)
170:
171: @klass.class_eval def _one_time_conditions_valid_#{@callback_id}? true #{key_options[0]} end, __FILE__, __LINE__ + 1
172: end
# File lib/active_support/callbacks.rb, line 153
153: def _update_filter(filter_options, new_options)
154: filter_options[:if].push(new_options[:unless]) if new_options.key?(:unless)
155: filter_options[:unless].push(new_options[:if]) if new_options.key?(:if)
156: end
# File lib/active_support/callbacks.rb, line 119
119: def clone(chain, klass)
120: obj = super()
121: obj.chain = chain
122: obj.klass = klass
123: obj.per_key = @per_key.dup
124: obj.options = @options.dup
125: obj.per_key[:if] = @per_key[:if].dup
126: obj.per_key[:unless] = @per_key[:unless].dup
127: obj.options[:if] = @options[:if].dup
128: obj.options[:unless] = @options[:unless].dup
129: obj
130: end
This will supply contents for around and after filters, but not before filters (for the backward pass).
# File lib/active_support/callbacks.rb, line 234
234: def end(key=nil, object=nil)
235: return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
236:
237: if @kind == :around || @kind == :after
238: # if condition # after_save :filter_name, :if => :condition
239: # filter_name
240: # end
241: if @kind == :after
242: [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
243: else
244: "end"
245: end
246: end
247: end
# File lib/active_support/callbacks.rb, line 149
149: def matches?(_kind, _filter)
150: @kind == _kind && @filter == _filter
151: end
# File lib/active_support/callbacks.rb, line 141
141: def name
142: chain.name
143: end
# File lib/active_support/callbacks.rb, line 145
145: def next_id
146: @@_callback_sequence += 1
147: end
# File lib/active_support/callbacks.rb, line 132
132: def normalize_options!(options)
133: options[:if] = Array.wrap(options[:if])
134: options[:unless] = Array.wrap(options[:unless])
135:
136: options[:per_key] ||= {}
137: options[:per_key][:if] = Array.wrap(options[:per_key][:if])
138: options[:per_key][:unless] = Array.wrap(options[:per_key][:unless])
139: end
# File lib/active_support/callbacks.rb, line 158
158: def recompile!(_options, _per_key)
159: _update_filter(self.options, _options)
160: _update_filter(self.per_key, _per_key)
161:
162: @callback_id = next_id
163: @filter = _compile_filter(@raw_filter)
164: @compiled_options = _compile_options(@options)
165: _compile_per_key_options
166: end
This will supply contents for before and around filters, and no contents for after filters (for the forward pass).
# File lib/active_support/callbacks.rb, line 180
180: def start(key=nil, object=nil)
181: return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
182:
183: # options[0] is the compiled form of supplied conditions
184: # options[1] is the "end" for the conditional
185: #
186: if @kind == :before || @kind == :around
187: if @kind == :before
188: # if condition # before_save :filter_name, :if => :condition
189: # filter_name
190: # end
191: filter = unless halted result = #{@filter} halted = (#{chain.config[:terminator]}) end
192:
193: [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
194: else
195: # Compile around filters with conditions into proxy methods
196: # that contain the conditions.
197: #
198: # For `around_save :filter_name, :if => :condition':
199: #
200: # def _conditional_callback_save_17
201: # if condition
202: # filter_name do
203: # yield self
204: # end
205: # else
206: # yield self
207: # end
208: # end
209: #
210: name = "_conditional_callback_#{@kind}_#{next_id}"
211: @klass.class_eval def #{name}(halted) #{@compiled_options[0] || "if true"} && !halted #{@filter} do yield self end else yield self end end, __FILE__, __LINE__ + 1
212: "#{name}(halted) do"
213: end
214: end
215: end
Filters support:
Arrays:: Used in conditions. This is used to specify
multiple conditions. Used internally to
merge conditions from skip_* filters
Symbols:: A method to call
Strings:: Some content to evaluate
Procs:: A proc to call with the object
Objects:: An object with a before_foo method on it to call
All of these objects are compiled into methods and handled the same after this point:
Arrays:: Merged together into a single filter
Symbols:: Already methods
Strings:: class_eval'ed into methods
Procs:: define_method'ed into methods
Objects::
a method is created that calls the before_foo method
on the object.
# File lib/active_support/callbacks.rb, line 291
291: def _compile_filter(filter)
292: method_name = "_callback_#{@kind}_#{next_id}"
293: case filter
294: when Array
295: filter.map {|f| _compile_filter(f)}
296: when Symbol
297: filter
298: when String
299: "(#{filter})"
300: when Proc
301: @klass.send(:define_method, method_name, &filter)
302: return method_name if filter.arity <= 0
303:
304: method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
305: else
306: @klass.send(:define_method, "#{method_name}_object") { filter }
307:
308: _normalize_legacy_filter(kind, filter)
309: scopes = Array.wrap(chain.config[:scope])
310: method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
311:
312: @klass.class_eval def #{method_name}(&blk) #{method_name}_object.send(:#{method_to_call}, self, &blk) end, __FILE__, __LINE__ + 1
313:
314: method_name
315: end
316: end
Options support the same options as filters themselves (and support symbols, string, procs, and objects), so compile a conditional expression based on the options
# File lib/active_support/callbacks.rb, line 254
254: def _compile_options(options)
255: return [] if options[:if].empty? && options[:unless].empty?
256:
257: conditions = []
258:
259: unless options[:if].empty?
260: conditions << Array.wrap(_compile_filter(options[:if]))
261: end
262:
263: unless options[:unless].empty?
264: conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
265: end
266:
267: ["if #{conditions.flatten.join(" && ")}", "end"]
268: end
# File lib/active_support/callbacks.rb, line 322
322: def _normalize_legacy_filter(kind, filter)
323: if !filter.respond_to?(kind) && filter.respond_to?(:filter)
324: filter.singleton_class.class_eval def #{kind}(context, &block) filter(context, &block) end, __FILE__, __LINE__ + 1
325: elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around
326: def filter.around(context)
327: should_continue = before(context)
328: yield if should_continue
329: after(context)
330: end
331: end
332: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.