I need to send an email when something happens in my model


#1

but the model isn’t the right place to send mail from. That just
seems like pretty bad coupling, generating emails from within the
model. Or is that not a problem? On top of that, these emails need
to contain links back to my site…which is a bit of a pain in
ActionMailer, but I found directions on
http://wiki.rubyonrails.com/rails/pages/HowtoUseUrlHelpersWithActionMailer
however I won’t be able to pass in an instance of the controller if
I’m generating the emails from the model.

Anyway, when a certain method is called on my model, I want to send an
email out to a bunch of users associated with that model, and have
each email contain a link back to the app. What’s the best way to go
about doing that?


#2

The best way to handle this is using observers.
http://rubyonrails.com/rails/classes/ActiveRecord/Observer.html

An excellent example of observers is present in Rick O.'s
(technoweenie) act_as_authenticated plugin.

Best,

Raja


#3

It seems like this is only for particular AR methods (save, create,
destroy, etc). I need to send out an email whenever a specific method
is called.

Pat


#4

Instiki has a relatively neat way of solving this that I’ve also
adopted for my own code. (The Instiki version is much more extensive
than the example that I’m going to give here.)

In the lib directory, I’ve got a class that looks like this:

class UrlGenerator
@@controller ||= nil

 def self.controller=(controller)
   @@controller = controller
 end

 def self.url_for(options)
   @@controller.url_for(options) if @@controller
 end

end

Then, in a before_filter I call UrlGenerator.controller = self.

Finally, when generating email, I can call UrlGenerator.url_for(…)
to create a URL.

Yes, this is breaking MVC in a way, but I don’t feel too bad about
it. Giving the model the ability to generate a URL seems reasonable,
and the URLGenerator class encapsulates that idea while hiding the
rest of the controller.

Pete Y.
http://9cays.com


#5

Is the method called from the controller or the model? If its the
controller, why not wrap it with another method in the controller and
call
from there?

The more sophisticated approach would just be to implement a
publish/subscribe mechanism of your own. The simplist approach is to
have
the method call something like:

def signal_event(params, sender)
subscribers.each{|ea|
ea.handle_event(params, sender)
}
end

This assumes that the object has a list of subscribers which include the
mail sender. For simplicity you could make params a hash and let the
subscribers read it according to the keys they need. Normally, you’d
subclass from something that handled publish/subscribe details like
attaching and detatching subscribers, implements signal_event, etc.

You could check the Gang of Four patterns book for other
implementations.
(or google for it)

cheers.


#6

On Saturday 15 April 2006 08:05 am, Pete Y. wrote:

   @@controller = controller

to create a URL.

Yes, this is breaking MVC in a way, but I don’t feel too bad about
it. Giving the model the ability to generate a URL seems reasonable,
and the URLGenerator class encapsulates that idea while hiding the
rest of the controller.

The real problem with this, is that it you’ll have tremendous problems
if you
ever try to run in a multi-threaded environment. Rails may not run
that way
today, but it’s reasonable to expect that it might someday.

David


#7

Pete Y. wrote:

Instiki has a relatively neat way of solving this that I’ve also
adopted for my own code. (The Instiki version is much more extensive
than the example that I’m going to give here.)

In the lib directory, I’ve got a class that looks like this:

class UrlGenerator
@@controller ||= nil

 def self.controller=(controller)
   @@controller = controller
 end

 def self.url_for(options)
   @@controller.url_for(options) if @@controller
 end

end

Then, in a before_filter I call UrlGenerator.controller = self.

Unfortunately this doesn’t work when you don’t have any controller
instances, e.g. in a daemon that works with the application data for
maintenance, importing data from other sources and stuff like that. I
have this problem with RForum, it’s the reason why email notifications
don’t work for posts that are received via mailing list.


#8

Pat M. wrote:

Also one advantage that I realized after doing this is that no fcgi
processes are tied up with the processing of sending a bunch of
emails. Stick it in the db, have another program that processes the
emails, and I get finer controller over how emails are delivered + my
fcgi processes stay dedicated to web stuff.

How do you generate URLs in Mails?


#9

Here’s what I ended up doing.

I made a new model, Message, which just has two fields - the user_id
and a string which represents a message type. In the model when the
even occurs, I create a new record.

I then created a new Mailer that has all methods and views that I need

  • standard.

Finally I wrote an app which pulls all Message records from the db,
does a case to figure out what it needs to do, delivers the right
message through the mailer, and then deletes the record.

Obie F. blogged about a messaging system his guys came up with,
but I couldn’t find any code for that. When he releases it I’ll
probably use that, and just publish messages from within my model.
This is working fine though, so who knows, I might not have a reason
to change to a proper publish/subscribe.

Also one advantage that I realized after doing this is that no fcgi
processes are tied up with the processing of sending a bunch of
emails. Stick it in the db, have another program that processes the
emails, and I get finer controller over how emails are delivered + my
fcgi processes stay dedicated to web stuff.

Pat