rails / rails-observers

Rails observer (removed from core in Rails 4.0)
MIT License
519 stars 119 forks source link

Can't disable observers #73

Open JohnSmall opened 6 years ago

JohnSmall commented 6 years ago

I'm going mad.

In most of my Rspec specs I want observers to be disabled, except for some. But

 ActiveRecord::Base.observers.disable :all

is not working.

It goes through the motions, something happens, I can see all the observers in the disabled_observers array but the observers still trigger.

It's driving me nuts. However I can see that the gem tests for disable are passing. It must be that the tests are not testing real things

rafaelfranca commented 6 years ago

Can you try to debug to see if the disable_for? method is retuning the expected values? Also if it is being called at all?

JohnSmall commented 6 years ago

I've forked the project and I'm digging into it now. But I'm used to being able run these commands

bundle install
rake

and expect it to work. But rake is failing with this error

LoadError: cannot load such file -- bundler/gem_tasks

But, using rails command line to debug a bit I get this (Rails 5.0.2)

ActiveRecord::Base.observers
 [:account_observer, :order_observer, :customer_order_observer, :product_observer]

ActiveRecord::Base.observers.send(:disabled_observers)
#<Set: {}>

(you can use #send(:method_name) to access a protected/private method)

 ActiveRecord::Base.observers.disabled_for?(Product)
 => false 

Then I disable all observers

    ActiveRecord::Base.observers.disable :all
    lots of output

   ActiveRecord::Base.observers.send(:disabled_observers)
    => #<Set: {ActiveRecord::Observer, ActionController::Caching::Sweeper, AccountObserver, 
     OrderObserver, CustomerOrderObserver, ProductObserver}> 

But

 ActiveRecord::Base.observers.disabled_for?(Product)
 false

and

ActiveRecord::Base.observers.disabled_for?(:product)
false 

and

ActiveRecord::Base.observers.disabled_for?(ProductObserver)
false

So it looks like the problem is in #disabled_for?. It always returns false even if the observer is disabled.

rafaelfranca commented 6 years ago

How about ActiveRecord::Base.observers.disabled_for?(ProductObserver.new)?

JohnSmall commented 6 years ago

Sorry for not replying earlier. I've been away for a few days.

With

ActiveRecord::Base.observers.disabled_for?(ProductObserver.new)

I get

NoMethodError: private method `new' called for ProductObserver:Class

But with

ActiveRecord::Base.observers.disabled_for?(ProductObserver.send(:new))

I get

true

Horray!! A result.

However when the code is running for real, it obviously doesn't check the product observer instance in the right place. Otherwise it wouldn't be running the code in the observers when they've been disabled.

I see that there are two methods #disabled_for? maybe the wrong one gets called.

I also notice that

 Product.observers

Is always

 []
JohnSmall commented 6 years ago

How do I run the test suite? I'm expecting to be able to type

bundle install
rake

And it will just work. But it isn't.

JohnSmall commented 6 years ago

Ok, I've progressed to the next error. I had to reinstall bundler. No when I type rake I get

Could not load 'active_record/connection_adapters/sqlite3_adapter'. 
Make sure that the adapter in config/database.yml is valid. 
If you use an adapter other than 'mysql2', 'postgresql' or 'sqlite3' add 
the necessary adapter gem to the Gemfile. (LoadError)

(newlines added by me to make it easier to read)

But if I create a new Rails 5 project, it defaults to sqlite3 and runs first time without a problem. So I do have sqlite3 installed. If I pause the tests right after it's created the new Rails project, and look for the Rails project then database.yml contains this

 default: &default
     adapter: sqlite3

As I expect it to, and the gemspec contains this

s.add_development_dependency 'sqlite3',        '>= 1.3'

So I can't see why it's complaining about the sqlite3 gem being missing, unless the test rails application isn't loading the gem in development mode. But I see that the line that creates the temporary Rails app

  `rails new #{app_template_path} --skip-gemfile --skip-listen`

Skips the gemfile, which is obviously why I'm getting the error

 Could not load 'active_record/connection_adapters/sqlite3_adapter'. 

So how can you ever have got the tests to run?

JohnSmall commented 6 years ago

Ahh!!! I think I've worked it out. Single Table Inheritance is not respected when disabling observers.

I have

class PhysicalProduct < Product etc

The observer is ProductObserver and it handles callbacks for Product and PhysicalProduct. If I disable `ProductObserver' then

Product.observers.disabled_for?(ProductObserver.send(:new))
true

but

 PhysicalProduct.observers.disabled_for?(ProductObserver.send(:new))
 false

So the callbacks in ProductObserver continue to be called from PhysicalProduct even after I've disabled ProductObserver

My workaround is to rename ProductObserver to PhysicalProductObserver, then when it's disabled I get

 PhysicalProduct.observers.disabled_for?(PhysicalProductObserver.send(:new))
 true

I would write a failing test, fix the code to make the test pass, and send in a PR but I can't get the test suite to run.

rafaelfranca commented 6 years ago

That sounds about right. If you can please send the PR.

malachaifrazier commented 5 years ago

I'm seeing this too, now. @JohnSmall still working on this?