Your SlideShare is downloading. ×
  • Like
Sending Email with Rails
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Now you can save presentations on your phone or tablet

Available for both IPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Sending Email with Rails

  • 7,625 views
Published

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.

Published in Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
7,625
On SlideShare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
172
Comments
0
Likes
6

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide























































































































Transcript

  • 1. Sending Email With Callbacks A look at ActionMailer and the ActiveRecord life cycle
  • 2. ActionMailer The email library that comes with Rails
  • 3. Mailers
  • 4. Mailers Mailers can be used to send email from Rails
  • 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
  • 10. M and V, Without the C
  • 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
  • 15. Email Example
  • 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
  • 20. A Problem
  • 21. A Problem I really need to make sure users give me a valid email
  • 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
  • 23. Adding Authentication
  • 24. Adding Authentication When a user signs up:
  • 25. Adding Authentication When a user signs up: We will send them an email with a special link in it
  • 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
  • 29. Generate a Mailer
  • 30. 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
  • 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
  • 33. app/models/user_notifier.rb The generated mailer gets us started
  • 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
  • 44. The Email Content This is the code from app/views/user_notifier/activation.erb
  • 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
  • 49. Sending an Email
  • 50. Sending an Email You can send an email from anywhere in the application
  • 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
  • 52. Mailers in Production
  • 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
  • 57. Callbacks Taking actions during the ActiveRecord life cycle
  • 58. The ActiveRecord Life Cycle
  • 59. The ActiveRecord Life Cycle Models have a life cycle
  • 60. The ActiveRecord Life Cycle Models have a life cycle They are created
  • 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
  • 65. The Callback Hooks
  • 66. The Callback Hooks after_initialize*
  • 67. The Callback Hooks after_initialize* before_save
  • 68. The Callback Hooks after_initialize* before_save before_create/update
  • 69. The Callback Hooks after_initialize* before_save before_create/update before_validation
  • 70. The Callback Hooks after_initialize* before_save before_create/update before_validation before_validation_on_ create/update
  • 71. The Callback Hooks after_initialize* before_save before_create/update before_validation before_validation_on_ create/update after_validation
  • 72. 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
  • 73. 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
  • 74. 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
  • 75. 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
  • 76. 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
  • 77. 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
  • 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
  • 83. Not Just for Email
  • 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
  • 97. Adding Activations We look the user up by token, activate them, and log them in
  • 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
  • 110. Creation Without Login Now we don’t log them in and we tell them to check their email
  • 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
  • 114. Activations in Action Let’s see what we have created
  • 115. Signing Up I can create a new user with my email address and desired password
  • 116. Signing Up I can create a new user with my email address and desired password
  • 117. Time to Check Email I wasn’t logged in, as planned
  • 118. Time to Check Email I wasn’t logged in, as planned
  • 119. Can’t Login Yet I can’t login yet either, since my account isn’t active yet
  • 120. Can’t Login Yet I can’t login yet either, since my account isn’t active yet
  • 121. log/development.rb In development mode, emails are printed to the log file (clear logs with: rake log:clear)
  • 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)
  • 123. Activated and Logged In Visiting the URL completes the process
  • 124. Activated and Logged In Visiting the URL completes the process
  • 125. Questions?
  • 126. User Emails Lab Your book has instructions for how to add email notifications to your application