Разъяснение собственной проверки Rails 3.0 с помощью методов - PullRequest
0 голосов
/ 06 июля 2011

Я создал специальный валидатор в Rails 3.0, который проверяет, является ли комбинация столбцов уникальной в таблице. Весь код проверки:

class UniqueInProjectValidator < ActiveModel::EachValidator

  def validate_each(object, attribute, value)
    unless object.class.where("project_id = ? AND #{attribute} = ?", object.project_id, value).empty?
      if object.new_record?
        object.errors[attribute] << (options[:message] || "must be unique in each project")
      else
        orig_rec = object.class.find(object.id)
        if value != orig_rec.method(attribute).call || object.project_id != orig_rec.project_id
          object.errors[attribute] << (options[:message] || "must be unique in each project")
        end
      end
    end

end

Обратите внимание, что нелегко распознать, что делают операторы if, поэтому я надеялся, что смогу заменить условное выражение unless на метод def attribute_and_project_exist?, а второе выражение if на метод def attribute_or_project_changed? , Однако при создании этих методов аргументы validates_each не передаются из-за инкапсуляции.

Теперь вопрос: есть ли способ как-то чисто разрешить доступ к этим переменным двумя моими недавно созданными методами, как можно сделать с именами столбцов в модели, или я застрял с вариантами передачи каждого аргумента снова или оставить трудно читаемые условные высказывания?

Заранее спасибо!

1 Ответ

1 голос
/ 06 июля 2011

Полагаю, вы могли бы немного его почистить с помощью одной переменной, одной лямбды и одного "возврата как можно скорее":

def validate_each(object, attribute, value)
  # If there is no duplication then bail out right away as
  # there is nothing to check. This reduces your nesting by
  # one level. Using a variable here helps to make your
  # intention clear.
  attribute_and_project_exists = object.class.where("project_id = ? AND #{attribute} = ?", object.project_id, value).empty?
  return unless attribute_and_project_exists

  # This lambda wraps up your second chunk of ugly if-ness and saves
  # you from computing the result unless you have to.
  attribute_or_project_changed = lambda do
    orig_rec = object.class.find(object.id)
    value != orig_rec.method(attribute).call || object.project_id != orig_rec.project_id
  end

  # Note that || short-circuits so the lambda will only be 
  # called if you have an existing record.
  if object.new_record? || attribute_or_project_changed.call
    object.errors[attribute] << (options[:message] || "must be unique in each project")
  end
end

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

...