rails ruby ruby on rails敏捷开发最佳实践 李刚 rails的邮件抽象层 注册激活系统
现在好像Ruby on Rails越来越流行了,我也开始研究一下这门新兴的热门技术。今天刚看到网上推荐了一本新书《Ruby on Rails敏捷开发最佳实践》,第一作者就是颇受好评的《struts2权威指南》的作者李刚,而第二作者据说是位在外企工作很有ror经验的人士。我读了一下试读部分,果然清晰易懂,又讲解得十分透彻,而所举的例子也非常实用。
下面,我摘录了书中关于“注册激活系统”的部分,是Rails的邮件抽象层中的内容。这个例子对于以后做通过邮件来激活注册这样的项目时,就能派上用场了。
(1) 基本注册功能
当用户注册一个账户时,实际上就等同于向底层数据表增加一条记录,因此本应用必须有持久层支持。本示例应用的注册功能需要一个数据表支持,该数据表用于保存系统的所有注册账户。下面是本应用的数据库脚本:
1 drop database if exists regist_development;
2
3 create database regist_development;
4
5 use regist_development;
6
7 --创建用户表
8
9 create table users (
10
11 id int not null auto_increment,
12
13 name varchar(100) not null unique,
14
15 pass varchar(100) not null,
16
17 email varchar(150) not null,
18
19 active_code varchar(255),
20
21 is_activated boolean,
22
23 primary key (id)
24
25 );
上面创建了一个users数据表,该数据表里保存了用户名、密码、E-mail、激活码和是否激活等5个数据列。前三个数据列的作用非常清晰,此处不再赘述。激活码列用于保存用户刚刚注册时系统生成的注册码,这个注册码将通过邮件发送给用户,系统正是通过比较用户输入的注册码和这个注册码是否相等,从而决定是否可以激活用户。
上面的users数据表对应的持久化类是User,这个持久化类的代码非常简单,只是增加了一些Model校验功能。下面是User类的代码:
26 class User < ActiveRecord::Base
27
28 def validate
29
30 #验证name不能为空
31
32 errors.add("", "用户名只能是字母、数字或下划线,且长度必须为4到20位")
33
34 unless name=~ /^\w{4,20}$/
35
36 #验证name不能是一个数据库中已经存在的名字
37
38 errors.add("", "用户名不能重复,您选择的用户名已经存在")
39
40 unless User.find_by_name(name).nil?
41
42 #验证password不能为空
43
44 errors.add("", "密码只能是字母或数字,且长度必须为4到20位")
45
46 unless pass =~ /^[a-zA-Z0-9]{4,20}$/
47
48 #验证e-mail规则
49
50 errors.add("", "电子邮件必须匹配电子邮件规则")
51
52 unless email =~ /^\w+@\w+.[a-zA-Z]{2,6}$/
53
54 end
55
56 end
提供了上面的User Model之后,处理用户注册就非常简单了,只需要提供一个注册表单,这个表单里包含用户注册的基本信息即可。下面是用户注册的表单页代码:
57 <b>请输入您的注册信息</b><br>
58
59 <b>注意:请务必输入有效的邮箱用于接收激活邮件</b><br>
60
61 <%= error_messages_for :user %>
62
63 <% form_for :user, @user, :url => { :action => "pro_regist" } do |f| %>
64
65 用户名:<%= f.text_field :name %><br/>
66
67 密 码:<%= f.password_field :pass %><br/>
68
69 Email:<%= f.text_field :email %><br/>
70
71 <%= submit_tag("注册")%>
72
73 <% end %>
当用户单击“注册”按钮时,将会把表单提交到pro_regist Action。这个Action也非常简单,它只需调用该User类的create方法向users数据表中增加一条记录即可。下面是处理用户注册的Action代码:
74 # 处理用户登录
75
76 def pro_regist
77
78 #创建一个新的user对象
79
80 @user = User.new(params[:user])
81
82 # 以当前时间来随机生成激活码
83
84 @user.active_code = rand(Time.now.to_i).to_s
85
86 # 设置用户开始处于未激活状态
87
88 @user.is_activated = false
89
90 #如果user对象能成功地保存进数据库
91
92 if @user.save then
93
94 flash[:notice] = '您已经注册成功'
95
96 flash[:name] = @user.name
97
98 # 发送邮件
99
100 ActivateMail.deliver_sent(@user)
101
102 redirect_to :action => 'success'
103
104 # 保存失败
105
106 else
107
108 render :action => 'index'
109
110 end
111
112 end
上面的Action方法的实现与普通注册Action代码大致相似,不同的是上面的Action代码需要调用ActivateMail.deliver_sent(@user)代码来发送邮件。这行代码调用ActivateMail Model里的sent方法来发送激活邮件,这也就是本应用实现用户激活的重点。
(2) 发送激活邮件
本应用需要向注册用户发送激活邮件,对于Rails应用而言,激活邮件也就是一封最普通的邮件,因此我们像开发普通邮件模块一样来实现发送激活邮件。同样,我们先使用Rails提供的邮件代码生成器来生成一个邮件模块。进入Rails应用的根路径下,输入如下命令:
# 生成ActivateMail邮件Model,并提供一个sent方法
ruby script/generate mail ActivateMail sent
上面的代码生成器命令会在app/models路径下生成一个activate_mail.rb文件,这个文件就是邮件发送Model,该Model里定义了一个sent方法,该方法就是发送邮件的业务逻辑方法。修改上面的sent方法,让该sent方法实现发送激活邮件的功能。修改后的sent方法代码如下:
113 # 定义发送邮件的业务方法
114
115 def sent(user)
116
117 # 指定邮件标题
118
119 @subject = '激活邮件'
120
121 # 将新注册的User实例传给邮件内容模板
122
123 @body = {'user'=>user}
124
125 # 使用用户的注册邮件作为收件人地址
126
127 @recipients = user.email
128
129 # 使用[email protected]作为发件人地址
130
131 @from = '[email protected]'
132
133 @sent_on = Time.now
134
135 @headers = {}
136
137 # 指定使用HTML格式的邮件
138
139 @content_type = 'text/html'
140
141 end
与前面完全类似的是,我们一样采用[email protected]作为发件人地址,这就要求我们必须在config路径下的environment.rb文件中配置sina的邮件服务器。在environment.rb文件中增加如下代码:
142 ActionMailer::Base.delivery_method = :smtp
143
144 ActionMailer::Base.server_settings = {
145
146 :address => 'smtp.sina.com',
147
148 :port => 25,
149
150 :domain => 'sina.com',
151
152 :user_name => '[email protected]',
153
154 :password => '123456',
155
156 :authentication => :login }
157
158 ActionMailer::Base.default_charset = 'GBK'
经过上面步骤,我们已经实现了发送激活邮件的大部分功能。还有一个必须完成的地方:我们定义sent(user)方法时,该方法为@body赋值的是一个Hash对象,这意味着我们还需要为该方法指定一个邮件模板。
当我们执行ruby script/generate mail ActivateMail sent命令时,该命令还在app\views\activate_mail路径下生成了一个sent.rhtml文件,这个模板文件的响应将作为ActivateMail.sent(user)方法发送的邮件内容。sent.rhtml模板文件的代码如下:
159 <h4><%= @user.name%>,您好!</h4>
160
161 请在浏览器的地址栏中输入如下地址来激活您的账户:<br/>
162
163 <a href='http://localhost:3000/regist/pro_activate?name=<%= @user.name%>&active_code=
164
165 <%= @user.active_code%>'>
166
167 http://localhost:3000/regist/pro_activate?name=<%= @user.name%>&active_code=
168
169 <%= @user.active_code%>
170
171 </a>
上面的邮件模板是一个非常简单的页面,这个页面包含了一个简单的超级链接,这个超级链接将向服务器发送两个请求参数:用户名和验证码。
用户只要单击账户激活链接,就可以激活刚刚注册的账户。在还未激活用户账户之前,我们打开users数据表,看到用户刚刚注册的账户信息处于未激活状态。
当用户账户被激活后,is_activated列的值将变成1,这表明用户账户已被激活。
(3) 处理激活
用户单击激活邮件中的链接,相当于向系统的regist控制器的pro_activate Action发送一个请求,并将用户名和激活码作为请求参数,Rails应用将根据这两个请求参数来决定是否激活用户账户。下面是pro_activate Action的代码:
172 # 处理用户激活
173
174 def pro_activate
175
176 user = User.find_by_name(params[:name])
177
178 # 如果用户不为空,且用户处于未激活状态,且用户输入的激活码正确
179
180 if user != nil && user.is_activated == false && user.active_code == params[:active_code] then
181
182 # 修改激活状态
183
184 user.update_attribute(:is_activated , true)
185
186 flash[:notice] = "恭喜您,您已经成功激活了您的账户!"
187
188 # 如果用户已经处于激活状态
189
190 elsif user != nil && user.is_activated == true then
191
192 flash[:notice] = "您的账户已经处于激活状态,请勿重复激活!"
193
194 else
195
196 flash[:notice] = "激活失败!"
197
198 end
199
200 redirect_to :action=>'activate_result'
201
202 end
上面Action先根据发送的name请求参数取出users表中对应的用户账户,然后判断取出的账户里包含的激活码与用户请求中包含的激活码请求参数是否相同。如果两个激活码相同,而且用户还未激活,系统将把用户账户的is_activated属性修改为true,即完成用户激活。如果用户的is_activated状态已经是true,则提醒用户不用重复激活账户。
当用户注册成功,并且通过激活链接来激活账户后,也就是将users数据表中对应记录行的is_activated数据列的值修改为1。