lundi 2 mars 2015

Rails controller filters magically reappearing after having been removed

I'm seeing very odd behaviour, where whether or not a filter is run seems to depend on whether it was set to run in the first spec to use that controller.


So I've reduced this down to a minimal controller:



class WtfController < ActionController::Base
before_filter :raise_error

def index
render nothing: true, status: 200
end

private
def raise_error
raise OMGError
end
end


(OMGError just subclasses StandardError)


And a test case:



describe 'wtfspec' do
it 'with filter' do
expect do
controller = set_up_controller
controller.process(:index)
end.to raise_error
end

it 'without filter' do
expect do
controller = set_up_controller
controller.class.skip_filter :raise_error
controller.process(:index)
end.not_to raise_error
end
end

def set_up_controller
controller = WtfController.new
controller.request = ActionController::TestRequest.new
controller.response = ActionController::TestResponse.new
controller
end


The filter should be skipped in the second test, but it isn't.


If the first expect block is not run, then the second test passes. It's only if it is run that the second fails. Similarly, if I skip_filter in the first test and use before_filter to readd it for the second test, the second fails to run the filter even though it should.


Here's the thing - if I examine the list of filters set on WtfController (using controller.class._process_action_callbacks.map(&:filters)) in the spec (in the second test) right before the controller.process line, then it has indeed been successfully removed, the skip_filter line works fine. But if I examine it inside the controller (in raise_error), it seems to have somehow been readded!


Note 'readded', not 'reappeared': The object_id of the raise_error callback that actually runs is different to the original one (the one that was there before running skip_filter). But the object_id of the array of filters (actually an ActiveSupport::Callbacks::CallbackChain) remains the same, as is the controller.


(Edit: turns out all the callback objects have different object_ids, not just the removed one. The mystery deepens...)


The behaviour is the same whether each of the two tests new's up a new controller object or reuses the same one (as you'd expect, since the filters are a property of the WtfController class object, not the instances).


I've spent a day debugging this and am utterly mystified. Any insight appreciated. Rails version is 3.2.21. Thanks :)


Aucun commentaire:

Enregistrer un commentaire