Extend question

Hello everybody,

I apologize if this a stupid question - but I'd be very grateful for
any hints!

Short Problem

···

-------------
Is there a way of _forcing_ methods to be
overwritten when including a module?

Background
----------
I'm implementing some algorithm, let's call it M, as a class. Now M
needs to be customised - e.g. different bits need to be
instatiated. If there was just one such bit, I'd create subclasses
that inherit from M.

However, there are several such bits -- and my idea was to realise
this by writing, for each such customisation point, different Modules
which all offer the same "plugin" method. As an example, look at the
code below.

class M
  def customize(param)
    if param == "useA" then
      extend(ModuleA)
    elsif param == "useB" then
      extend(ModuleB)
    else
      raise someException
    end
  end

  def runAlgorithm
     pluginMethod()
  end
end

module A
  def pluginMethod
     puts "module A"
  end
end

module B
  def pluginMethod
    puts "module B"
  end
end

But what happens if I customize the class several times?

m_obj = new M()

m_obj.runAlgorithm() -> fails; that it fine

First I run it extended with A...

m_obj.customize("useA")
m_obj.runAlgorithm() -> prints "module A"

Then extended with B...

m_obj.customize("useB")
m_obj.runAlgorithm() -> prints "module B"

But if I want to switch back to A, it goes wrong:

m_obj.customize("useA")
m_obj.runAlgorithm() -> prints "module B"

Somewhere in the documentation of Module I found that
Module.append_features only appends its features if they have not been
added already - I suspect that this is the underlying reason.

So, is there any way to "force" the methods to be overwritten, even if
it appears to be superfluous? Or am I using an unsuitable approach to
begin with? Any

Hi --

Hello everybody,

I apologize if this a stupid question - but I'd be very grateful for
any hints!

Short Problem
-------------
Is there a way of _forcing_ methods to be
overwritten when including a module?

You can dup the module:

   module A
     def x
       puts "A"
     end
   end

   module B
     def x
       puts "B"
     end
   end

   obj = Object.new
   obj.extend(A).x # A
   obj.extend(B).x # B
   obj.extend(A).x # B
   obj.extend(A.dup).x # A

I'm not sure whether there are hidden pitfalls to this, perhaps
involving constants.

David

···

On Wed, 14 Sep 2005, Sebastian [iso-8859-1] Padó wrote:

--
David A. Black
dblack@wobblini.net

Sebastian Padó wrote:

Hello everybody,

I apologize if this a stupid question - but I'd be very grateful for
any hints!

Short Problem
-------------
Is there a way of _forcing_ methods to be
overwritten when including a module?

Background
----------
I'm implementing some algorithm, let's call it M, as a class. Now M
needs to be customised - e.g. different bits need to be
instatiated. If there was just one such bit, I'd create subclasses
that inherit from M.

However, there are several such bits -- and my idea was to realise
this by writing, for each such customisation point, different Modules
which all offer the same "plugin" method. As an example, look at the
code below.

<snip/>

So, is there any way to "force" the methods to be overwritten, even if
it appears to be superfluous? Or am I using an unsuitable approach to
begin with? Any

In your case no effort is necessary since you just extend with one of your
modules and Ruby will complain as soon as the needed methods are invoked.

If you want to check in customize, you can use ModuleA.instance_methods to
check whether all methods are there.

An additional caveat: if you invoke customize multiple times you might run
into trouble if you need to extend the same instance with different
modules over time. An approach that uses delegation (see state pattern,
strategy pattern and related patterns) might be better in that case.

Kind regards

    robert

Sebastian Padó said:

So, is there any way to "force" the methods to be overwritten, even if
it appears to be superfluous? Or am I using an unsuitable approach to
begin with?

You might want to consider the Strategy Pattern ... create an object that
implements your plugin method and then delegate to that object whenever
the plugin is called. By using a separate object, it becomes much easier
to switch strategies at runtime.

class StrategyA
  def pluginMethod
    puts "A"
  end
end

class StrategyB
  ... similar to above
end

class M
  def customize(param)
    if param == "useA" then
      @strategy = StrategyA.new
    elsif param == "useB" then
      @strategy = StrategyB.new
    else
      raise someException
    end
  end

  def runAlgorithm
     pluginMethod
  end

  def pluginMethod
    @strategy.pluginMethod
  end
end

The strategy object is a bit less intimate with your original object than
the extend module approach ... this could good or bad, depending on what
you need.

···

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Sebastian Padó wrote:

Is there a way of _forcing_ methods to be
overwritten when including a module?

Here's a somewhat wild suggestion that I have found useful when running
up against the limitations of ruby's modules: use a proc instead. That is,
instead of:

  module ModuleA
    def pluginMethod
      puts "module A"
    end
  end

use

  ModuleA = proc do
    def pluginMethod
      puts "module A"
    end
  end

and then instance_eval the blocks instead of extending:

   ...
   if param == "useA" then
     instance_eval(ModuleA)

If there's just one method that changes, you might be better off using
define_method... but this way will handle more than one method. You
will get a warning about method redefinition if you enable warnings.... I think
undef'ing the method first will disable it.

This also works around the problem of not being able to redefine an already
existing method of a class in a module that gets included in that class. It is a fairly
low-level approach, so use with caution