2011-06-01 38 views
2

我當前的代碼:Rails:使用關聯時,不會在模型上調用method_missing

class Product < ActiveRecord::Base 
    belongs_to :category 
end 

class Category < ActiveRecord::Base 
    def method_missing name 
    true 
    end 
end 

Category.new.ex_undefined_method   #=> true 
Product.last.category.ex_undefined_method #=> NoMethodError: undefined method `ex_undefined_method' for #<ActiveRecord::Associations::BelongsToAssociation:0xc4cd52c> 

這是因爲rails中的這個代碼只傳遞了模型中存在的方法。

private 
def method_missing(method, *args) 
    if load_target 
    if @target.respond_to?(method) 
     if block_given? 
     @target.send(method, *args) { |*block_args| yield(*block_args) } 
     else 
     @target.send(method, *args) 
     end 
    else 
     super 
    end 
    end 
end 

這就是我想要的:

Product.last.category.ex_undefined_method #=> true 

我怎樣才能做到這一點?

+0

您的'Product'' belongs_to'行有錯字。這是在原始代碼中,還是隻是在這裏發佈的代碼? – Chowlett 2011-06-01 08:11:14

+0

只是在這裏發佈的代碼。 – 2011-06-01 08:18:00

+0

這是非常相似的:http://stackoverflow.com/questions/3386567/method-missing-override-is-not-working。我可能可以重寫BelongsToAssociation類的method_missing,但這似乎有點過於普遍。不是嗎? – 2011-06-01 08:19:34

回答

7

注意,AssociationProxy對象只發送關於目標聲稱respond_to?方法。因此,這裏的解決辦法是更新respond_to?還有:

class Category < ActiveRecord::Base 
    def method_missing(name, *args, &block) 
    if name =~ /^handleable/ 
     "Handled" 
    else 
     super 
    end 
    end 

    def respond_to?(name) 
    if name =~ /^handleable/ 
     true 
    else 
     super 
    end 
    end 
end 

事實上,你應該始終更新如果respond_to?重定義method_missing - 你已經改變了你的類的接口,所以您需要確保每個人都知道它。見here

0

通過

if block_given? 
    @target.send(method, *args) { |*block_args| yield(*block_args) } 
else 
    @target.send(method, *args) 
end 

替換

if @target.respond_to?(method) 
    if block_given? 
    @target.send(method, *args) { |*block_args| yield(*block_args) } 
    else 
    @target.send(method, *args) 
    end 
else 
    super 
end 

如猴補丁到AssociationProxy

+0

猴子補丁時,我總是感到討厭。我教那裏會有更好的方法! – 2011-06-01 12:08:13

2

Chowlett's response確實是去imho的路。但是,如果您使用的是Rails 3 *,請確保包含已在responds_to中引入的the second argument。定義:

def respond_to?(name,include_private = false) 
    if name =~ /^handleable/ 
    true 
    else 
    super 
    end 
end