Method dispatching issue with mixins

I’m working on a large codebase in ruby 1.8.7 and I’m encountering a
weird error regarding method delegation.

This is the expected behaviour and a reproduction of the issue:
module DiscountSupport
def attach_coupon
:in_discount_support
end
end

class Loan
include DiscountSupport
end

module LoanTest
def attach_coupon
:in_loan_test
end
end

include LoanTest # some file in the test suite does this great thing,
which includes a lot of test helper methods in Object

puts Loan.new.attach_coupon #.should eq :in_discount_support

Because include works with “append_features”, if a method already exists
on an object, it will not be overridden by an include. This was always
working, until in one of my tests a loan object started getting
dispatched to the LoanTest method. I’m attaching a coupon twice, with
the first time working as expected, and only the second time I’m getting
dispatched to the LoanTest method.

I’ve tried to debug this. I thought first, attach_coupon does not exist
anymore on loan?

loan.method(:attach_coupon)
=> #<Method: Loan(LoanDiscountSupport)#attach_coupon> (the expected and
correct location for loan.attach_coupon())
loan.method(:attach_coupon).arity
-2 (which tells us that attach_coupon() is variadic function, with at
least one argument, as opposed to the one from the test suite which
doesn’t have any required arguments).

So ruby still thinks that attach_coupon exists on loan and it’s defined
in LoanDiscountSupport, but when we call the method, we are not hitting
it.

This is the backtrace, where you can see that instead of being
dispatched to loan_discount_support, we are getting dispatched to a the
method in the test suite.

begin; loan.attach_coupon; rescue Exception => @e; end

[“gems/activerecord-1.15.6/lib/active_record/base.rb:1863:in
method_missing'", "lib/rails/models/loan.rb:2900:inmethod_missing’”,
“gems/activerecord-1.15.6/lib/active_record/associations/association_proxy.rb:123:in
send'", "gems/activerecord-1.15.6/lib/active_record/associations/association_proxy.rb:123:inmethod_missing’”,
“suite/lib/helpers/portal_helpers.rb:243:in attach_coupon'", "(pry):9:inattach_coupon’”,

Can anyone help out with some more debugging ideas on this issue?

Dear Constantin,

When you say that you’re "… attaching a coupon twice … " what
exactly are you doing?
Are you doing something like this?

loan = Loan.new

loan.attach_coupon.attach_coupon # calling attach_coupon twice in
chain?!?

If this is the case you can do it step by step.

loan.attach_coupon send the attach_coupon method of the Loan class.
And it returns the Symbol :in_discount_support
When you chain another attach_coupon method on it you’re trying to
call attach_coupon
on a Symbol object not on a Loan object.
As Symbol doesn’t have an attach_coupon method defined on it so it
follows
the upper classes till it hits “that other” attach_coupon
method that was latter included on Object and returns to you the other
Symbol (:in_loan_test)

So…

l = Loan.new
=> #Loan:0x000000010eea38

l.attach_coupon
=> :in_discount_support

l.attach_coupon.attach_coupon
=> :in_loan_test

Sorry if this is not the case.

Best regards,
Abinoam Jr.

On Tue, Aug 13, 2013 at 7:22 PM, Constantin G.

On Wed, Aug 14, 2013 at 12:22 AM, Constantin G. <
[email protected]> wrote:

class Loan
which includes a lot of test helper methods in Object
I’ve tried to debug this. I thought first, attach_coupon does not exist
So ruby still thinks that attach_coupon exists on loan and it’s defined
["gems/activerecord-1.15.6/lib/active_record/base.rb:1863:in

Can anyone help out with some more debugging ideas on this issue?

It’s hard to come up with explanations since your information is a bit
incomplete. You could insert this
before each invocation of #attach_coupon:

pp loan.class, loan.class.ancestors, loan.method(:attach_coupon)

(You need to require ‘pp’ at the top.)

It also seems that you are using ActiveRecord. That may include even
more
modules and / or change methods.

Kind regards

robert