Switching dynamically between methods (inside modules)

Hi,

I would like to allow a person object (instanced from a Person class) to
speak a language (which is a collection of public methods stored in
Language module):

class Person
  attr_accessor :current_language

  def quit
    # Unselect the current language, if any:
    @current_language = nil
  end
end

Suppose that languages are the following:

module Language
  module Japanese
    def konnichiwa
      "こんにちは! (in #{@current_language} language)"
    end

    def sayounara
      "さようなら。 (in #{@current_language} language)"
    end
  end

  module French
    def bonjour
      "Bonjour ! (in #{@current_language} language)"
    end

    def au_revoir
      "Au revoir. (in #{@current_language} language)"
    end
  end

  module English
    def hello
      "Hello! (in #{@current_language} language)"
    end

    def bye
      "Bye. (in #{@current_language} language)"
    end
  end
end

Example of use:

person = Person.new

person.current_language # => nil
person.hello # => may raise a nice no method error

person.current_language = :english
person.hello # => "Hello! (from english instance variable)"
person.bonjour # => may also raise a no method error
person.quit

person.current_language = :french
person.bonjour # => "Bonjour ! (from french instance variable)"

As you can see, a language is such as a protocol. So a person can switch
on a specific protocol, but only one at a time.

For modular reasons, storing each language into a module is friendly. So
I think this way is the more logical Ruby way, isn't it.

So I would like to merge and unmerge dynamically module's methods (with
a kind of dynamic include) into the object... and then (for instance)
access them with person.public_send(). Thus, instance variables like
@current_language are still accessible from the new methods.

But, I believe that it is not possible to write something like this:

class Person
  # wrong code:
  include "Language::#{@current_language}" unless @current_language.nil?
end

According to you, what could be the best practice to do so?

Any comments and messages are welcome. Thank you.

Regards

···

--
Posted via http://www.ruby-forum.com/\.

A first, elegant approach suffers from the minor defect that it does not work ;(
module A
  def a; "A" end
end

module B
  def a; "B" end
end

o = Object::new
o.extend A
puts o.a
o.extend B
puts o.a
o.extend A
puts o.a

thus more work is needed
the following code will give you some ideas, I hope

class Module
  def apply obj
    this_mod = self
    methods = instance_methods( false ).map{ | mname | [ mname,
instance_method( mname ) ] }
    p methods
    class << obj; self end.module_eval do
      include this_mod
      methods.each do | name, mthd |
        define_method name do | *args, &blk |
           mthd.bind( self ).call( *args, &blk )
        end
      end
    end
  end
end
module A
  def a; "A" end
end

module B
  def a; "B" end
end

class Object
  def force_extend *mods
    mods.each do | mod |
      mod.apply self
    end
  end
end
o = Object::new
o.force_extend A
puts o.a
o.force_extend B
puts o.a
o.force_extend A
puts o.a

HTH
R.

···

On Fri, Apr 30, 2010 at 12:50 PM, Paul A. <cyril.staff@gmail.com> wrote:

--
The best way to predict the future is to invent it.
-- Alan Kay

Oh BTW, traits do exactly what you want, maybe you want to check out
my implementation
http://rubyforge.org/frs/?group_id=4642

might save you some troubles.
R.

To me, this problem cries out for delegation rather than inheritance
or module inclusion.

Here's a first cut.

module Language

  Japanese = {
    :konnichiwa => "こんにちは! (in Japanese language)",
    :sayounara => "さようなら。 (in Japanese language)"
  }

  French = {
    :bonjour => "Bonjour ! (in French language)",
    :au_revoir => "Au revoir. (in French language)"
  }

  English = {
    :hello =>"Hello! (in English language)",
    :bye => "Bye. (in English language)"
  }

  class NewBornKnowledge
    def self.method_missing(word, *args)
      "Goo Goo Gaa Gaa (in BabyTalk)"
    end

    def to_s
      nil
    end
  end

  class Knowledge

    def initialize(language)
      @language = language
      @dictionary = Language.const_get(language.to_sym)
    end

    def to_s
      @language
    end

    def method_missing(word, *args)
      @dictionary[word] || "that's not a word in #{@language}"
    end

  end

  def self.knowledge_for(language=nil)
    if language
      Knowledge.new(language)
    else
      NewBornKnowledge
    end
  end

end

class Person
  def initialize(language = nil)
    self.current_language = language
  end

  def method_missing(word, *args)
    @language_center.send(word)
  end

  def current_language=(language)
    @language_center = Language.knowledge_for(language)
  end

  def quit
    self.current_language = nil
  end
end

person = Person.new

person.current_language # => "Goo Goo Gaa Gaa (in BabyTalk)"
person.hello # => "Goo Goo Gaa Gaa (in BabyTalk)"

person.current_language = :English
person.hello # => "Hello! (in English language)"
person.bonjour # => "that's not a word in English"
person.quit

person.hello # => "Goo Goo Gaa Gaa (in BabyTalk)"
person.bonjour # => "Goo Goo Gaa Gaa (in BabyTalk)"
person.konnichiwa # => "Goo Goo Gaa Gaa (in BabyTalk)"

person.current_language = :French
person.bonjour # => "Bonjour ! (in French language)"

This also has the advantage that it can fairly easily be extended to
support multilingual people. Something even a few of us Americans
think is une bonne idée!

···

--
Rick DeNatale

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

To me, this problem cries out for delegation rather than inheritance
or module inclusion.

Absolutely!

Here's a first cut.

Reminds me of a bad record by a famous band which had otherwise really
great stuff - back in 60s to 80s... :slight_smile:

This also has the advantage that it can fairly easily be extended to
support multilingual people. Something even a few of us Americans
think is une bonne idée!

:slight_smile:

Kind regards

robert

···

2010/4/30 Rick DeNatale <rick.denatale@gmail.com>:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Impressive code, thanks Rick Denatale! Thank you too, Robert Dober! I
am currently reading your solutions.

Anyway, I don't know if delegation is the best option for my needs.
Actually, if for instance we update Person class such as:

class Person
  attr_accessor :name

  def initialize(language = nil)
    self.current_language = language
  end

  def method_missing(word, *args)
    @language_center.send(word)
  end

  def current_language=(language)
    @language_center = Language.knowledge_for(language)
  end

  def quit
    self.current_language = nil
  end
end

...and English hash like this:

module Language
  English = {
    :hello => "Hello! My name is #{@name}.",
    :bye => "Bye. (in English language)"
  }
  # ...
}

