开发者

Rails: method_missing is not called on model when using associations

My current code:

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>

This happens because of this code in rails which only passes methods that exist to the model.

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

This is what I want:

Product.last.category.ex_undefined_metho开发者_开发百科d #=> true

How can I accomplish this?


Note that the AssociationProxy object only sends on methods that the target claims to respond_to?. Therefore, the fix here is to update respond_to? as well:

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

In fact, you should always update respond_to? if you redefine method_missing - you've changed the interface of your class, so you need to make sure that everyone knows about it. See here.


Chowlett's response is indeed the way to go imho.

But, if you are using Rails 3*, make sure to include the second argument that has been introduced in the responds_to? definition:

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


Replace

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

by

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

As monkey patch to the AssociationProxy

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