Rails start
如何进行架构设计
难点不在于架构设计的好不好,而在于细节是否做得妥当,或者叫做你的架构是否基于最佳实践。
依据
- 用户需求
- 团队配置
- 技术成熟度
用户需求
- 可登陆的增删改查
团队配置
- 会前后端的全栈工程师
技术成熟度
- 假设只会 Rails + Vue / React
什么是前后端分离

这里说的前端和后端是指前端代码和后端代码,不指人。
不分离:传统的后端工程师从数据库、Redis读取数据渲染到 HTML 中,HTML 需要引用 js、css ,但是现代前端代码代码都是打包生成的(style.xxx.css、main.xxx.js),所以无法在 HTML 中提前引用这些 js、css,所以只能用 Rails(插件 webpacker) 读取 webpack 的内容,再反向写到 HTML 中。
分离:前端自己负责 HTML,自己搭建静态服务器给用户访问,如:Ngnix 或 Node等等,好处是 HTML 可以直接用 webpack 的插件,直接把打包后生成的文件写到 HTML 中,数据只通过 AJAX 获取。
注意前后端代码不一定要交给两个人写,可以由一个人写
用户需求
视觉稿

用例图
用例图释义:把使用的例子全部画出来

表设计
有哪些表?
- users / records / tags
- 还有 taggings 表,表示 records 和 tags 的关联
- password_reset_requests 记录所有重置请求
每个表有哪些属性
- 不用一次想好,但命名一定要准确
- 从简单的需求出发,逐渐迭代
users 表
从需求出发
- 注册:邮箱、密码、确认密码
- 数据库需要存密码吗?不需要,只存密文
- 数据库需要存确认密码吗?不需要
- 如何加密?使用最佳实践 has_secure_password
- 密文叫什么?使用最佳实践 password_digest
- 注册之后需要发欢迎邮件吗? 使用 mailer
- 需要强制用户验证邮箱吗? 可以不强制,也可以强制
结论
- users 表含有 email 和 password_digest
开始实现
步骤
- 创建 model
- console 操作 user
- 创建 controller
- 配置 routes
- 配置 mailer
- 创建 mailer
- 使用 HttpClient
- 创建 rspec 测试
- 改代码,测试
- 改代码,测试
- 改代码,测试
约定
- RESTful 接口风格
- post /users 就是注册接口,不能不加 s
- 文件目录
创建数据库
bin/rails db:create
创建 User 表和 Model
1 | bin/rails g model User email:string password_digest:string |
使用控制台增删改查
1 | bin/rails console |
不用写任何代码,为什么就可以进行增删改查呢?这才是一个成熟的框架应该内置好的。
上述操作有个问题,我们不能直接赋值 password_digest
- 搜索 rails has_secure_password
- 找到 Gemfile 打开注释
gem 'bcrypt', '~> 3.1.7' - 然后再 User 上加这句话
has_secure_password - 安装依赖
bin/bundle install
再次创建
1 | bin/rails console |
使用 http 请求来创建用户
首先在 routes 里添加
1 | get '/users', to: 'users#index' |
这五个增删改查几乎是每个表都要写,既然这么麻烦,于是 rails 就提供了另一个方法,等价于写了上面五句话
1 | resources :users |
使用 bin/rails routes 即可查看所有 routes 得以验证
创建 controller
1 | bin/rails g controller users |
然后在 controller 定义一个 create 方法
1 | class UsersController < ApplicationController |
但是测试时发现没有传 password_confirmation 竟然也能成功保存,所以我们需要加上非空校验
在 user.rb 中加入
1 | class User < ApplicationRecord |
并加上返回响应
1 | class UsersController < ApplicationController |
当我们什么都没传时,发现 password 重复报错了两遍,可能是 has_secure_password 也做了校验,所以移除我们自己的
1 | class User < ApplicationRecord |
再加上更完善的校验
1 | class User < ApplicationRecord |
校验国际化
- 搜索 rails i18n
- 在
config/locales/下创建文件zh-CN.yml - 在
config/initializers/下创建locale.rb文件1
2
3
4
5
6
7
8
9
10# config/initializers/locale.rb
# Where the I18n library should search for translation files
I18n.load_path += Dir[Rails.root.join('lib', 'locale', '*.{rb,yml}')]
# Permitted locales available for the application
I18n.available_locales = [:en, 'zh-CN']
# Set default locale to something other than :en
I18n.default_locale = 'zh-CN' - 根据报错依次追加内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15zh-CN:
activerecord:
errors:
models:
user:
attributes:
password:
blank: 密码不能为空
too_short: 密码不能少于 %{count} 位
email:
blank: 邮箱不能为空
invalid: 邮箱格式不合法
password_confirmation:
blank: 确认密码不能为空
额外,优化一下上面的这部分代码
1 | class UsersController < ApplicationController |
第一步
1 | class UsersController < ApplicationController |
第二步使用 permit 方法
1 | class UsersController < ApplicationController |
第三步
1 | class UsersController < ApplicationController |
第四步,使用 create 方法,等价于 先 new 再 save
1 | class UsersController < ApplicationController |
第五步,很明显 user 声明了直接使用
1 | class UsersController < ApplicationController |
发送邮件
- bin/rails generate mailer UserMailer
- app/mailers/user_mailer
1
2
3
4
5
6
7class UserMailer < ApplicationMailer
def welcome_email(user)
@user = user
@url = 'https://www.baidu.com'
mail(to: @user.email, subject: 'Welcome to My Awesome Site')
end
end - app/mailers/application_mailer.rb
1
2
3
4class ApplicationMailer < ActionMailer::Base
default from: '529743595@qq.com'
layout 'mailer'
end - app/views./user_mailer/welcome_email.html.erb
1
2
3
4
5
6
7
8
<p>
<%= @user.email %> 您好,
</p>
<p>
欢迎来到 Morney,情记一笔把!
</p> - config/environments/development.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# config.action_mailer.raise_delivery_errors = false
#
# config.action_mailer.perform_caching = false
config.action_mailer.delivery = :smtp
config.action_mailer.raise_delivery_errors = true
config.action_mailer.perform_caching = false
config.action_mailer.smtp_settings = {
address: ENV['smtp_domain'],
port: ENV['smtp_port'],
domain: ENV['smtp_domain'],
user_name: ENV['smtp_username'],
password: ENV['smtp_password'],
authentication: ENV['smtp_authentication'],
enable_starttls_auto: ENV['smtp_enable_starttls_auto'],
}
config.action_mailer.preview_path = "#{Rails.root}/spec/mailers/previews" - 用 dotenv 创建环境变量,Gemfile
1
gem 'dotenv-rails'
- config/application.rb
1
2Bundler.require(*Rails.groups)
Dotenv::Railtie.load - .env
1
2
3
4
5
6
7
8export smtp_username=''
export smtp_password=''
export smtp_domain='smtp.qq.com'
export smtp_port='587'
export smtp_authentication='plain'
export smtp_enable_starttls_auto=true
export mailer_sender='' - .env.local(并添加至 .gitignore)
1
2export smtp_username='xxxxx'
export smtp_password='xxxxxx' - app/models/user.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class User < ApplicationRecord
has_secure_password
validates_presence_of :email
validates_uniqueness_of :email
validates_presence_of :password_confirmation, on: [:create]
validates_format_of :email, with: /.+@.+/, if: :email
validates_length_of :password, minimum: 6, on: [:create], if: :password
after_create :send_welcome_email
def send_welcome_email
UserMailer.welcome_email(self).deliver_now
end
end