私には、とりわけユーザーのアドレスを格納するユーザーモデルがあります。アカウント(user_sessionsコントローラー)を作成するとき、名前の存在を検証したいだけです。アドレスはオプションです。
アカウントをアップグレードするとき(コントローラーのアップグレード)、完全なアドレスが入力されていることを確認したいと思います。
コントローラーに基づいて条件付き検証を行うための洗練された方法はありますか?
私には、とりわけユーザーのアドレスを格納するユーザーモデルがあります。アカウント(user_sessionsコントローラー)を作成するとき、名前の存在を検証したいだけです。アドレスはオプションです。
アカウントをアップグレードするとき(コントローラーのアップグレード)、完全なアドレスが入力されていることを確認したいと思います。
コントローラーに基づいて条件付き検証を行うための洗練された方法はありますか?
古いモデルを継承した新しいモデルを作成してみましたか。だからあなたは持つことができます:
class User < ActiveRecord::Base
attr_accessible :name, :address
validates_presence_of :name
end
class UpgradedUser < User
validates_precense_of :address
end
User
クラスから継承するため、UpgradedUser
クラスにはアドレスと名前の両方の検証があります。そして、このソリューションはまだ1つのデータベーステーブルのみを使用します。これらはまだ1つのテーブルであるため:account_status
、アカウントがアップグレードされているかどうかを判断するには、列などを追加する必要があります。
upgrade_usersコントローラーには、標準の更新方法があります。
class upgraded_users_controller
def update
@upgraded_user = UpgradedUser.find params[:id]
respond_to do |format|
if @upgraded_user.update_attributes params[:user]
format.html { redirect_to @upgraded_user }
end
end
このメソッドは、同じUserテーブルにアクセスしますが、代わりにモデルusers_controller
を介してアクセスするだけなのでUpgradedUser
、名前とアドレスの両方の存在について保存時にバリデーターをチェックします。
これを実現するには、デコレータパターンを使用できます。インスタンス拡張は別のオプションです。私たちはrubyを使用していて、インスタンス拡張の方法がもっと楽しいので、次のようにします。
class User < ActiveRecord::Base
validate :decorator_validations
protected
def decorator_validations
# noop at this point
end
end
module User::Upgrader
def upgrade!
# do whatever upgrade operation is needed
save
end
protected
def decorator_validations
super
self.errors.add(:address, 'needs to be provided') unless self.address.present?
end
end
そしてあなたのコントローラーで:
def upgrade
@user = User.find(params[:id])
@user.extend User::Upgrader
if @user.upgrade!
# success case
else
# failure case
end
end
別の方法は、コマンドパターンを使用することです。
class UserUpgrader
def initialize(user)
@user = user
end
def call
validate_upgrade_state
return false if @user.errors.present?
@user.update_attribute(:upgraded, true)
end
protected
def validate_upgrade_state
@user.errors.add(:address, 'must be present') unless @user.address.present?
end
end
この場合、コントローラーは次のようになります。
@user = User.find(params[:id])
if UserUpgrader.new(@user).call
# success case
else
# failure case
end
これらのソリューションの両方の良いところは、非常にテスト可能であるということです。