Динамическая проверка и метапрограммирование в Ruby - PullRequest
3 голосов
/ 11 апреля 2011

Я пытаюсь разработать приложение, которое может представлять один и тот же ресурс разным пользователям и где ресурс может по-разному проверять поведение в зависимости от пользователя.

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

Я могу привести пример такой модели, как

class Profile < ActiveRecord::Base
  # validates_presence_of :string1
end

Модель имеет свойство 'string1', которое иногда требуется, а иногда нет. Я хотел бы создать подклассы для каждого пользователя (по причинам, не очевидным в этом упрощении) и создал модуль, который я хотел бы включить:

module ExtendProfile
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def configure_validation(required)
      if required
        class_eval("ActiveRecord::Base.validates_presence_of :string1")
      end
    end
  end
end

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

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

Это можно проиллюстрировать следующими тестами:

profile = Profile.new
profile.save; profile.errors
 => []

Профиль по умолчанию может быть сохранен без ошибок.

Object::const_set('FirstExtendedProfile'.intern, Class::new(Profile) { include ExtendProfile })
FirstExtendedProfile.configure_validation(true)
fep = FirstExtendedProfile.new; fep.save; fep.errors
 => {:string1=>["skal udfyldes", "skal udfyldes"]}

Создание нового подкласса и вызов configuring_validation добавляет проверку, но по какой-то причине она вызывается дважды во время проверки («skal udfyldes» - это датский язык и означает, что это требуется).

Object::const_set('SecondExtendedProfile'.intern, Class::new(Profile) { include ExtendProfile })
sep = SecondExtendedProfile.new; sep.save; sep.errors
 => {:string1=>["skal udfyldes"]}

Создается еще один потомок, и хотя configure_validation не вызывается, он все равно проверяет свойство string1 (но теперь только один раз).

Добавление еще одного потомка и вызов configure_validation добавляет проверку еще раз ...

Почему я не могу добавить проверку для определенного потомка профиля?

Я использую Ruby 1.9.2 и Rails 3.06. Пожалуйста, поймите, что я хотел бы понять, как заставить это создание динамического класса работать - я знаю о "стандартной" пользовательской проверке.

Ответы [ 2 ]

2 голосов
/ 11 апреля 2011

По причинам, которые становятся понятными при работе с Single Table Inheritance, проверки сохраняются в переменной в «корневом» классе, то есть в классе, который наследуется напрямую от ActiveRecord :: Base.За кулисами идет немало работы, чтобы убедиться, что то, что вы пытаетесь сделать, не работает.

Я предлагаю сохранить некоторые данные конфигурации в каждом классе, а затем написать одну динамическую проверку, которая проверяет конфигурацию.и проверяет на основе того, что он находит там.Однако это может означать то, что вы подразумеваете под «я знаю» стандартную «пользовательскую проверку».

 with_options :if => lambda { |obj| obj.class.validation_configuration[:string1]} do |t|
   t.validates_presence_of :string1
 end 
2 голосов
/ 11 апреля 2011

Это другой способ решения проблемы ... вместо использования метапрограммирования для расширения или отсутствия расширения класса:

class Profile 

  with_options :if => lambda { |o| o.required? } do |on_required|
    on_required.validates_presence_of :string1
  end

end
...