rails-observers
rails-observers copied to clipboard
Can't disable observers
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
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?
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.
How about ActiveRecord::Base.observers.disabled_for?(ProductObserver.new)
?
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
[]
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.
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?
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.
That sounds about right. If you can please send the PR.
I'm seeing this too, now. @JohnSmall still working on this?