#206 Action Mailer in Rails 3
- Download:
- source codeProject Files in Zip (97.3 KB)
- mp4Full Size H.264 Video (17.6 MB)
- m4vSmaller H.264 Video (12.1 MB)
- webmFull Size VP8 Video (33.4 MB)
- ogvFull Size Theora Video (23.9 MB)
ActionMailer在Rails3.0里面有很大的变化。ActionMailer在Rails3.0里面使用更好用的Mail gem 替代之前使用的TMail gem。
我们创建一个新的Rails 3 应用程序来演示这个ActionMailer。我们把这个应用程序叫做:mailit
。
rails mailit
接下来我们为一个User
模型创建脚手架(scaffold)。这个模型有两个属性:name
和email
,用来演示简单的用户注册页面。
rails g scaffold user name:string email:string
然后我们运行数据库迁移(migrations):
rake db:migrate
刚才生成的脚手架(scaffolding)代码包括一个创建用户的页面。我们希望应用程序在这个表单(form)提交后创建一个新用户并且发送一封确认信。
首先我们需要做的是创建一个新的初始化文件(initializer file)并添加一些配置信息,我们把它命名为setup_mail.rb
。这个文件放在/config/initializers目录下。
如果你的机器里面已经安装并配置了sendmail,ActionMailer会使用sendmail发送邮件。我们也可以在initializer中指定SMTP设置。
ActionMailer::Base.smtp_settings = { :address => "smtp.gmail.com", :port => 587, :domain => "asciicasts.com", :user_name => "asciicasts", :password => "secret", :authentication => "plain", :enable_starttls_auto => true }
也许你想用在生产环境下(production environment)使用另外的发送方式。但如果我们的应用程序正在开发阶段,这个方式已经足够了。你需要去根据你自己的Gmail帐号信息去修改domain
,user_name
,和password
。
这样我们已经完成了配置信息,我们可以用下面的代码去生成一个新的mailer。
rails g mailer user_mailer
这个命令会帮我们在/app/mailers
目录下创建一个新的user_mailer.rb
文件。早期版本的Rails会把mailer类文件都放在/app/models
文件夹里面,在Rails 3他们被放在自己的文件夹里面(译者注:/app/mailer)。Mailers在Rails 3像控制器(controller),并且Mailers和控制器共享很多底层代码。
UserMailer类的默认代码看起来像这样(/app/mailers/user_mailer.rb):
class UserMailer < ActionMailer::Base default :from => "from@example.com" end
我们现在把default
那行代码删掉,在后面我们会解释这行代码的用途。
就像Rails 2应用程序里面一样,我们为每个我们要发送的email类型添加一个方法。在我们的例子里,我们添加一个叫做 registration_confirmation
的方法。
class UserMailer < ActionMailer::Base def registration_confirmation(user) mail(:to => user.email, :subject => "Registered", :from => "eifion@asciicasts.com") end end
我们给registration_confirmation
方法传递一个User
对象,然后我们需要做的仅仅是调用mail方法,给mail方法传递:to
,:from
和:subject
之类的参数。
如果我们在类里面有多个共享相关设置的方法,我们可以把这些共享值放在之前我们删除的default方法里面。比方说email总是从同样的地方发送,我们就可以把:from设置放到default里面:
class UserMailer < ActionMailer::Base default :from => "eifion@asciicasts.com" def registration_confirmation(user) mail(:to => user.email, :subject => "Registered") end end
我们可以把任何准备共享的设定(任何可以在mail
方法里面设定的值)抽出来放在default
方法里面。
类似controllers,mail需要有相对于的视图(view)文件。 我们的注册确认邮件的视图文件放在/app/views/user_mailer
文件夹内。 我们准备用纯文本的形式发送邮件,因此我们的文件名叫做registration_confirmation.text.erb
。这个文件里面的任何内容都将作为email的内容显示。
Thank you for registering!
接下来我们需要写些代码,这些代码实现当创建用户的时候发送邮件的功能。有些人喜欢使用Model Observer去实现,但我们准备直接在controller层里面实现。 我们这样做的理由是: 如果我们使用观察者(observer), 那么我们如果在Rails命令行(console)里面创建User对象的时候,后台也会发送email。我们只想用户在应用程序里面创建时发送email。因此我们就把实现代码放在controller里面。
我们在UsersController
的create
action里面添加发送email的代码。我们所需要做的仅仅是调用我们刚刚写的registration_confirmation
方法,把新创建的user作为参数传递进去,最后调用deliver
方法发送email。
def create @user = User.new(params[:user]) respond_to do |format| if @user.save UserMailer.registration_confirmation(@user).deliver format.html { redirect_to(@user, :notice => 'User was successfully created.') } format.xml { render :xml => @user, :status => :created, :location => @user } else format.html { render :action => "new" } format.xml { render :xml => @user.errors, :status => :unprocessable_entity } end end end
这个跟Rails 2有点不同,在Rails 2里,我们调用deliver_registration_confirmation
方法。 现在我们的registration_confirmation方法返回一个mail message对象,然后再调用它的deliver方法。 这样我们可以在我们需要发送email的时候才调用deliver。
我们现在测试一下: 注册一个新的用户,当我们提交表单的时候,系统就会发送一封email.
从上图可以看到我们邮件发送成功。
如果我们想在email里面显示刚注册的用户的姓名该怎么办呢? 我们需要把user对象传到view里面。 在Rails 3里面我们很容易就可以这样做,因为mailers就像controllers一样, 任何实例变量(instance variables)都可以在view里面访问。 我们需要做的仅仅是用传递到registration_confirmation
方法的user变量创建一个view里可以访问的实体变量。
def registration_confirmation(user) @user = user mail(:to => user.email, :subject => "Registered") end
因为mail
方法会返回mail message,mail方法的调用必须写在方法的最后。 这样我们得在mail方法前定义实体变量。
现在我们在mailer里面定义了实体变量,我们可以在view里面使用它把用户的姓名添加在email里。
<%= @user.name %>,
Thank you for registering!
当我们再次注册一个新的用户,用户的姓名将会显示在email的内容里:
如果我们想要在email里面提供给新用户编辑他们的个人信息的链接,我们可以这样做:
Edit Profile: <%= edit_user_url(@user) %>
这样是无法工作的。 我们必须使用:host
提供应用程序域名.
<%= @user.name %>, Thank you for registering! Edit Profile: <%= edit_user_url(@user, :host => "localhost:3000") %>
必须这样做的原因是mailers已经完全从当前的请求(request)分离出来。 这样设计的原因是:mailer可以不在当前controller的请求内发送email。
我们可以在之前的initializer文件里设定host的值,这样我们就不需要对每封email的每个link都设定host值。
ActionMailer::Base.smtp_settings = { :address => "smtp.gmail.com", :port => 587, :domain => "asciicasts.com", :user_name => "asciicasts", :password => "secret", :authentication => "plain", :enable_starttls_auto => true } ActionMailer::Base.default_url_options[:host] = "localhost:3000"
我们在这里可以用hash指定任何设置(options),现在我们只需要设定host值。当我们再次注册,我们可以在email里面看到一个带有正确url的链接。
Multipart邮件与附件
在Rails 3里面发送multipart emails也相当方便。 我们所需要做的仅仅是创建一个跟text view同名的视图文件(view)。 在我们的例子里面这个文件为registration_confirmation.html.erb
。 在这个文件里面,我们添加一些简单的html代码。
<p><%= @user.name %>,</p> <p>Thank you for registering!</p> <p><%= link_to "Edit Profile", edit_user_url(@user, :host => "localhost:3000") %></p>
如果我们在支持html的客户端查看email,我们将会看到email里面有一个链接。两种类型的email内容都会发送,这样如果客户端不支持html,它将会显示纯文本形式的email内容。
添加附件操作相当直接而简单。我们只用调用attachements方法,设置附件名称,并读取一个文件给这个附件。
def registration_confirmation(user) @user = user attachments["rails.png"] = File.read("#{Rails.root}/public/images/rails.png") mail(:to => "#{user.name} <#{user.email}>", :subject => "Registered") end
当我们再次注册,rails.png
文件就作为一个附件显示在email里面。 注意上面的代码,我们把用户的姓名添加到:to
里面了。
可以看出使用新的ActionMailer API,我们很容易就创建复杂的emails. 默认的设置基本上够用了,如果你想设置类似编码格式(encoding types)的值,你可以修改覆盖默认设置。
拦截器(Interceptors)
再结束本章之前我们将会介绍一向新技术:在email messages发送前拦截它们。 拦截器可以帮我们在开发模式的时候(development mode)改变email的发送方式, 这样emails将会只发送到你自己的帐号里而不会发送到任何你创建的用户的email帐号里。
这个功能刚刚被加在新的Mail gem里, 因此我们需要升级mail到最新版本(至少2.1.3)。我们通过修改应用程序的Gemfile文件去获得正确的版本:
gem "mail", "2.1.3"
然后我们运行bundle install
去安装升级的版本。
接下来我们需要创建拦截器(interceptor)类。 我们把这个文件命名为development_mail_interceptor.rb
并把它放在/lib
目录下。
class DevelopmentMailInterceptor def self.delivering_email(message) message.subject = "[#{message.to}] #{message.subject}" message.to = "eifion@asciicasts.com" end end
类方法delivering_email
获得准备发送的email message, 修改它的主题(subject)为原目标地址(message.to) 加上原主题(message.subject)。message的to字段也被修改,这样所有的email将会发到eifion@asciicasts.com
。
我们需要把这个拦截器(interceptor)注册到我们的初始化文件(initializer)内:
Mail.register_interceptor(DevelopmentMailInterceptor) if Rails.env.development?
如果我们的应用程序运行在开发模式(development mode)下,我们的拦截器(interceptor)的delivering_email
方法将会被调用。 如果我们的Rails 3.0 包含升级了的Mail gem, 我们可以把方法调用Mail.register_interceptor
替换为ActionMailer::Base.register_interceptor
当我们注册新用户的时候,注册确认email将永远发给eifion@asciicasts.com
。 原始的收件人(recipient)地址显示在主题栏。
当你开发你的应用程序的时候,这是个检查你的emails是否工作的好办法。
以上就是这章的全部了。 我希望这章对你有用。新的ActionMailer API 让 Rails 应用程序发送email更加方便。