Extend quesion

The following doesn't quite do what I would expect:

module A
    def foo
       "A::foo"
    end
end
module B
    def foo
       "B::foo"
    end
end
class Demo
    def to_a
        self.extend(A)
    end
    def to_b
        self.extend(B)
    end
    def initialize
      self.extend(A)
    end
end

d = Demo.new
puts d.foo #=> A::foo
d.to_b
puts d.foo #=> B::foo
d.to_a
puts d.foo #=> B::foo (but I expected A::foo)

OK, I can kind of see why this happended; B's foo 'hide's A's foo method. But
I'm not sure why it didn't happen in the second 'puts' above (ie. why
wouldn't it just keep printing A::foo every time?). I think the answer is that
A has already been mixed-in so it's not really mixed-in again (true?).

And how would I go about making this work so that it prints:

···

A::foo
B::foo
A::foo

?

Phil

Dňa Streda 15 Február 2006 03:23 Phil Tomson napísal:

I think the answer is that A has already been mixed-in so it's not really
mixed-in again (true?).

Quite so.

And how would I go about making this work so that it prints:

A::foo
B::foo
A::foo

?

Make the message printed an instance attribute and change it? *duck*

Mixins aren't plain supposed to do that. They are to add methods, not rewrite
them at whim. If you -have- to do this via metaprogramming, use singleton
methods. Especially in non-trivial code, heavy use of #extend to change
behaviour over and over again would get really messy.

David Vallner

I see that you've already got a solution, but how about something like this?

module A
   def foo
      "A::foo"
   end
end
module B
   def foo
      "B::foo"
   end
end
class Demo
   def to_a
       @available_modules[:A] = Object.new.extend(A) unless
@available_modules[:A]
       @current_module = @available_modules[:A]
   end
   def to_b
       @available_modules[:B] = Object.new.extend(B) unless
@available_modules[:B]
       @current_module = @available_modules[:B]
   end
   #Or more generically
   def to_module(mod)
       mod_sym = mod.name.to_sym
       @available_modules[mod_sym] = Object.new.extend(mod) unless
@available_modules[mod_sym]
       @current_module = @available_modules[mod_sym]
   end
   def method_missing(sym, *args)
       @current_module.send(sym, *args)
   end
   def initialize
     @available_modules = {A.name.to_sym, Object.new.extend(A)}
     @current_module = [A.name.to_sym]
   end
end

···

On 2/14/06, Phil Tomson <ptkwt@aracnet.com> wrote:

The following doesn't quite do what I would expect:

module A
    def foo
       "A::foo"
    end
end
module B
    def foo
       "B::foo"
    end
end
class Demo
    def to_a
        self.extend(A)
    end
    def to_b
        self.extend(B)
    end
    def initialize
      self.extend(A)
    end
end

d = Demo.new
puts d.foo #=> A::foo
d.to_b
puts d.foo #=> B::foo
d.to_a
puts d.foo #=> B::foo (but I expected A::foo)

OK, I can kind of see why this happended; B's foo 'hide's A's foo method. But
I'm not sure why it didn't happen in the second 'puts' above (ie. why
wouldn't it just keep printing A::foo every time?). I think the answer is that
A has already been mixed-in so it's not really mixed-in again (true?).

And how would I go about making this work so that it prints:

A::foo
B::foo
A::foo

?

Phil

--
-Dan Nugent

In article <200602150449.40928.david@vallner.net>,

D=C5=88a Streda 15 Febru=C3=A1r 2006 03:23 Phil Tomson nap=C3=ADsal:

I think the answer is that A has already been mixed-in so it's not really
mixed-in again (true?).=20

Quite so.

And how would I go about making this work so that it prints:

A::foo
B::foo
A::foo

?

Make the message printed an instance attribute and change it? *duck*

Mixins aren't plain supposed to do that. They are to add methods, not rewri=
te=20
them at whim.

Yeah, I just thought it would be a nice, fairly easy solution.

If you -have- to do this via metaprogramming, use singleton=20
methods.

Yeah, I could probably do something like that by redefining the method...

(BTW: Using extend as above makes foo a singleton method )

Especially in non-trivial code, heavy use of #extend to change=20
behaviour over and over again would get really messy.

The only way to really get it to work would be to remove_method before doing
the extend. The problem with that is that remove_method doesn't work in that
context (removing singleton methods).

I should probably consider some sort of delegation pattern instead. One
fairly easy way would be to use method_missing and then change the target
object of the message (though I want to avoid overuse of method_missing because
1) it can make things difficult to debug and 2) I might need it for another
purpose in this application. )

