Surprising behavior when extending instances

This surprised me:

module English; def say; “yes”; end; end
module French; def say; “oui”; end; end
x = Object.new
x.extend English
x.say # “yes”
x.extend French
x.say # “oui”
x.extend English
x.say # “oui”

What wrong assumptions am I making that this surprises me? What is
the more elegant “Ruby way” of alternating an object’s behavior at
runtime?

Thanks,
Jim

Hi,

In message “Re: Surprising behavior when extending instances”
on Sat, 14 Nov 2009 23:50:05 +0900, Jim C. [email protected]
writes:

|This surprised me:
|
| module English; def say; “yes”; end; end
| module French; def say; “oui”; end; end
| x = Object.new
| x.extend English
| x.say # “yes”
| x.extend French
| x.say # “oui”
| x.extend English
| x.say # “oui”
|
|What wrong assumptions am I making that this surprises me? What is
|the more elegant “Ruby way” of alternating an object’s behavior at
|runtime?

#extend (and #include) include a module only once.

          matz.

Jim C.:

This surprised me:

module English; def say; “yes”; end; end
module French; def say; “oui”; end; end
x = Object.new
x.extend English

This adds English to the front of the method look-up path.

x.say # “yes”

At this point, the #say method is first looked-up in English.

x.extend French

This adds French to the front of the method look-up path.

x.say # “oui”

At this point, the #say method is first looked-up in French.

x.extend English

Hey, English is already in the method look-up path, no need to add it!

x.say # “oui”

Oui, French is still at the front of the method look-up path…

What wrong assumptions am I making that this surprises me?

That the second #extend call moved English
to the front of the method look-up path.

What is the more elegant “Ruby way” of
alternating an object’s behavior at runtime?

I don’t have hand-on experience, and there probably is a simple,
canonical way, but I’d start with looking at Object#instance_eval.

— Shot

On Sat, Nov 14, 2009 at 9:50 AM, Jim C. [email protected] wrote:

x.say # “oui”

What wrong assumptions am I making that this surprises me?

Matz has already explained that module inclusion is a one-time only
deal.

Here’s a bit on why that is.

http://talklikeaduck.denhaven2.com/2007/11/03/a-chat-with-matz-classs-variable-reversion-and-a-mystery-explained

What is
the more elegant “Ruby way” of alternating an object’s behavior at
runtime?

I’d use a form of delegation

module English; def self.agree; “yes”; end; end
module French; def self.agree; “oui”; end; end

class Speaker

attr_accessor :language

def initialize(language = English)
self.language = language
end

def say
language.agree
end

def speak(language)
self.language = language
end
end

jack = Speaker.new
jacques = Speaker.new(French)

jack.say # => “yes”
jacques.say # => “oui”

jack.speak(French)
jacques.speak(English)

jack.say # => “oui”
jacques.say # => “yes”

jack.speak(English)
jacques.speak(French)

jack.say # => “yes”
jacques.say # => “oui”


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale