Sending Email with Rails
Upcoming SlideShare
Loading in...5
×
 

Sending Email with Rails

on

  • 9,130 views

This was the eight speech of a three day Rails training I gave in Tulsa, OK in the spring 2010.

This was the eight speech of a three day Rails training I gave in Tulsa, OK in the spring 2010.

Statistics

Views

Total Views
9,130
Views on SlideShare
9,090
Embed Views
40

Actions

Likes
6
Downloads
166
Comments
0

1 Embed 40

http://www.slideshare.net 40

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />

Sending Email with Rails Sending Email with Rails Presentation Transcript

  • Sending Email With Callbacks A look at ActionMailer and the ActiveRecord life cycle
  • ActionMailer The email library that comes with Rails
  • Mailers
  • Mailers Mailers can be used to send email from Rails
  • Mailers Mailers can be used to send email from Rails Supports plain text, HTML, and multi-part emails
  • Mailers Mailers can be used to send email from Rails Supports plain text, HTML, and multi-part emails Supports attachments
  • 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 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
  • 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
  • M and V, Without the C
  • M and V, Without the C Mailer structure is a bit different than other parts of Rails
  • M and V, Without the C Mailer structure is a bit different than other parts of Rails It’s a model
  • 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
  • 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
  • Email Example
  • Email Example Let’s say I have an application built that requires users to login to see any content
  • 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
  • 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
  • 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
  • A Problem
  • A Problem I really need to make sure users give me a valid email
  • 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
  • Adding Authentication
  • Adding Authentication When a user signs up:
  • Adding Authentication When a user signs up: We will send them an email with a special link in it
  • 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
  • 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
  • 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
  • Generate a Mailer
  • Generate a Mailer We call script/ generate as usual $ ruby script/generate mailer user_notifier activation exists app/models/ create app/views/user_notifier exists test/unit/ create test/fixtures/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
  • 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
  • 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
  • app/models/user_notifier.rb The generated mailer gets us started
  • 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
  • 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
  • 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
  • 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
  • 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
  • Customized to our Needs We will work with a User since that makes sense for what we are trying to do
  • 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
  • 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
  • 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
  • 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
  • The Email Content This is the code from app/views/user_notifier/activation.erb
  • 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
  • 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
  • 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
  • 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
  • Sending an Email
  • Sending an Email You can send an email from anywhere in the application
  • 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
  • Mailers in Production
  • Mailers in Production By default, ActionMailer will try to use sendmail to deliver emails in production
  • 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
  • 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
  • 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
  • Callbacks Taking actions during the ActiveRecord life cycle
  • The ActiveRecord Life Cycle
  • The ActiveRecord Life Cycle Models have a life cycle
  • The ActiveRecord Life Cycle Models have a life cycle They are created
  • The ActiveRecord Life Cycle Models have a life cycle They are created Read from the database
  • The ActiveRecord Life Cycle Models have a life cycle They are created Read from the database Updated
  • The ActiveRecord Life Cycle Models have a life cycle They are created Read from the database Updated Destroyed
  • 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
  • The Callback Hooks
  • The Callback Hooks after_initialize*
  • The Callback Hooks after_initialize* before_save
  • The Callback Hooks after_initialize* before_save before_create/update
  • The Callback Hooks after_initialize* before_save before_create/update before_validation
  • The Callback Hooks after_initialize* before_save before_create/update before_validation before_validation_on_ create/update
  • The Callback Hooks after_initialize* before_save before_create/update before_validation before_validation_on_ create/update after_validation
  • The Callback Hooks after_initialize* after_validation_on_cr eate/update before_save before_create/update before_validation before_validation_on_ create/update after_validation
  • The Callback Hooks after_initialize* after_validation_on_cr eate/update before_save after_save before_create/update before_validation before_validation_on_ create/update after_validation
  • The Callback Hooks after_initialize* after_validation_on_cr eate/update before_save after_save before_create/update after_create/update before_validation before_validation_on_ create/update after_validation
  • The Callback Hooks after_initialize* after_validation_on_cr eate/update before_save after_save before_create/update after_create/update before_validation after_find* before_validation_on_ create/update after_validation
  • The Callback Hooks after_initialize* after_validation_on_cr eate/update before_save after_save before_create/update after_create/update before_validation after_find* before_validation_on_ create/update before_destroy after_validation
  • The Callback Hooks after_initialize* after_validation_on_cr eate/update before_save after_save before_create/update after_create/update before_validation after_find* before_validation_on_ create/update before_destroy after_validation after_destroy
  • Building a Callback Just choose the type of callback, name a method, and write a matching Ruby method
  • 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
  • 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
  • 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
  • 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
  • Not Just for Email
  • Not Just for Email Sending email using callbacks is a common usage
  • Not Just for Email Sending email using callbacks is a common usage However, callbacks are a general tool with many uses
  • Not Just for Email Sending email using callbacks is a common usage However, callbacks are a general tool with many uses For example:
  • 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
  • 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
  • Completing the Example We need to make some minor changes and add a controller to get activation working
  • Migrating in Activation Fields Rails migrations are pretty smart and can guess where you want to add the fields
  • $ 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
  • $ 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
  • $ 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
  • $ 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
  • $ 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
  • $ 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
  • Adding Activations We look the user up by token, activate them, and log them in
  • $ ruby script/generate controller activations Adding Activations We look the user up by token, activate them, and log them in
  • $ 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
  • $ 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
  • $ 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
  • $ 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
  • Email Routing We can’t make an email link POST, so I created a custom route for the action
  • 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
  • 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
  • The Old Sign-up Our old controller logs them in as they are created and we can’t have that
  • 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
  • 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
  • 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
  • Creation Without Login Now we don’t log them in and we tell them to check their email
  • 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
  • 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
  • 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
  • Activations in Action Let’s see what we have created
  • Signing Up I can create a new user with my email address and desired password
  • Signing Up I can create a new user with my email address and desired password
  • Time to Check Email I wasn’t logged in, as planned
  • Time to Check Email I wasn’t logged in, as planned
  • Can’t Login Yet I can’t login yet either, since my account isn’t active yet
  • Can’t Login Yet I can’t login yet either, since my account isn’t active yet
  • log/development.rb In development mode, emails are printed to the log file (clear logs with: rake log:clear)
  • 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)
  • Activated and Logged In Visiting the URL completes the process
  • Activated and Logged In Visiting the URL completes the process
  • Questions?
  • User Emails Lab Your book has instructions for how to add email notifications to your application