...then it's hard to access instance variables (and instance methods)
from person object:

person = Person.new
person.name = "Paul"

person.current_language = :English
person.hello # => "Hello! My name is ."

Maybe only inheritance can do it, no? It's tricky :s

Best regards.
P.

···

--
Posted via http://www.ruby-forum.com/.

To me, this problem cries out for delegation rather than inheritance
or module inclusion.

Absolutely!

Ok can you kinda stop of spoiling the fun ;).

I really did not analyze the use case. Delegation is often forgotten
and often could be used instead of inheritance with beneficial
effects.
However, traits are behavior based on, well traits and *not* inheritance.
I believe that traits are not really used anywhere in the spirit of
the "original". If I am mistaken that would be a joyful surprise. And
it would allow us to better study traits vs. x. In our case x being
delegation.

The question I would like to see answered is: Is it really the
responsibility of a delegate to do the work? Look at where the data
lives. In our case delegation is wonderful as there is an almost
perfect decoupling of the language data. When the delegate needs many
features of the delegator(sp?) however, traits become a really good
alternative.
And sorry for the advertising :wink:

Cheers
R.

···

On Fri, Apr 30, 2010 at 2:57 PM, Robert Klemme <shortcutter@googlemail.com> wrote:

2010/4/30 Rick DeNatale <rick.denatale@gmail.com>:

--
The best way to predict the future is to invent it.
-- Alan Kay

Impressive code, thanks Rick Denatale! Thank you too, Robert Dober! I
am currently reading your solutions.

Anyway, I don't know if delegation is the best option for my needs.
Actually, if for instance we update Person class such as:

class Person
attr_accessor :name

def initialize(language = nil)
self.current_language = language
end

def method_missing(word, *args)
@language_center.send(word)
end

def current_language=(language)
@language_center = Language.knowledge_for(language)
end

def quit
self.current_language = nil
end
end

...and English hash like this:

module Language
English = {
:hello => "Hello! My name is #{@name}.",

This does not work as the #{} is expanded right here, i.e. if you look
at the map you'll likely find this:
"Hello! My name is ."

:bye => "Bye. (in English language)"
}
# ...
}

...then it's hard to access instance variables (and instance methods)
from person object:

person = Person.new
person.name = "Paul"

person.current_language = :English
person.hello # => "Hello! My name is ."

Maybe only inheritance can do it, no? It's tricky :s

No, you just need to change the contract between Person and the
delegate. You can for example pass on self (i.e. the Person instance)
and the delegate extracts instance variable values via
instance_variable_get. You can also do it the other way round and
make delegate methods expect an object which replies to particular
methods (like a callback interface in other languages).

Inheritance is often abused IMHO. People seem to tend to lump too
much disparate functionality together in a single inheritance
hierarchy. At least that's my observation.

Kind regards

robert

···

2010/4/30 Paul A. <cyril.staff@gmail.com>:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

To me, this problem cries out for delegation rather than inheritance
or module inclusion.

Absolutely!

Ok can you kinda stop of spoiling the fun ;).

How can I stop you from having fun? It's a free world and you can do
whatever pleases you.

I really did not analyze the use case. Delegation is often forgotten
and often could be used instead of inheritance with beneficial
effects.

Exactly Rick's and my point.

However, traits are behavior based on, well traits and *not* inheritance.
I believe that traits are not really used anywhere in the spirit of
the "original". If I am mistaken that would be a joyful surprise. And
it would allow us to better study traits vs. x. In our case x being
delegation.

I would look a bit further into this if I had enough time right now.
I have to admit I don't really have a firm grip on traits so I would
have to do some research upfront to be able to properly comment on
that matter.

The question I would like to see answered is: Is it really the
responsibility of a delegate to do the work? Look at where the data
lives. In our case delegation is wonderful as there is an almost
perfect decoupling of the language data. When the delegate needs many
features of the delegator(sp?) however, traits become a really good
alternative.
And sorry for the advertising :wink:

No sweat.

Kind regards

robert

···

2010/4/30 Robert Dober <robert.dober@gmail.com>:

On Fri, Apr 30, 2010 at 2:57 PM, Robert Klemme > <shortcutter@googlemail.com> wrote:

2010/4/30 Rick DeNatale <rick.denatale@gmail.com>:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/