Phil

···

David Vallner <david@vallner.net> wrote:

Phil Tomson wrote:

In article <200602150449.40928.david@vallner.net>,

D=C5=88a Streda 15 Febru=C3=A1r 2006 03:23 Phil Tomson nap=C3=ADsal:

I think the answer is that A has already been mixed-in so it's not
really mixed-in again (true?).=20

Quite so.

And how would I go about making this work so that it prints:

A::foo
B::foo
A::foo

?

Make the message printed an instance attribute and change it? *duck*

Mixins aren't plain supposed to do that. They are to add methods,
not rewri= te=20
them at whim.

Yeah, I just thought it would be a nice, fairly easy solution.

If you -have- to do this via metaprogramming, use singleton=20
methods.

Yeah, I could probably do something like that by redefining the
method...

Or use the strategy / state pattern.

(BTW: Using extend as above makes foo a singleton method )

Especially in non-trivial code, heavy use of #extend to change=20
behaviour over and over again would get really messy.

The only way to really get it to work would be to remove_method
before doing the extend. The problem with that is that remove_method
doesn't work in that context (removing singleton methods).

I should probably consider some sort of delegation pattern instead.
One fairly easy way would be to use method_missing and then change
the target object of the message (though I want to avoid overuse of
method_missing because 1) it can make things difficult to debug and
2) I might need it for another purpose in this application. )

I think you can use SimpleDelegator and change the target in between.
Didn't test it myself though.

Kind regards

    robert

···

David Vallner <david@vallner.net> wrote:

Dňa Streda 15 Február 2006 08:48 Phil Tomson napísal:

The only way to really get it to work would be to remove_method before
doing the extend. The problem with that is that remove_method doesn't work
in that context (removing singleton methods).

Not even in the singleton class?

David Vallner

In article <200602152248.43632.david@vallner.net>,

D=C5=88a Streda 15 Febru=C3=A1r 2006 08:48 Phil Tomson nap=C3=ADsal:

The only way to really get it to work would be to remove_method before
doing the extend. The problem with that is that remove_method doesn't wo=

rk

in that context (removing singleton methods).

Not even in the singleton class?

I couldn't get it to work. Maybe there's some way.

However, I've come up with another way of doing what I was trying to do.
What I needed was a Simulation functionality (where the code is run) and a
Translation functionality (where the code translated to another language).
Since the user doesn't need to switch from one to the other in the same run it
makes sense to inherit from a different class based on a commandline
argument(or mixin a different module). something along the lines of this
very simplified example:

  class A #could be modules too
    def foo
       "A::foo"
    end
  end
  class B
    def foo
       "B::foo"
    end
  end

  superclass = if ARGV[0] == "B"
               B
             else
               A
             end

  class Demo < superclass
    #could use include module instead of inheritance of superclass
  end

  d = Demo.new
  puts d.foo

ruby demo.rb #=> A::foo (default)
ruby demo.rb B #=> B::foo

This should do what I want and it doesn't lead to extra calls as with
delegation/method_missing schemes which could slow things down
a little bit.

Phil

···

David Vallner <david@vallner.net> wrote:

Dňa Štvrtok 16 Február 2006 01:33 Phil Tomson napísal:

In article <200602152248.43632.david@vallner.net>,

>D=C5=88a Streda 15 Febru=C3=A1r 2006 08:48 Phil Tomson nap=C3=ADsal:
>> The only way to really get it to work would be to remove_method before
>> doing the extend. The problem with that is that remove_method doesn't
>> wo=
>
>rk
>
>> in that context (removing singleton methods).
>
>Not even in the singleton class?

I couldn't get it to work. Maybe there's some way.

However, I've come up with another way of doing what I was trying to do.
What I needed was a Simulation functionality (where the code is run) and a
Translation functionality (where the code translated to another language).
Since the user doesn't need to switch from one to the other in the same run
it makes sense to inherit from a different class based on a commandline
argument(or mixin a different module).

I'll agree with Robert Klemme's previous post that you probably want to
delegate to a strategy in this case. Much less... well... weird. -And- you
can switch at your leisure too.

David Vallner

···

David Vallner <david@vallner.net> wrote:

In article <200602160202.15509.david@vallner.net>,

