我的應用程序有一個STI模型:RoR:如何在控制器中創建一個對象,該控制器的類是動態確定的?
# file: app/models/metered_service.rb
class MeteredService < ActiveRecord::Base
...
end
# file: app/models/metered_services/pge_residential.rb
class PGEResidential < MeteredService
...
end
# file: app/models/metered_services/sce_residential.rb
class SCEResidential < MeteredService
...
end
和架構支持STI:
# file: db/schema.rb
create_table "metered_services", :force => true do |t|
t.integer "premise_id"
t.string "type"
end
MeteredService是一個嵌套的資源(雖然這不是真正相關的這個問題):
# file: config/routes.rb
resources :premises do
resources :metered_services
end
因此,這裏的交易:要創建一個MeteredService,用戶在下拉列表中選擇其中的許多子類之一。該表單將類名作爲字符串返回到params['metered_services']['class']
中的MeteredServicesController#create。現在我們需要創建適當的子類。
我正在工作的方法 - 排序 - 但我不知道這是不是最好的辦法:
def create
@premise = Premise.find(params[:premise_id])
MeteredService.descendants() # see note
class_name = params["metered_service"].delete("class")
@metered_service = Object.const_get(class_name).new(params[:metered_service].merge({:premise_id => @premise.id}))
if @metered_service.save
... standard endgame
end
end
我在做什麼是剝類名出params['metered_service']
這樣我就可以使用剩餘的參數來創建計量服務。並且class_name被解析爲一個類(通過Object.const_get
),所以我可以調用它的.new方法。
MeteredServices.descendants()
調用是因爲在開發模式下完成緩存的方式。它的工作原理,但它確實很難看 - 請參閱this question瞭解我爲什麼這樣做的原因。
有沒有更好/更可靠的方法來做到這一點?
要用戶輸入小心調用'const_get'。他們理論上可以傳遞任何東西。也許你可以過濾白名單? – 2011-05-25 05:14:55
我剛纔發現雖然我不能''MeteredService.new(:type =>'PGEResidential')',我**可以做'MeteredService.new(...){| m | m.type ='PGEResidential'}',(驚人地)似乎做了正確的事情,並且(向@John Gibb點頭)最終變得安全,因爲類型在保存時被檢查。 – 2011-05-25 05:52:50