Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing extra arguments to Rails observer

I have several methods in Account model: pay, receive, refund etc. that I want my AccountObserver to watch. But I would like to pass extra objects to my AccountObserver.

For example, in my Account model, I would like to define:

def pay
  ...
  notify_observers(:after_pay, payee, amount)

end

How should I do this?

Thank you.

like image 949
AdamNYC Avatar asked Feb 23 '12 01:02

AdamNYC


3 Answers

Don't overwrite notify_observers. Patching core classes is a really bad idea. Instead, what you should do is this:

Instead of calling the instance method on your model which is restricted to just the method name, call the class method yourself:

# Notify list of observers of a change.
def notify_observers(*arg)
  observer_instances.each { |observer| observer.update(*arg) }
end

It passes along any arguments you give it to the #update method of your observer, which you can overwrite inside your own observer class.

The default doesn't accept any extraneous arguments, but isn't too complex:

def update(observed_method, object, &block) #:nodoc:
  return unless respond_to?(observed_method)
  return if disabled_for?(object)
  send(observed_method, object, &block)
end

So simply add this to your observer, for example:

def update(observed_method, object, *args)
  return unless respond_to?(observed_method)
  return if disabled_for?(object)
  send(observed_method, object, *args)
end

def after_pay(payee, amount)
  ...
end

And call

Payee.notify_observers(:after_pay, payee, amount)
like image 74
fx_ Avatar answered Nov 01 '22 13:11

fx_


I'm afraid that this isn't a very good use case for observers. The best way I can think of, without going a different route entirely, is to set a instance variables on your Account object which you can then access in the observer, e.g.:

class Account < ActiveRecord::Base
  attr_reader :last_payment

  def pay payee, amt
    @last_payment = [ payee, amt ]

    notify_observers :after_pay
  end
end

class AccountObserver < ActiveRecord::Observer
  def after_pay account
    payee, amt = account.last_payment

    Rails.logger.info "#{payee} paid #{amt}!"
  end
end
like image 2
Jordan Running Avatar answered Nov 01 '22 12:11

Jordan Running


Late response, but I hope somebody will find it helpful.

First of all, it seems being fixed in Rails 4

But in my Rails 3 project I'm passing a hash to class-level notify_observers call.

  self.class.notify_observers :organisation_added_to_group, { group: self, organisation: organisation }

And in the observer I'm receiving this hash:

def organisation_added_to_group(args)
  puts args[:group]
  puts args[:organisation]
end

I like that I don't care about update's method internals, like in the answer above.

like image 1
cutalion Avatar answered Nov 01 '22 12:11

cutalion