读了重构臃肿 ActiveRecord 模型的 7 种方式这篇相当经典的文章,我注意到了文中提到的Virtus这个 gem,可以相当漂亮的解决一次表单更新多个 model 这样的情况,比如这段摘自文章里面的代码:
class Signup
include Virtus
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
attr_reader :user
attr_reader :company
attribute :name, String
attribute :company_name, String
attribute :email, String
validates :email, presence: true
# … more validations …
# Forms are never themselves persisted
def persisted?
false
end
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
@company = Company.create!(name: company_name)
@user = @company.users.create!(name: name, email: email)
end
end
这样用到 SignUp 的 controller 就可以非常的精简:
class SignupsController < ApplicationController
def create
@signup = Signup.new(params[:signup])
if @signup.save
redirect_to dashboard_path
else
render "new"
end
end
end
但是我注意到这篇经典文章写于 2012 年,然后 2013 年 Rails 4 引入了ActiveModel::Model,所以似乎第一段代码也可以用 ActiveModel::Model 这样实现:
class Signup
include ActiveModel::Model
attr_accessor(
:company_name,
:email,
:name,
)
attr_reader :user
attr_reader :company
validates :company_name, presence: true
validates :email, presence: true, email: true
validates :name, presence: true
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
@company = Company.create!(name: company_name)
@user = @company.users.create!(name: name, email: email)
end
end
显然后一种 Signup 的实现更短,而且persisted?
方法也在 ActiveModel::Model 里面定义了,还能省不少行数,且不用引入一个额外的 gem,但是这样用真的没有啥其他问题么?