Я услышал этот аргумент также - от людей, которые забыли помещать индекс на их внешние ключи и затем жаловались, что определенные операции были медленными (потому что ограничительная проверка могла использовать в своих интересах любой индекс). Таким образом подвести итог: нет никакого серьезного основания не использовать внешние ключи. Вся современная расположенная каскадом поддержка баз данных удаляет, таким образом...
Если проверка одинакова для каждого класса, ответ довольно прост: поместите метод проверки в модуль и смешайте его с каждой моделью, затем используйте validate
для добавления проверки:
# in lib/validates_dated_on_around_now
module ValidatesDatedOnAroundNow
protected
def validate_dated_around_now
# make sure dated_on isn't more than five years in the past or future
self.errors.add(:dated_on, "is not valid") unless ((5.years.ago)..(5.years.from_now)).include?(self.dated_on)
end
end
class FirstModel
include ValidatesDatedOnAroundNow
validate :validate_dated_around_now
end
class SecondModel
include ValidatesDatedOnAroundNow
validate :validate_dated_around_now
end
Если вам нужны разные диапазоны для каждой модели, вы, вероятно, захотите что-то вроде этого:
module ValidatesDateOnWithin
def validates_dated_on_within(&range_lambda)
validates_each :dated_on do |record, attr, value|
range = range_lambda.call
record.errors.add(attr_name, :inclusion, :value => value) unless range.include?(value)
end
end
end
class FirstModel
extend ValidatesDatedOnWithin
validates_dated_on_within { ((5.years.ago)..(5.years.from_now)) }
end
class SecondModel
extend ValidatesDatedOnWithin
validates_dated_on_within { ((2.years.ago)..(2.years.from_now)) }
end
Другое решение - полагаться на тот факт, что validates_inclusion_of
требует только объекта : in
, который отвечает на include?
. Создайте диапазон с отложенной оценкой следующим образом:
class DelayedEvalRange
def initialize(&range_block)
@range_block = range_block
end
def include?(x)
@range_block.call.include?(x)
end
end
class FirstModel
validates_inclusion_of :dated_on, :in => (DelayedEvalRange.new() { ((5.years.ago)..(5.years.from_now)) })
end