В моей модели ActiveRecord есть следующие классы:
def Property < ActiveRecord::Base
# attribute: value_type (can hold values like :integer, :string)
end
def PropertyValue < ActiveRecord::Base
belongs_to property
# attribute: string_value
# attribute: integer_value
end
Объект PropertyValue предназначен для хранения только строкового значения или целочисленного значения, в зависимости от типа, указанного в атрибуте value_type связанного объекта Property. Очевидно, нам не следует беспокоить пользователя класса PropertyValue с помощью этого базового механизма string_value / integer_value. Поэтому я хотел бы использовать виртуальный атрибут «значение» в PropertyValue, который выполняет что-то вроде этого:
def value
unless property.nil? || property.value_type.nil?
read_attribute((property.value_type.to_s + "_value").to_sym)
end
end
def value=(v)
unless property.nil? || property.value_type.nil?
write_attribute((property.value_type.to_s + "_value").to_sym, v)
end
end
Я хочу предложить пользователю представление для заполнения набора значений свойств, и когда представление публикуется, я бы хотел, чтобы объекты PropertyValue создавались на основе списка атрибутов, передаваемых из представления. Я привык к использованию операции build (attribute) для этого. Однако теперь возникает проблема, связанная с тем, что я не могу контролировать порядок, в котором происходит инициализация атрибута. Таким образом, присвоение атрибута value не будет работать, когда связь с атрибутом Property еще не была выполнена, поскольку тип_значения не может быть определен. Как правильно "Rails" способ справиться с этим?
Кстати, в качестве обходного пути я пробовал следующее:
def value=(v)
if property.nil? || property.value_type.nil?
@temp_value = v
else
write_attribute((property.value_type.to_s + "_value").to_sym, v)
end
end
def after_initialize
value = @temp_value
end
Помимо того, что я думаю, что это довольно уродливое решение, на самом деле оно не работает с операцией "сборки". @Temp_value устанавливается в операции "value = (v)". Кроме того, "after_initialize" выполняется. Но , значение "value = @temp_value" не вызывает операцию "value = (v)" как ни странно! Так что я действительно застрял.
РЕДАКТИРОВАТЬ: код сборки
Я действительно понял, что код для создания объектов Property будет полезен. Я делаю это из класса Product, который имеет ассоциацию has_many с Property. Код выглядит следующим образом:
def property_value_attributes=(property_value_attributes)
property_value_attributes.each do |attributes|
product_property_values.build(attributes)
end
end
В то же время я понял, что я сделал неправильно в операции after_initialize; следует читать:
def after_initialize
@value = @temp_value
end
Другая проблема заключается в том, что сопоставление свойств во вновь созданном объекте property_value никогда не будет установлено до тех пор, пока не произойдет фактическое сохранение (), то есть после after_initialize. Я заставил это работать, добавив значение value_type соответствующего объекта свойства к представлению, а затем передав его через атрибуты, установленные после публикации. Таким образом, мне не нужно создавать экземпляр объекта Property только для извлечения value_type. Недостаток: мне нужен избыточный метод доступа «value_type» в классе PropertyValue.
Так что это работает, но мне все еще очень интересно, есть ли более чистый способ сделать это. Еще один способ - убедиться, что объект свойства сначала присоединен к новому PropertyValue, прежде чем инициализировать его другими атрибутами, но затем механизм просочится в «объект клиента», который тоже не слишком чист.
Я бы ожидал, что какой-то способ переопределит функциональность инициализатора таким образом, что я мог бы повлиять на порядок, в котором присваиваются атрибуты. Что-то очень распространенное в таких языках, как C # или Java. Но в Rails ...?