假设我有一个类:
class Basket < ActiveRecord::Base
has_many :fruits
“fruits”是一个STI基类,有“苹果”,“橙子”等子类......
我希望能在篮子中设置一个setter方法,如:
def fruits=(params)
unless params.nil?
params.each_pair do |fruit_type, fruit_data|
fruit_type.build(fruit_data)
end
end
end
但是,显然,我得到了一个例外:
NoMethodError (undefined method `build' for "apples":String)
我认为的解决方法是这样的:
def fruits=(params)
unless params.nil?
params.each_pair do |fruit_type, fruit_data|
"#{fruit_type}".create(fruit_data.merge({:basket_id => self.id}))
end
end
end
但是这会导致Fruit STI对象在Basket类之前被实例化,因此basket_id键永远不会保存在Fruit子类中(因为basket_id还不存在)。
我完全难过了。有人有什么想法吗?
答案 0 :(得分:1)
不要在Basket中添加setter方法,而是将它添加到Fruit:
中class Fruit < ActiveRecord::Base
def type_setter=(type_name)
self[:type]=type_name
end
end
现在,您可以在通过关联构建对象时传递类型:
b = Basket.new
b.fruits.build(:type_setter=>"Apple")
请注意,您无法以这种方式分配:type
,因为它受到质量分配的保护。
修改强>
哦,你想根据子类运行不同的回调?右。
你可以这样做:
fruit_type = "apples"
b = Basket.new
new_fruit = b.fruits << fruit_type.titleize.singularize.constantize.new
new_fruit.class # Apple
或为每种类型定义has_many
关联:
require_dependency 'fruit' # assuming Apple is defined in app/models/fruit.rb
class Basket
has_many :apples
end
然后
fruit_type = "apples"
b = Basket.new
new_fruit = b.send(fruit_type).build
new_fruit.class # Apple
答案 1 :(得分:-1)
在Ruby术语中,"#{x}"
简单地等同于x.to_s
,其中String值与字符串本身完全相同。在其他语言中,如PHP,您可以取消引用字符串并将其视为一个类,但这不是这种情况。你可能的意思是:
fruit_class = fruit_type.titleize.singularize.constantize
fruit_class.create(...)
constantize
方法从字符串转换为等效类,但它区分大小写。
请注意,您可能会有人可能会创建fruit_type
设置为"users"
的内容,然后继续创建管理员帐户。或许更负责任的是做一个额外的检查,你所做的是实际上是正确的班级。
fruit_class = fruit_type.titleize.singularize.constantize
if (fruit_class.superclass == Fruit)
fruit_class.create(...)
else
render(:text => "What you're doing is fruitless.")
end
以这种方式加载类时需要注意的一点是constantize
不会自动加载类,比如在应用程序中拼写它们。在开发模式下,您可能无法创建尚未显式引用的子类。您可以通过使用映射表来解决潜在的安全问题并一次性预加载,从而避免这种情况:
fruit_class = Fruit::SUBCLASS_FOR[fruit_type]
您可以像这样定义此常量:
class Fruit < ActiveRecord::Base
SUBCLASS_FOR = {
'apples' => Apple,
'bananas' => Banana,
# ...
'zuchini' => Zuchini
}
end
在模型中使用文字类常量会立即加载它们。