Как удалить валидацию с помощью предложения instance_eval в Rails? - PullRequest
36 голосов
/ 25 сентября 2011

Я хотел бы улучшить существующий класс, используя instance_eval. Там оригинальное определение содержит проверки, которые требуют наличия определенных полей, а именно:

class Dummy < ActiveRecord::Base
  validates :field, :presence => true 
end

Теперь я хочу изменить это на необязательное с помощью instance_eval (или любого другого метода, действительно):

Dummy.instance_eval do
  ...
end

Какой будет правильный синтаксис для удаления проверки, поэтому поле является необязательным. Я бы предпочел сделать это непосредственно на уровне модели, вместо этого делать странные хаки в контроллерах или представлениях. Использование instance_eval на самом деле не требуется, но, насколько я знаю, это, как правило, лучший способ улучшить классы в Rails.

Редактировать # 1

В целом - оригинальный класс является частью гема, и я не хочу его разветвлять или привязывать к конкретному выпуску. Общая причина не очень важна. Простое редактирование оригинальной модели имеет гораздо худшие последствия, чем исправление обезьян

Ответы [ 18 ]

1 голос
/ 25 сентября 2011

Если вы действительно хотите это сделать, то здесь было бы неплохо начать копать: https://github.com/rails/rails/blob/ed7614aa7de2eaeba16c9af11cf09b4fd7ed6819/activemodel/lib/active_model/validations/validates.rb#L82

Однако, если честно, внутри ActiveModel нет места, куда бы я ткнул палкой.

1 голос
/ 12 октября 2015

Хотелось бы добавить, что, если вы пытаетесь очистить проверки в экземпляре вашей Модели ( не весь класс модели ), не делайте my_dummy._validate_callbacks.clear, поскольку это очистит проверки каждого экземпляра (и будущего экземпляра) вашего Dummy класса модели.

Только для экземпляра (и если вы хотите восстановить проверки позже), попробуйте следующее:

  1. Создайте копию проверки обратных вызовов (если вы хотите восстановить позже): my_dummy_validate_callbacks = my_dummy._validate_callbacks.clone

  2. Установите пустые обратные вызовы в вашем экземпляре на пустое значение: my_dummy._validate_callbacks = {}

  3. Делай что хочешь на my_dummy валидация бесплатная!

  4. Восстановление обратных вызовов: my_dummy._validate_callbacks = my_dummy_validate_callbacks

1 голос
/ 30 сентября 2015

Если вы не хотите вносить какие-либо изменения в родительский класс, то сначала очистите все проверки в дочернем классе и скопируйте все необходимые проверки из родительского класса в дочерний класс

  class Dummy < ActiveRecord::Base
      validates :property, presence: true
      validates :value, length: { maximum: 255 }
  end

и переопределите его в дочернем классе

Dummy.class_eval do    
  clear_validators!
  validates :property, presence: true
end
0 голосов
/ 18 декабря 2018

Это не дает прямого ответа на вопрос, но вот вариант, который вы должны рассмотреть в такой ситуации: вместо отключения проверки вы можете установить обязательные поля в before_validation hook.

Так как вы неВам не нужны эти обязательные поля, установите для них фиктивные данные, которые удовлетворяют требованиям валидации, и забудьте о них.

Никаких уродливых исправлений обезьян.

0 голосов
/ 21 мая 2018

Каждый валидатор Rails, предопределенный или пользовательский, является объектом и должен отвечать на метод #validate(record). Вы можете обезьяна исправить или заглушки этот метод.

# MyModel.validators_on(:attr1, :attr2, ...) is also useful
validator = MyModel.validators.detect do |v|
  validator_i_am_looking_for?(v)
end

def validator.validate(*_)
  true
end

# In RSpec you can also consider:
allow(validator).to receive(:validate).and_return(true)

Протестировано в Rails 5.1.

Не делай этого, пока не поймешь, что делаешь;)

0 голосов
/ 10 октября 2011

Мне бы пришлось больше посмотреть на код и помощь, но я думаю, что можно было бы проверить список валидаторов класса, а затем изменить запись для валидации, которую вы хотите изменить, чтобы добавить в:if => :some_function условный к этому.

Вам нужно будет сделать это только один раз для производства (поэтому его можно поместить в инициализатор, но для разработки вам нужно будет поместить его в модель или в другое место, которое будет загружаться каждый разсоответствующая модель - (возможно, наблюдатель?).

(я отредактирую ответ с более подробной информацией, когда приду к его исследованию.)

0 голосов
/ 26 июня 2012

Если исходная реализация Dummy определена в движке, есть неприятный хак, который будет делать то, что вы хотите. Определите Dummy в вашем приложении, чтобы исходная реализация Dummy не загружалась автоматически. Затем загрузите источник в Dummy и удалите строку, которая выполняет проверку. Оцените измененный источник.

Добавьте следующее в ваше приложение / models / dummy.rb

class Dummy < ActiveRecord::Base
end

# Replace DummyPlugin with name of engine
engine = Rails::Application::Railties.engines.find { |e| e.class == DummyPlugin::Engine }
dummy_source = File.read File.join(engine.config.root, "app", "models", "dummy.rb")
dummy_source = dummy_source.gsub(/validates :field, :presence => true.*/, "")
eval dummy_source

Если это обычный гем вместо движка, применима та же концепция, просто нужно загрузить источник для Dummy из корня драгоценного камня вместо корня движка.

0 голосов
/ 05 октября 2011

Если вы можете отредактировать ограничение на исходной модели, добавив в нее функцию: if =>: some_function, вы можете легко изменить поведение вызываемой функции для возврата false. Я проверил это, и это работает довольно легко:

class Foo < ActiveRecord::Base

  validates :field, :presence => true, :if => :stuff


  attr_accessor :field

  def stuff
      return true;
  end

end

, а затем в другом месте:

Foo.class_eval {
  def stuff
    false
  end
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...