前置知识: Form Object http://railscasts.com/episodes/416-form-objects
Form Object 在创建表单的时候特别好使,在不同的业务的时候对同一个 model 的处理和验证可能都不同,这时候逻辑无论是写在 model 层或 controller 层都不太好使。这时候 Form Object 就横空出世了。
但是我们在使用 Form Object 的时候也遇到了一个坑。
class SignUpForm
validate :uniqueness_of_email
def submit(params)
user.attributes = params.slice(:email, :first_name, :last_name)
if valid?
run_callbacks :save do
user.save!
end
end
end
private
def uniqueness_of_email
errors.add(:email, 'Email is already in use') unless User.find_by(email: email).nil?
end
end
当用户在注册表单上连点两次提交的话就会挂在 user.save!
上。
原因是 User 这个 model 内为了保证数据完整性也加了一个 validates :email, uniqueness: true
当用户连点两次提交,两次提交会进入两个不同的进程,两个进程都会通过 Form Object 下的验证,
幸好 Model 层有事务的保护,最后一次提交会失败,保证了数据的完整。
现在已经在提交按钮上添加了 disable_with: 'saving'
使用户不能点两次提交,从前端解决这个问题。
但是这暴露了非事务的 Form Object 在并发下验证的脆弱。
对此,同事的建议是使用 http://www.ruby-doc.org/core-2.1.2/Mutex.html 让保存这件事只能单线程执行。