Skip to content

Commit

Permalink
Fixes skipping object callback filters
Browse files Browse the repository at this point in the history
This allows you to skip callbacks that are defined by objects, e.g. for
`ActionController`:

    skip_after_filter MySpecialFilter

Previously this didn't work due to a bug in how Rails compared callbacks
in `Callback#matches?`. When a callback is compiled, if it's an object
filter (i.e. not a method, proc, etc.), `Callback` now defines a method on
`@klass` that is derived from the class name rather than `@callback_id`.
So, when `skip_callback` tries to find the appropriate callback to
remove, `Callback` can regenerate the method name for the filter
object and return the correct value for `Callback#matches?`.
  • Loading branch information
benmcredmond committed Apr 4, 2013
1 parent e456ad5 commit e377228
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 1 deletion.
3 changes: 3 additions & 0 deletions activesupport/CHANGELOG.md
Expand Up @@ -14,6 +14,9 @@


*Charles Jones* *Charles Jones*


* Fix skipping of filters defined by objects in `ActiveSupport::Callbacks::Callback`.

*Ben McRedmond*


## Rails 4.0.0.beta1 (February 25, 2013) ## ## Rails 4.0.0.beta1 (February 25, 2013) ##


Expand Down
14 changes: 13 additions & 1 deletion activesupport/lib/active_support/callbacks.rb
Expand Up @@ -131,6 +131,7 @@ def next_id
end end


def matches?(_kind, _filter) def matches?(_kind, _filter)
_filter = _method_name_for_object_filter(_kind, _filter) if @_is_object_filter
@kind == _kind && @filter == _filter @kind == _kind && @filter == _filter
end end


Expand Down Expand Up @@ -234,6 +235,14 @@ def recompile_options!
@compiled_options = conditions.flatten.join(" && ") @compiled_options = conditions.flatten.join(" && ")
end end


def _method_name_for_object_filter(kind, filter)
class_name = filter.kind_of?(Class) ? filter.to_s : filter.class.to_s
class_name.gsub!(/<|>|#/, '')
class_name.gsub!(/\/|:/, "_")

"_callback_#{kind}_#{class_name}"
end

# Filters support: # Filters support:
# #
# Arrays:: Used in conditions. This is used to specify # Arrays:: Used in conditions. This is used to specify
Expand All @@ -255,6 +264,8 @@ def recompile_options!
# a method is created that calls the before_foo method # a method is created that calls the before_foo method
# on the object. # on the object.
def _compile_filter(filter) def _compile_filter(filter)
@_is_object_filter = false

case filter case filter
when Array when Array
filter.map {|f| _compile_filter(f)} filter.map {|f| _compile_filter(f)}
Expand All @@ -269,7 +280,8 @@ def _compile_filter(filter)


method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ") method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
else else
method_name = "_callback_#{@kind}_#{next_id}" method_name = _method_name_for_object_filter(kind, filter)
@_is_object_filter = true
@klass.send(:define_method, "#{method_name}_object") { filter } @klass.send(:define_method, "#{method_name}_object") { filter }


_normalize_legacy_filter(kind, filter) _normalize_legacy_filter(kind, filter)
Expand Down
17 changes: 17 additions & 0 deletions activesupport/test/callbacks_test.rb
Expand Up @@ -66,13 +66,24 @@ def history
end end
end end


class CallbackClass
def self.before(model)
model.history << [:before_save, :class]
end

def self.after(model)
model.history << [:after_save, :class]
end
end

class Person < Record class Person < Record
[:before_save, :after_save].each do |callback_method| [:before_save, :after_save].each do |callback_method|
callback_method_sym = callback_method.to_sym callback_method_sym = callback_method.to_sym
send(callback_method, callback_symbol(callback_method_sym)) send(callback_method, callback_symbol(callback_method_sym))
send(callback_method, callback_string(callback_method_sym)) send(callback_method, callback_string(callback_method_sym))
send(callback_method, callback_proc(callback_method_sym)) send(callback_method, callback_proc(callback_method_sym))
send(callback_method, callback_object(callback_method_sym.to_s.gsub(/_save/, ''))) send(callback_method, callback_object(callback_method_sym.to_s.gsub(/_save/, '')))
send(callback_method, CallbackClass)
send(callback_method) { |model| model.history << [callback_method_sym, :block] } send(callback_method) { |model| model.history << [callback_method_sym, :block] }
end end


Expand All @@ -86,6 +97,7 @@ class PersonSkipper < Person
skip_callback :save, :after, :before_save_method, :unless => :yes skip_callback :save, :after, :before_save_method, :unless => :yes
skip_callback :save, :after, :before_save_method, :if => :no skip_callback :save, :after, :before_save_method, :if => :no
skip_callback :save, :before, :before_save_method, :unless => :no skip_callback :save, :before, :before_save_method, :unless => :no
skip_callback :save, :before, CallbackClass , :if => :yes
def yes; true; end def yes; true; end
def no; false; end def no; false; end
end end
Expand Down Expand Up @@ -430,6 +442,7 @@ def test_skip_person
[:before_save, :object], [:before_save, :object],
[:before_save, :block], [:before_save, :block],
[:after_save, :block], [:after_save, :block],
[:after_save, :class],
[:after_save, :object], [:after_save, :object],
[:after_save, :proc], [:after_save, :proc],
[:after_save, :string], [:after_save, :string],
Expand All @@ -449,8 +462,10 @@ def test_save_person
[:before_save, :string], [:before_save, :string],
[:before_save, :proc], [:before_save, :proc],
[:before_save, :object], [:before_save, :object],
[:before_save, :class],
[:before_save, :block], [:before_save, :block],
[:after_save, :block], [:after_save, :block],
[:after_save, :class],
[:after_save, :object], [:after_save, :object],
[:after_save, :proc], [:after_save, :proc],
[:after_save, :string], [:after_save, :string],
Expand Down Expand Up @@ -715,8 +730,10 @@ def test_skip_writer
[:before_save, :string], [:before_save, :string],
[:before_save, :proc], [:before_save, :proc],
[:before_save, :object], [:before_save, :object],
[:before_save, :class],
[:before_save, :block], [:before_save, :block],
[:after_save, :block], [:after_save, :block],
[:after_save, :class],
[:after_save, :object], [:after_save, :object],
[:after_save, :proc], [:after_save, :proc],
[:after_save, :string], [:after_save, :string],
Expand Down

0 comments on commit e377228

Please sign in to comment.