2014-11-04 2 views
1

Мне нужна условная проверка в некоторых частях моего приложения. Сейчас я использую следующую схему:Изменение правил валидации на лету

User.create 
User::WithPassword.create 
User::WithPhone.create 

Было бы здорово, если бы я мог изменить поведение класса на лету, как это:

User.with_phone.with_password.create 

Так что я попытался сделать это следующим образом:

class User < ActiveRecord::Base 
    validates :phone, presence: true, if: :phone_required? 

    def self.with_phone 
    define_method(:phone_required?) { true } 
    self 
    end 

    private 

    def phone_required? 
    false 
    end 
end 

Так он может быть использован как это, где это необходимо:

User.with_phone.create(user_params) 

Проблема с этим подходом заключается в том, что все экземпляры Пользователя получают новое поведение после изменения фактического класса.

Есть ли способ вернуть только модифицированную копию класса User с новым методом экземпляра phone_required?, не затрагивая «базовый» класс?

Update

Спасибо за комментарии, как это было больше идей, требование в том, что я создаю пользователь без определенной проверки автоматически, а затем, когда они редактируют профиль, они имеют дело с нетронутой User модели , Я создаю with_/without_ на лету в методе, который отсутствует при необходимости.

Вот моя следующая итерация:

class User < ActiveRecord::Base 
    validates :phone, presence: true, if: :phone_required? 

    def self.with_password 
    define_singleton_method(:password_required?) { true } 
    self 
    end 

    def password_required? 
    self.class.try :password_required? 
    end 
end 

Видимо это не лучше, как метод одноплодной остается там все время.

+2

Пожалуйста, не делайте этого. Он летит перед лицом объектно-ориентированного дизайна, если вы не пытаетесь использовать шаблон «Работа за счет размытия обфускации». Почему бы не использовать флаг 'attr_accessor' или столбец первого класса, чтобы сохранить это логическое значение? Этот метод больше не будет отображаться, поэтому, когда вы попытаетесь перезаписать существующую запись, ваша проверка не удастся. – tadman

+0

Благодарим вас за ввод, на самом деле мне нужен пользовательский класс только для времени создания. Я реорганизую это из модели User :: WithoutPassword. Я обновил исходный вопрос при создании метода singleton, чтобы он не загрязнял базовый класс. – firedev

ответ

1

Почему бы просто не использовать переменную экземпляра, инициализированную во время создания?

class User < ActiveRecord::Base 
    validates :phone, presence: true, if: :phone_required? 

    @phone_required = false 

    def self.create_with_phone(params) 
    obj = self.create(params) 
    obj.phone_required = true 
    end 

    private 

    def phone_required=(v) 
    @phone_required = v 
    end 

    def phone_required? 
    @phone_required 
    end 
end 

User.create_with_phone(user_params) 
+0

Благодарим вас за предложение, я хотел бы как можно лучше имитировать интерфейс базового класса без необходимости добавлять виртуальные поля. Дело в том, что во время создания мне нужно «User.without_password.without_phone», а затем просто «User.without_password», но как только пользователь получает право редактировать свой профиль, это просто «Пользователь». Я обновил оригинальный пост с новой идеей. – firedev

Смежные вопросы