···

David Vallner <david@vallner.net> wrote:

D=C5=88a =C5=A0tvrtok 16 Febru=C3=A1r 2006 01:33 Phil Tomson nap=C3=ADsal:

In article <200602152248.43632.david@vallner.net>,

David Vallner <david@vallner.net> wrote:
>D=3DC5=3D88a Streda 15 Febru=3DC3=3DA1r 2006 08:48 Phil Tomson nap=3DC3=

=3DADsal:

>> The only way to really get it to work would be to remove_method before
>> doing the extend. The problem with that is that remove_method doesn't
>> wo=3D
>
>rk
>
>> in that context (removing singleton methods).
>
>Not even in the singleton class?

I couldn't get it to work. Maybe there's some way.

However, I've come up with another way of doing what I was trying to do.
What I needed was a Simulation functionality (where the code is run) and a
Translation functionality (where the code translated to another language).
Since the user doesn't need to switch from one to the other in the same r=

un

it makes sense to inherit from a different class based on a commandline
argument(or mixin a different module).

I'll agree with Robert Klemme's previous post that you probably want to=20
delegate to a strategy in this case. Much less... well... weird. -And- you=
=20
can switch at your leisure too.

Conditional inheritance considered weird?

At one point or another much of what we now consider common practice was
thought to be weird. :wink:

Phil

Well, certainly. I can remember that we had a discussion about
implementing role pattern by changing inheritance. But since it
doesn't work as one would like I live with that and try different
approaches. IMHO it's also more fun to try to get the best out of what
one has than to try to change the rules of the game all the time. :slight_smile:
And Ruby is so extremely flexible that there's almost nothing that
can't be done.

Maybe we can make a variant of the extends approach work. Here's how:
do not use extend directly but another method that simply stores the
module in a member variable. Then override method_missing to delegate
to methods of this module by rebinding them to self (is that possible?
need to try that).

Kind regards

robert

···

2006/2/16, Phil Tomson <ptkwt@aracnet.com>:

In article <200602160202.15509.david@vallner.net>,
David Vallner <david@vallner.net> wrote:
>D=C5=88a =C5=A0tvrtok 16 Febru=C3=A1r 2006 01:33 Phil Tomson nap=C3=ADsal:
>> In article <200602152248.43632.david@vallner.net>,
>>
>> David Vallner <david@vallner.net> wrote:
>> >D=3DC5=3D88a Streda 15 Febru=3DC3=3DA1r 2006 08:48 Phil Tomson nap=3DC3=
>=3DADsal:
>> >> The only way to really get it to work would be to remove_method before
>> >> doing the extend. The problem with that is that remove_method doesn't
>> >> wo=3D
>> >
>> >rk
>> >
>> >> in that context (removing singleton methods).
>> >
>> >Not even in the singleton class?
>>
>> I couldn't get it to work. Maybe there's some way.
>>
>> However, I've come up with another way of doing what I was trying to do.
>> What I needed was a Simulation functionality (where the code is run) and a
>> Translation functionality (where the code translated to another language).
>> Since the user doesn't need to switch from one to the other in the same r=
>un
>> it makes sense to inherit from a different class based on a commandline
>> argument(or mixin a different module).
>
>I'll agree with Robert Klemme's previous post that you probably want to=20
>delegate to a strategy in this case. Much less... well... weird. -And- you=
>=20
>can switch at your leisure too.

Conditional inheritance considered weird?

At one point or another much of what we now consider common practice was
thought to be weird. :wink:

--
Have a look: Robert K. | Flickr

Dňa Štvrtok 16 Február 2006 20:23 Phil Tomson napísal:

Conditional inheritance considered weird?

At one point or another much of what we now consider common practice was
thought to be weird. :wink:

By all means, play around with uncommon concepts. Except I'm more likely to
use established patterns that do the same. Matter of taste? Certainly. Up to
the point where one way doesn't work and the other does - at the end of the
day, it's usefulness that matters.

In the specific case of doing so only once, conditional inheritance does
roughly what delegating to a strategy would. But in the first posted example,
the latter approach gives you a few more points of flexibility that were
needed to achieve the expected behaviour.

Maybe there is a problem that is indeed most elegantly solved by runtime
conditional inheritance. Considering that delegation can technically replace
inheritance, as seen in prototype-based OO programming languages, I
personally doubt it. But this wasn't such a problem.

David Vallner