我在 twitter 上得到了一个我很满意的解决方案 - 不是我的问题本身的答案,而是一个更好的架构:
“我建议使用专门的表单/服务类,只需要你需要的验证。” - https://twitter.com/joshuapaling/status/538169606876057600
有一个很棒的关于表单对象的 railscast:http://railscasts.com/episodes/416-form-objects(还有一个关于服务对象 http://railscasts.com/episodes/398-service-objects)。
还有改革的宝石 - https://github.com/apotonick/reform 看起来不错,不过我决定这次不去。
如果你还没有订阅 railscast(去订阅吧!这太值得了,而且只需一次 9 美元的费用,无限期地,而 Ryan Bates 正在休息),这里有一些示例代码,取自 RailsCasts 剧集:
class SignupForm
include ActiveModel::Model
# ^ do this and validations and stuff mostly just work - see lines below
validates_presence_of :username
validate :verify_unique_username
validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/
validates_length_of :password, minimum: 6
delegate :username, :email, :password, :password_confirmation, to: :user
delegate :twitter_name, :github_name, :bio, to: :profile
def user
@user ||= User.new
end
def profile
@profile ||= user.build_profile
end
def submit(params)
user.attributes = params.slice(:username, :email, :password, :password_confirmation)
profile.attributes = params.slice(:twitter_name, :github_name, :bio)
self.subscribed = params[:subscribed]
if valid?
generate_token
user.save!
profile.save!
true
else
false
end
end
def subscribed
user.subscribed_at
end
def subscribed=(checkbox)
user.subscribed_at = Time.zone.now if checkbox == "1"
end
def generate_token
begin
user.token = SecureRandom.hex
end while User.exists?(token: user.token)
end
def verify_unique_username
if User.exists? username: username
errors.add :username, "has already been taken"
end
end
end