5. Mailers
Mailers can be used to send email from Rails
Supports plain text, HTML, and multi-part emails
6. Mailers
Mailers can be used to send email from Rails
Supports plain text, HTML, and multi-part emails
Supports attachments
7. Mailers
Mailers can be used to send email from Rails
Supports plain text, HTML, and multi-part emails
Supports attachments
Multiple send modes including: sendmail and SMTP
8. Mailers
Mailers can be used to send email from Rails
Supports plain text, HTML, and multi-part emails
Supports attachments
Multiple send modes including: sendmail and SMTP
Mailers can also be used to receive emails
9. Mailers
Mailers can be used to send email from Rails
Supports plain text, HTML, and multi-part emails
Supports attachments
Multiple send modes including: sendmail and SMTP
Mailers can also be used to receive emails
Parses the email data into a Ruby object
11. M and V, Without the C
Mailer structure is a bit different than other parts of Rails
12. M and V, Without the C
Mailer structure is a bit different than other parts of Rails
It’s a model
13. M and V, Without the C
Mailer structure is a bit different than other parts of Rails
It’s a model
But it has views
14. M and V, Without the C
Mailer structure is a bit different than other parts of Rails
It’s a model
But it has views
You can think of it as rendering content for the user, just
not through a browser
16. Email Example
Let’s say I have an application built that requires users
to login to see any content
17. Email Example
Let’s say I have an application built that requires users
to login to see any content
I have created a User model and built the controller
that allows them to sign-up
18. Email Example
Let’s say I have an application built that requires users
to login to see any content
I have created a User model and built the controller
that allows them to sign-up
I have also wired up a login system using Authlogic
19. Email Example
Let’s say I have an application built that requires users
to login to see any content
I have created a User model and built the controller
that allows them to sign-up
I have also wired up a login system using Authlogic
The whole thing works now
22. A Problem
I really need to make sure users give me a valid email
Authlogic checks the email address format, but you
can only know an email is valid by sending a message
26. Adding Authentication
When a user signs up:
We will send them an email with a special link in it
Clicking that link will authenticate their address
27. Adding Authentication
When a user signs up:
We will send them an email with a special link in it
Clicking that link will authenticate their address
We won’t allow non-authenticated users access to the
site
28. Adding Authentication
When a user signs up:
We will send them an email with a special link in it
Clicking that link will authenticate their address
We won’t allow non-authenticated users access to the
site
After authentication, their account will work normally
31. Generate a Mailer
We call script/
generate as usual
We ask for a mailer $ ruby script/generate mailer user_notifier activation
exists app/models/
and name it create app/views/user_notifier
exists test/unit/
create test/fixtures/user_notifier
user_notifier create app/models/user_notifier.rb
create test/unit/user_notifier_test.rb
create app/views/user_notifier/activation.erb
create test/fixtures/user_notifier/activation
32. Generate a Mailer
We call script/
generate as usual
We ask for a mailer $ ruby script/generate mailer user_notifier activation
exists app/models/
and name it create app/views/user_notifier
exists test/unit/
create test/fixtures/user_notifier
user_notifier create app/models/user_notifier.rb
create test/unit/user_notifier_test.rb
create app/views/user_notifier/activation.erb
create test/fixtures/user_notifier/activation
Optionally, we can also
pass the names of
emails to create
34. class UserNotifier < ActionMailer::Base
def activation(sent_at = Time.now)
subject 'UserNotifier#activation'
recipients ''
from ''
sent_on sent_at
body :greeting => 'Hi,'
end
end
app/models/user_notifier.rb
The generated mailer gets us started
35. class UserNotifier < ActionMailer::Base
def activation(sent_at = Time.now)
subject 'UserNotifier#activation'
recipients ''
from ''
sent_on sent_at
body :greeting => 'Hi,'
end
end
app/models/user_notifier.rb
The generated mailer gets us started
36. class UserNotifier < ActionMailer::Base
def activation(sent_at = Time.now)
subject 'UserNotifier#activation'
recipients ''
from ''
sent_on sent_at
body :greeting => 'Hi,'
end
end
app/models/user_notifier.rb
The generated mailer gets us started
37. class UserNotifier < ActionMailer::Base
def activation(sent_at = Time.now)
subject 'UserNotifier#activation'
recipients ''
from ''
sent_on sent_at
body :greeting => 'Hi,'
end
end
app/models/user_notifier.rb
The generated mailer gets us started
38. class UserNotifier < ActionMailer::Base
def activation(sent_at = Time.now)
subject 'UserNotifier#activation'
recipients ''
from ''
sent_on sent_at
body :greeting => 'Hi,'
end
end
app/models/user_notifier.rb
The generated mailer gets us started
39. Customized to our Needs
We will work with a User since that makes sense
for what we are trying to do
40. class UserNotifier < ActionMailer::Base
def activation(user)
subject 'Activate Your Account'
recipients user.email
from 'admin@secureapp.com'
sent_on Time.now
body :user => user
end
end
Customized to our Needs
We will work with a User since that makes sense
for what we are trying to do
41. class UserNotifier < ActionMailer::Base
def activation(user)
subject 'Activate Your Account'
recipients user.email
from 'admin@secureapp.com'
sent_on Time.now
body :user => user
end
end
Customized to our Needs
We will work with a User since that makes sense
for what we are trying to do
42. class UserNotifier < ActionMailer::Base
def activation(user)
subject 'Activate Your Account'
recipients user.email
from 'admin@secureapp.com'
sent_on Time.now
body :user => user
end
end
Customized to our Needs
We will work with a User since that makes sense
for what we are trying to do
43. class UserNotifier < ActionMailer::Base
def activation(user)
subject 'Activate Your Account'
recipients user.email
from 'admin@secureapp.com'
sent_on Time.now
body :user => user
end
end
Customized to our Needs
We will work with a User since that makes sense
for what we are trying to do
45. Welcome to the Secure Application.
Please click the following link to activate your account:
<%= activate_url(:token => @user.perishable_token, :host => "localhost:3000") %>
The Email Content
This is the code from
app/views/user_notifier/activation.erb
46. Welcome to the Secure Application.
Please click the following link to activate your account:
<%= activate_url(:token => @user.perishable_token, :host => "localhost:3000") %>
The Email Content
This is the code from
app/views/user_notifier/activation.erb
47. Welcome to the Secure Application.
Please click the following link to activate your account:
<%= activate_url(:token => @user.perishable_token, :host => "localhost:3000") %>
The Email Content
This is the code from
app/views/user_notifier/activation.erb
48. Welcome to the Secure Application.
Please click the following link to activate your account:
<%= activate_url(:token => @user.perishable_token, :host => "localhost:3000") %>
The Email Content
This is the code from
app/views/user_notifier/activation.erb
51. Sending an Email
You can send an email
from anywhere in the
application
UserNotifier.deliver_activation(user)
Just call
deliver_EMAIL()
where EMAIL is the
name of the message
53. Mailers in Production
By default, ActionMailer will try to use sendmail to
deliver emails in production
54. Mailers in Production
By default, ActionMailer will try to use sendmail to
deliver emails in production
This works on a lot of servers but is not robust
55. Mailers in Production
By default, ActionMailer will try to use sendmail to
deliver emails in production
This works on a lot of servers but is not robust
I recommend setting up a Gmail account and
configuring ActionMailer to send via SMTP
56. Mailers in Production
By default, ActionMailer will try to use sendmail to
deliver emails in production
This works on a lot of servers but is not robust
I recommend setting up a Gmail account and
configuring ActionMailer to send via SMTP
You may also wish to shut off ActionMailer’s default
error raising behavior
61. The ActiveRecord Life Cycle
Models have a life cycle
They are created
Read from the database
62. The ActiveRecord Life Cycle
Models have a life cycle
They are created
Read from the database
Updated
63. The ActiveRecord Life Cycle
Models have a life cycle
They are created
Read from the database
Updated
Destroyed
64. The ActiveRecord Life Cycle
Models have a life cycle
They are created
Read from the database
Updated
Destroyed
Callbacks allow us to run code at points in this cycle
78. Building a Callback
Just choose the type of callback, name a
method, and write a matching Ruby method
79. class User < ActiveRecord::Base
acts_as_authentic
after_create :send_activation_email
def send_activation_email
reset_perishable_token!
UserNotifier.deliver_activation(self)
end
end
Building a Callback
Just choose the type of callback, name a
method, and write a matching Ruby method
80. class User < ActiveRecord::Base
acts_as_authentic
after_create :send_activation_email
def send_activation_email
reset_perishable_token!
UserNotifier.deliver_activation(self)
end
end
Building a Callback
Just choose the type of callback, name a
method, and write a matching Ruby method
81. class User < ActiveRecord::Base
acts_as_authentic
after_create :send_activation_email
def send_activation_email
reset_perishable_token!
UserNotifier.deliver_activation(self)
end
end
Building a Callback
Just choose the type of callback, name a
method, and write a matching Ruby method
82. class User < ActiveRecord::Base
acts_as_authentic
after_create :send_activation_email
def send_activation_email
reset_perishable_token!
UserNotifier.deliver_activation(self)
end
end
Building a Callback
Just choose the type of callback, name a
method, and write a matching Ruby method
84. Not Just for Email
Sending email using callbacks is a common usage
85. Not Just for Email
Sending email using callbacks is a common usage
However, callbacks are a general tool with many uses
86. Not Just for Email
Sending email using callbacks is a common usage
However, callbacks are a general tool with many uses
For example:
87. Not Just for Email
Sending email using callbacks is a common usage
However, callbacks are a general tool with many uses
For example:
You might update an average_review_rating column
with an after_save on Review
88. Not Just for Email
Sending email using callbacks is a common usage
However, callbacks are a general tool with many uses
For example:
You might update an average_review_rating column
with an after_save on Review
You might generate a login column from a provided
email address in a before_validation on User
89. Completing the Example
We need to make some minor changes and
add a controller to get activation working
90. Migrating in Activation Fields
Rails migrations are pretty smart and can guess
where you want to add the fields
91. $ ruby script/generate migration add_activation_fields_to_users
perishable_token:string
active:boolean
Migrating in Activation Fields
Rails migrations are pretty smart and can guess
where you want to add the fields
92. $ ruby script/generate migration add_activation_fields_to_users
perishable_token:string
active:boolean
Migrating in Activation Fields
Rails migrations are pretty smart and can guess
where you want to add the fields
93. $ ruby script/generate migration add_activation_fields_to_users
perishable_token:string
active:boolean
Migrating in Activation Fields
Rails migrations are pretty smart and can guess
where you want to add the fields
94. $ ruby script/generate migration add_activation_fields_to_users
perishable_token:string
active:boolean
class AddActivationFieldsToUsers < ActiveRecord::Migration
def self.up
add_column :users, :perishable_token, :string
add_column :users, :active, :boolean, :default => false, :null => false
end
def self.down
remove_column :users, :active
remove_column :users, :perishable_token
end
end
Migrating in Activation Fields
Rails migrations are pretty smart and can guess
where you want to add the fields
95. $ ruby script/generate migration add_activation_fields_to_users
perishable_token:string
active:boolean
class AddActivationFieldsToUsers < ActiveRecord::Migration
def self.up
add_column :users, :perishable_token, :string
add_column :users, :active, :boolean, :default => false, :null => false
end
def self.down
remove_column :users, :active
remove_column :users, :perishable_token
end
end
Migrating in Activation Fields
Rails migrations are pretty smart and can guess
where you want to add the fields
96. $ ruby script/generate migration add_activation_fields_to_users
perishable_token:string
active:boolean
class AddActivationFieldsToUsers < ActiveRecord::Migration
def self.up
add_column :users, :perishable_token, :string
add_column :users, :active, :boolean, :default => false, :null => false
end
def self.down
remove_column :users, :active
remove_column :users, :perishable_token
end
end
$ rake db:migrate
Migrating in Activation Fields
Rails migrations are pretty smart and can guess
where you want to add the fields
98. $ ruby script/generate controller activations
Adding Activations
We look the user up by token, activate them,
and log them in
99. $ ruby script/generate controller activations
class ActivationsController < ApplicationController
def create
if @user = User.find_using_perishable_token(params[:token])
@user.active = true # activate the user
@user.save
UserSession.create(@user) # log them in
flash[:notice] = "User activated."
else
flash[:error] = "User not found."
end
redirect_to root_path
end
end
Adding Activations
We look the user up by token, activate them,
and log them in
100. $ ruby script/generate controller activations
class ActivationsController < ApplicationController
def create
if @user = User.find_using_perishable_token(params[:token])
@user.active = true # activate the user
@user.save
UserSession.create(@user) # log them in
flash[:notice] = "User activated."
else
flash[:error] = "User not found."
end
redirect_to root_path
end
end
Adding Activations
We look the user up by token, activate them,
and log them in
101. $ ruby script/generate controller activations
class ActivationsController < ApplicationController
def create
if @user = User.find_using_perishable_token(params[:token])
@user.active = true # activate the user
@user.save
UserSession.create(@user) # log them in
flash[:notice] = "User activated."
else
flash[:error] = "User not found."
end
redirect_to root_path
end
end
Adding Activations
We look the user up by token, activate them,
and log them in
102. $ ruby script/generate controller activations
class ActivationsController < ApplicationController
def create
if @user = User.find_using_perishable_token(params[:token])
@user.active = true # activate the user
@user.save
UserSession.create(@user) # log them in
flash[:notice] = "User activated."
else
flash[:error] = "User not found."
end
redirect_to root_path
end
end
Adding Activations
We look the user up by token, activate them,
and log them in
103. Email Routing
We can’t make an email link POST,
so I created a custom route for the action
104. ActionController::Routing::Routes.draw do |map|
map.resources :users
map.resource :user_session
map.login "login", :controller => "user_sessions", :action => "new"
map.logout "logout", :controller => "user_sessions", :action => "destroy"
map.activate "activate/:token", :controller => "activations",
:action => "create"
map.root :controller => "home"
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
Email Routing
We can’t make an email link POST,
so I created a custom route for the action
105. ActionController::Routing::Routes.draw do |map|
map.resources :users
map.resource :user_session
map.login "login", :controller => "user_sessions", :action => "new"
map.logout "logout", :controller => "user_sessions", :action => "destroy"
map.activate "activate/:token", :controller => "activations",
:action => "create"
map.root :controller => "home"
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
Email Routing
We can’t make an email link POST,
so I created a custom route for the action
106. The Old Sign-up
Our old controller logs them in as they are
created and we can’t have that
107. class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(params[:user])
if @user.save
flash[:notice] = "Welcome!"
redirect_to root_path
else
flash.now[:error] = "Sign-up could not be completed."
render :action => :new
end
end
end
The Old Sign-up
Our old controller logs them in as they are
created and we can’t have that
108. class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(params[:user])
if @user.save
flash[:notice] = "Welcome!"
redirect_to root_path
else
flash.now[:error] = "Sign-up could not be completed."
render :action => :new
end
end
end
The Old Sign-up
Our old controller logs them in as they are
created and we can’t have that
109. class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(params[:user])
if @user.save
flash[:notice] = "Welcome!"
redirect_to root_path
else
flash.now[:error] = "Sign-up could not be completed."
render :action => :new
end
end
end
The Old Sign-up
Our old controller logs them in as they are
created and we can’t have that
111. class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(params[:user])
if @user.save_without_session_maintenance
flash[:notice] = "Please check your email to activate your account."
redirect_to login_path
else
flash.now[:error] = "Sign-up could not be completed."
render :action => :new
end
end
end
Creation Without Login
Now we don’t log them in and we tell them to
check their email
112. class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(params[:user])
if @user.save_without_session_maintenance
flash[:notice] = "Please check your email to activate your account."
redirect_to login_path
else
flash.now[:error] = "Sign-up could not be completed."
render :action => :new
end
end
end
Creation Without Login
Now we don’t log them in and we tell them to
check their email
113. class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(params[:user])
if @user.save_without_session_maintenance
flash[:notice] = "Please check your email to activate your account."
redirect_to login_path
else
flash.now[:error] = "Sign-up could not be completed."
render :action => :new
end
end
end
Creation Without Login
Now we don’t log them in and we tell them to
check their email
122. Sent mail to james@graysoftinc.com
Date: Sun, 7 Mar 2010 15:10:11 -0600
From: admin@secureapp.com
To: james@graysoftinc.com
Subject: Activate Your Account
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8
Welcome to the Secure Application.
Please click the following link to activate your account:
http://localhost:3000/activate/5HkeFFwiInKfjA4x25q9
log/development.rb
In development mode, emails are printed to
the log file (clear logs with: rake log:clear)