# 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.