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
精彩评论