Overriden method still gets called

2019-07-04 00:03发布

I am using a library that is implementing a belongs_to association between two entries in a database. Since this is not the behaviour I need I want to override this method via prepend. But pry tells me that the original method is still called. I double checked and I'm using ruby 2.0.

The code that gets prepended:

module Associations
  module ClassMethods

    [...]
    #Add the attributeName to the belongsToAttributes
    #and add a field in the list for the IDs
    def belongs_to(attr_name)
      @belongsToAttributes ||= []
      @belongstoAttributes << attr_name

      create_attr attr_name.to_s
      attribute belongs_to_string.concat(attr_name.to_s).to_sym
    end

    def belongsToAttributes
      @belongsToAttributes
    end    
  end

  def self.included(base)
    base.extend(ClassMethods)
  end
end

# prepend the extension 
Couchbase::Model.send(:prepend, Associations)

I use this in this class:

Note: I also tried to directly override the method in this class but it still doesn't happen

require 'couchbase/model'

class AdServeModel < Couchbase::Model

[...]
  #I tried to add the belongs_to method like this
  #def belongs_to(attr_name)
  #   @belongsToAttributes ||= []
  #   @belongstoAttributes << attr_name

  #   create_attr attr_name.to_s
  #    attribute belongs_to_string.concat(attr_name.to_s).to_sym
  #  end

  #  def belongsToAttributes
  #    @belongsToAttributes
  #  end
end

When I check with pry it shows me that I end up in this method call:

def self.belongs_to(name, options = {})
  ref = "#{name}_id"
  attribute(ref)
  assoc = name.to_s.camelize.constantize
  define_method(name) do
    assoc.find(self.send(ref))
  end
end

Any pointer to what I'm doing wrong would be appreciated.

Edit: Ok I solved the problem like this:

 self.prepended(base)
    class << base
      prepend ClassMethods
    end
  end

end
# prepend the extension 
Couchbase::Model.send(:prepend, Associations)

Since Arie Shaw's post contains important pointers to solve this problem I will accept his answer. Although he missed the point about extending and prepending the method that I want to call. For a more detailed discussion about my trouble with prepending the methods please refer to this question.

1条回答
甜甜的少女心
2楼-- · 2019-07-04 00:31

According to the pry trace you posted, the method you wanted to monkey patch is a class method of AdServeModel, not a instance method.

  • The problem with your Associations module approach is, you are calling Module#prepend to prepend the module to the existing class, however, you wrote a self.included hook method which will only be called when the module is included (not prepended). You should write Module#prepended hook instead.

  • The problem with the directly overriding approach is, you were actually overriding the instance method, rather than the class method. It should be something like this:

require 'couchbase/model'

class AdServeModel < Couchbase::Model
  class << self
    # save the original method for future use, if necessary
    alias_method :orig_belongs_to, :belongs_to

    def belongs_to(attr_name)
      @belongsToAttributes ||= []
      @belongstoAttributes << attr_name

      create_attr attr_name.to_s
      attribute belongs_to_string.concat(attr_name.to_s).to_sym
    end

    def belongsToAttributes
      @belongsToAttributes
    end
  end
end
查看更多
登录 后发表回答