40

I'm using restful_authentication in my app. I'm creating a set of default users using a rake task, but every time I run the task an activation email is sent out because of the observer associated with my user model. I'm setting the activation fields when I create the users, so no activation is necessary.

Anyone know of an easy way to bypass observers while running a rake task so that no emails get sent out when I save the user?

Thanks.

MediaJunkie
  • 791
  • 1
  • 7
  • 18

12 Answers12

124

Rails 3.1 finally comes with API for this: http://api.rubyonrails.org/v3.1.0/classes/ActiveModel/ObserverArray.html#method-i-disable

ORM.observers.disable :user_observer
  # => disables the UserObserver

User.observers.disable AuditTrail
  # => disables the AuditTrail observer for User notifications.
  #    Other models will still notify the AuditTrail observer.

ORM.observers.disable :observer_1, :observer_2
  # => disables Observer1 and Observer2 for all models.

ORM.observers.disable :all
  # => disables all observers for all models.

User.observers.disable :all do
  # all user observers are disabled for
  # just the duration of the block
end

Where ORM could for example be ActiveRecord::Base

Community
  • 1
  • 1
parapet
  • 1,831
  • 1
  • 13
  • 6
9

As a flag for the observer I like to define a class accessor called "disabled" so it reads like this:

class ActivityObserver < ActiveRecord::Observer
  observe :user

  # used in tests to disable the observer on demand.
  cattr_accessor(:disabled)
end

I put it as a condition in the sensitive callbacks

def after_create(record)
       return if ActivityObserver.disabled
       # do_something
end

and I just turn the flag on when needed

ActivityObserver.disabled=true
fsainz
  • 121
  • 1
  • 3
8

You could add an accessor to your user model, something like "skip_activation" that wouldn't need to be saved, but would persist through the session, and then check the flag in the observer. Something like

class User
  attr_accessor :skip_activation
  #whatever
end

Then, in the observer:

def after_save(user)
  return if user.skip_activation
  #rest of stuff to send email
end
Terry
  • 1,088
  • 6
  • 10
6

Another one you can try (rails 3)

config.active_record.observers = :my_model_observer unless File.basename($0) == 'rake'
Nathan Bertram
  • 1,079
  • 11
  • 18
  • this works for me as well!, but i'd like to add in extra conditional of ARGV[0] == "db:migrate" because you may in the future write a rake task that utilizes your observers, and you may forget about this small little line and pull your hair trying to figure out why your observers dont run during your fancy new rake task... – Peter P. Feb 24 '13 at 22:51
2

Disabling observers for Rails 3 it's simple:

Rails.configuration.active_record.observers = []
Anton Orel
  • 576
  • 7
  • 8
2

In generally, for these sorts of situations, you can:

  1. Set up a mock object to "absorb" the unwanted behavior
  2. Have an externally accessible flag / switch that the observers respect to inhibit the behavior
  3. Add logic to the observer to detect when the behavior is unneeded in general (e.g. what dbarker suggests)
  4. Have a global flag "testing", "debug", "startup" or whatever that changes low level behavior
  5. Introspect and remove the observers
  6. Add a method to your model that performs an alternative, unobserved version of the task (sharing implementation with the normal method as much as possible).

In this case, I'd say #3 is your best bet.

MarkusQ
  • 21,814
  • 3
  • 56
  • 68
2

When running tests on an app I am working on, I use the following:

Model.delete_observers
Matt Haley
  • 4,304
  • 4
  • 25
  • 17
1
User.skip_callback("create", :after, :send_confirmation_email)

....

User.set_callback("create", :after, :send_confirmation_email)

More on this:

Disabling Callbacks in Rails 3

yuяi
  • 2,617
  • 1
  • 23
  • 46
1

You can take the method off the observer;

MessageObserver.send(:remove_method, :after_create)

Will stop the :after_create on MessageObserver by removing it.

james2m
  • 1,562
  • 12
  • 15
1

I came here looking for the an answer to the same... none of the above seemed to do the trick (or involve adding migration-specific logic to my application code -- boo).

Here's what I came up with (a bit lame that it needs to go in each relevant migration, but...)

class ChangeSomething < ActiveRecord::Migration

  # redefine...
  class MessageObserver < ActiveRecord::Observer
    def after_create(observed) ; end
    def after_update(observed) ; end
  end

  def self.up
    # Message create/update stuff...
  end
end
0

There isn't a straightforward way to disable observers that I know of, but it sounds possible to add logic to your observer to not send an email when the activation code is set...

dstnbrkr
  • 4,305
  • 22
  • 23
0

As others have hinted; I would wrap the unwanted logic in your Observer with a simple if statement.

def after_create
  send_email if RAILS_ENV == "production"
end
Matt Darby
  • 6,294
  • 4
  • 27
  • 35