Send an actionmailer email upon user registration in devise rails 3 application - ruby-on-rails

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.

Related

Rails4: Cannot send email from lib/module

I have this weird thing going on in my rails4 app:
I created event.rb in the lib folder.
In there, I call a mailer:
def whatever
puts 'here'
UserMailer.welcome(user)
puts 'there'
end
which is calling
class UserMailer < ActionMailer::Base
def welcome(user)
#user = user
mail(to: #user.mailer, subject: 'Welcome to my app').deliver
end
end
The weird thing is that the method welcome is never called, while whatever is called, without raising any error (the logs are there).
But if I call UserMailer.welcome(User.first) in the console, it is sent.
What am I doing wrong? Is it that it is not possible to send an email from a module? I should move this code to a model? That would be weird.
Thanks in advance
IMO mailer should look like this:
class UserMailer < ActionMailer::Base
def welcome(user)
#user = user
mail(to: #user.mailer, subject: 'Welcome to my app') #.deliver removed
end
end
and should be invoked with this manner:
def whatever
puts 'here'
UserMailer.welcome(user).deliver_now # and added here
puts 'there'
end

Class methods defined as instance methods

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 :)

Devise: Require admin to activate account before sign_in

I'm trying to follow this wiki to have the admin approve requests for registration.
https://github.com/plataformatec/devise/wiki/How-To%3a-Require-admin-to-activate-account-before-sign_in
When I try to complete the sign up form, I get this error when I press the sign up button:
NameError at /users
uninitialized constant User::AdminMailer
It refers to line 96 in my user model. That is where this method is:
def send_admin_mail
AdminMailer.new_user_waiting_for_approval(self).deliver
end
I have a after action for send_admin_email.
class UserMailer < ActionMailer::Base
default from: "hello#cr.com"
def send_admin_mail
mail(to: hello#cr.com, subject: 'Registration Request')
end
end
Any ideas as to what I'm doing wrong?
Thank you.
your class is called UserMailer, but you're creating an instance of AdminMailer. Maybe try renaming one or the other. The tutorial suggests the class should be called AdminMailer.

Using Observer to send email from rails app

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

Rails3: access request.host from within a Mailer

I'm trying to access request.host (well, ideally host_with_port) from a Mailer in Rails. The actually call to request.host is in a Helper:
#/app/helpers/confirmations_helper
module ConfirmationsHelper
def email_confirm_url(token)
"http://#{request.host_with_port}/confirm/#{token}" # failure: undefined method
end
end
#/app/mailers/user_mailer
class UserMailer < ActionMailer::Base
default from: "email#domain.com"
add_template_helper(ConfirmationsHelper) #get access to helpers/confirmations_helper.rb
def email_confirmation(user)
#user = user
#url = "http://www.domain.com/"
mail(to: user.email, subject: "Email Confirmation")
end
end
#config/environments/development.rb
...
config.action_mailer.default_url_options = { :host => "localhost:3000" }
Error I'm getting is:
ActionView::Template::Error:
undefined method `host' for nil:NilClass
Use
ActionMailer::Base.default_url_options[:host]
in your Mailer to access the configured host in in config/environments/
I believe, this is the easiest way.
ActionView::Template::Error:
undefined method `host' for nil:NilClass
This is telling you that request is nil. This is because outside of the scope of your controller (ie. in a class extending ActionMailer::Base) request doesn't exist.
You need to pass the request object or just the part you need (request.host_with_port) to the mailer like you do other data like user in your email_confirmation.
So you have a create method with something like this
def create
#user = User.new
#user.assign_attributes(params[:user])
#user.save
#user.send_email_confirmation
end
Inside your User model you have a send_email_confirmation method like this
class User < ActiveRecord::Base
def send_email_confirmation
UserMailer.email_confirmation(self).deliver
end
Your mailer's email_confirmation looks like
def email_confirmation(user)
#user = user
#url = "http://www.domain.com/"
mail(to: user.email, subject: "Email Confirmation")
end
Making the request to the mailer from your model is not the best idea; you should keep a cleaner separation of concerns. This is part of your problem and why you are finding unwanted complexity when trying to pass something like request from your controller action into the mailer template.
What I might suggest is creating a worker class. Here I explain how to setup classes in lib/ - the same concept can be applied to something like a lib/your_app/workers/user.rb.
You could have the following in this class
module YourApp
module Workers
module User
extend self
def create!(params, options{})
options.reverse_merge! host: ""
user = User.new
user.assign_attributes(params)
user.save
UserMailer.email_confirmation(user, host).deliver
user
end
end
end
end
Your controller action could then simply be
def create
#user = ::YourApp::Worker::User.create!(params[:user], host: request.host_with_port)
end
Your mailer method can now look like
def email_confirmation(user, host)
#user = user
token = "" # define token somehow
#url = "#{host}/confirm/#{token}"
mail(to: user.email, subject: "Email Confirmation")
end
Finally, you can remove send_email_confirmation from your model as well as the email_confirm_url method from your helper since they're no longer used. Two things to note
my example above doesn't include anything in the way of validations/error-checks
my example makes an assumtion about where token is being defined and used
As you can see, by introducing this 'worker' class, there is a clean separation of functionality without duplication.

Resources