I am trying to send an email from my app. The email gets sent when I don't use observer. When I use observer i get the following error :
undefined local variable or method ` UserMailer' for #<UserObserver:0x7f5730c07400>
Here's my UserMailer
class UserMailer < ActionMailer::Base
default :from => "from#me.com"
def welcome_email(user)
#user = user
#url = "website.com/home"
mail(:to => user.email, :subject => "Welcome to My Awesome Site")
end
end
The observer code
require "#{Rails.root}/app/mailers/user_mailer.rb"
class UserObserver < ActiveRecord::Observer
observe :user
def after_save(user)
UserMailer.welcome_email(user).deliver
end
end
Any help will be appreciated. I am a nuby to ruby on rails. TIA
Observers have been removed in rails 4.
Which means, even though you can still use them with a gem, they are a deprecated and you should not use them.
The main reason for it is that observers are making your application a mess to read for new developers.
I would suggest you to use services (take a look at this post, which mentions how you can refactor your AR models properly), and send your email in one of them.
Do you have the below line setup in your application.rb file?
config.active_record.observers = :user_observer
Related
This is a working code from the Michael Hartle book. This is the mailer code in app/mailers/user_mailer.rb to create an activation mail for a user account:
class UserMailer < ActionMailer::Base
def account_activation(user)
#user = user
mail to: user.email, subject: "Account activation"
end
end
The preview of the mail is generated using the ruby file in test/mailers/previews/user_mailer_preview.rb:
class UserMailerPreview < ActionMailer::Preview
def account_activation
user = User.first
user.activation_token = User.new_token
UserMailer.account_activation(user)
end
end
The account_activation method is defined as an instance method in user_mailer.rb. But it is used in preview generator as a class method. Did I misunderstand the code or is there something else going on here?
To anyone who have the same doubt
That's how ActionMailer works. Emails are defined as instance method in a class that extends ActionMailer::Base , but you access them as class methods.
That's how ActionMailer works. Emails are defined as instance method in a class that extends ActionMailer::Base, but you access them as class methods.
class MyMailer < ActionMailer::Base
def my_email
end
end
MyMailer.my_email
# and not MyMailer.new.my_email
This is a shortcut that will instantiate an instance of the ActionMailer class, invoke the corresponding email method and return an email message. This is the code that handles the call.
The Magic is behind deliver/deliver_now/deliver_later (or any other deliver method):
def deliver_now
processed_mailer.handle_exceptions do
message.deliver
end
end
Looks like processed_mailer is the key method that we were looking for:
def processed_mailer
#processed_mailer ||= #mailer_class.new.tap do |mailer|
mailer.process #action, *#args
end
end
This method creates the instance of the mailer, calls process method with #action argument (which is the name of the instance method) and with #args, which are the arguments passed to the class method and in the end it returns the created instance of the mailer.
So thats the reason that mailer methods are being declared like instance ones but get called like class ones.
Happy Learning :)
I'm using observer pattern in rails. The "delivered_email" method in TestObserver will be called after notification email been sent out. How could I pass the instance variable in "notification" to the "delivered_email"? I could add it either in the header or subject. But it could pose security issue since user who received email could also see the variable. Is there any better way to solve it?
class GeneralMailer < ActionMailer::Base
def notification(data)
#emails = data[:emails]
subject = "#{#sender.to_s}"
mail(:to => #emails, :subject => subject)
end
end
class TestObserver
def self.delivered_email(message)
begin
# do something here
puts #emails
rescue => ex
# do something here
end
end
ActionMailer::Base.register_observer(TestObserver)
If you do want to send extended information to a mail observer – as I myself did – it can be done by monkey-patching your mailer a bit to use a custom class extending from Mail::Message.
Note that this requires setting an internal, undocumented attribute. In a future Rails version, it might not continue to work.
class ExtendedMessage < Mail::Message
attr_accessor :extra_args
end
class ApplicationMailer
before_action :patch_message
def patch_message
# Patch the internal attribute #_message to use our overridden class
# that you can store extra attributes inside.
# NOTE: If a future Rails version decides to change the internals, this could break.
#_message = ExtendedMessage.new
end
end
class MailObserver
def self.delivered_email(mail)
do_something_with mail.extra_args
end
end
I've got a Rails 3 mailer that works fine.
class Notifier < ActionMailer::Base
def cool_email(user_id)
#user = User.find_by_id(user_id)
mail(to: #user.email,
from: 'admin#example.com',
subject: 'hi'
)
end
end
The view for this will render the #user instance variable correctly and the email is sent without any problem.
However, when I namespace the mailer, everything breaks. With the mailer structured like this.
class Foo::Notifier < ::ActionMailer::Base
def cool_email(user_id)
#user = User.find_by_id(user_id)
mail(to: #user.email,
from: 'admin#example.com',
subject: 'hi'
)
end
end
And the view inside app/view/foo, Rails is unable to find the html template. The email sends, but there is nothing inside the body.
What am I doing wrong?
The views should be stored in app/view/foo/notifier, specifically app/view/foo/notifier/cool_email.EXTENSION.
FYI, it's always a good practice to append Mailer to the name of a mailer.
class Foo::NotifierMailer < ::ActionMailer::Base
or
class Foo::NotificationMailer < ::ActionMailer::Base
It prevents conflicts and makes possible to immediately understand the scope of the class.
I am building an rails based e-commerce application first time. I want to send an email to user when he submits the order, like the way we get receipt on email when we place order on e.g. mealnut.com or fab.com. I was searching for tutorials but not getting related to order submit emails. Every where user sign up or reset etc.
Has any one implemented it? or know any resource/tutorial in rails?
Your guidance/help will be appreciated!
First generate the mailer for writing required actions.
rails g mailer UserMailer
then in app/mailers/user_mailer.rb file
class UserMailer < ActionMailer::Base
default from: 'notifications#example.com'
def order_confirmation(user, order)
#user = user
#order = order
mail(to: user.email, subject: 'Order has been received')
end
end
and the view content for the email would be like this app/views/user_mailer/order_confirmation.html.erb
Hi <%= #user.name %>
You have successfully placed an order with us.
Please find the details of order....
#.............
then in the controller action, where you will create a order, place the below line after creating the order to send an email
UserMailer.order_confirmation(user, order).deliver
Go through the action mailer tutorial for more information.
I'm afraid sending an email is a fairly standard procedure.. and the tutorials you've found are probably applicable. You need to understand that triggering a message to be sent can be done from any controller action.. in your case you'll want the order create action.
After reading the following:
http://railscasts.com/episodes/61-sending-email-revised?view=asciicast
You can make the necessary changes and call the mailer from your order create action:
class OrdersController < ApplicationController
def create
#order = Order.new(params[:order])
if #order.save
UserMailer.order_confirmation(#order, #user).deliver
redirect_to #user, notice: "Order Completed Successfully."
else
render :new
end
end
end
The reason I am using UserMailer in the above is because you will likely want to set up a mailer that sends messages to Users, but you could call it OrderMailer if you wanted.
You cannot/not suitable use Devise mailer (since devise mailer is for User authentication purposes). What you could do is, you could use a observer class to send e-mails
Ex:
class Order < ActiveRecord::Base
#your standard order code
end
class OrderObserver < ActiveRecord::Observer
def after_create(order)
#Email sending code
end
end
This OrderObserver sends an email when a Order#create is finished. Read more about observer class
Regarding sending email with rails3 check this, and its same as sending emails for forgotpassword / signup etc, it's just that content is different.
I'm trying to send an email upon user registration in a rails 3 application that is using the devise gem. Every time I try to the send the email I get the following error:
NoMethodError in Devise::RegistrationsController#create
undefined method `welcome_email' for UserMailer:Class
My app/model/user.rb has the following code...
after_create :send_welcome_email
def send_welcome_email
UserMailer.welcome_email(self).deliver
end
My app/mailers/user_mailer.rb has the following code...
class UserMailer < ActionMailer::Base
default from: "support#mysite.com"
def welcome_email(user)
#user = user
#url = "http://mysite.com/login"
mail(:to => "#{user.email}", :subject => "Welcome to My Awesome Site")
end
end
The method welcome_email exists so I'm not sure why I'm getting the error. Been trying to resolve this problem for the past couple of hours.
Thanks in advance for you help!! As always if you give me a good answer I will accept it as so.
Alex
Looks like the welcome_email is an instance method. And it looks like the error says it needs to be a class method. So try rewriting the method declaration as:
def self.welcome_email(user)
I guess that should solve it.