2010-04-29 5 views
0

Je voudrais permettre à un parler une langue (qui est une collection de méthodes publiques stockées dans Langue module objet personne (instancié d'une classe Personne)):Ruby - Commutation dynamique entre les protocoles (avec des modules)

class Person 
    attr_accessor :current_language 

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

Supposons que les langues sont les suivantes:

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

    def sayounara 
     "さようなら。" 
    end 
    end 

    module French 
    def bonjour 
     "Bonjour ! (from #{@current_language} instance variable)" 
    end 

    def au_revoir 
     "Au revoir." 
    end 
    end 

    module English 
    def hello 
     "Hello! (from #{@current_language} instance variable)" 
    end 

    def bye 
     "Bye." 
    end 
    end 
end 

Exemple d'utilisation:

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)" 

Comme vous pouvez le voir, un langage est tel qu'un protocole. Ainsi, une personne peut activer un protocole spécifique, mais un seul à la fois.

Pour des raisons modulaires, le stockage de chaque langue dans un module est amical. Donc, je pense que c'est la façon la plus logique de Ruby, n'est-ce pas.

Mais, je crois qu'il est impossible d'écrire quelque chose comme ceci:

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

Selon vous, quelle devrait être la meilleure pratique de le faire?

Tous les commentaires et messages sont les bienvenus. Je vous remercie.

Cordialement

Répondre

0

Vous pouvez faire cette jolie élégance dans Ruby si vous vos modules correctement arrangez.

module LanguageProxy 
    def method_missing(phrase) 
    if @current_language.respond_to?(phrase) 
     @current_language.send(phrase) 
    else 
     super 
    end 
    end 
end 

module Language 
    module French 
    def self.bonjour 
     "Bonjour ! (from #{@current_language} instance variable)" 
    end 

    def self.au_revoir 
     "Au revoir." 
    end 
    end 

    module English 
    def self.hello 
     "Hello! (from #{@current_language} instance variable)" 
    end 

    def self.bye 
     "Bye." 
    end 
    end 
end 

class Person 
    attr_accessor :current_language 

    include LanguageProxy 

    def quit 
    @current_language = nil 
    end 
end 

person = Person.new 

person.current_language # => nil 
begin 
    p person.hello   # => may raise a nice no method error 
rescue 
    puts "Don't know hello" 
end 

person.current_language = Language::English 
p person.hello # => "Hello! (from english instance variable)" 
begin 
    p person.bonjour # => may also raise a no method error 
rescue 
    puts "Don't know bonjour" 
end 
person.quit 

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

Essentiellement, tout ce que nous faisons ici est la création d'une classe Proxy aux « messages inconnus s au module de langue stocké dans le Person » envoyer Person variable s @current_language instance. Le "truc" que j'ai utilisé ici est de faire hello, bye, etc. module méthodes, pas de méthodes d'instance. Ensuite, j'ai assigné le module réel dans @current_language.

Vous remarquerez également ici que la variable d'instance @current_language de Person n'est pas disponible dans les modules Language. Cela devient un peu plus compliqué si vous avez besoin des méthodes de langage pour accéder à ces variables: la solution rapide serait probablement de simplement les passer en paramètres.

Si vous voulez vraiment utiliser des symboles pour désigner la langue, vous devrez faire un peu de magie avec Language.const_get.

Sortie:

 
C:\temp\ruby>ruby person.rb 
Don't know hello 
"Hello! (from instance variable)" 
Don't know bonjour 
"Bonjour ! (from instance variable)" 
+0

Merci Mark pour cette astuce. Et ça marche bien! Cependant, je voudrais que les méthodes de ce module soient fusionnées et non fusionnées dynamiquement (avec une sorte d'inclusion dynamique) dans l'objet ... et puis (par exemple) y accèdent avec person.public_send(). Ainsi, les variables d'instance sont accessibles à partir des nouvelles méthodes. –

0

Juste pour montrer que vous pouvez le faire (pas que vous):

include "A::B::C".split("::").inject{|p,c| (p.class == String ? Kernel.const_get(p) : p).const_get(c)} 
+0

Merci pour votre réponse.Mais je ne comprends pas vraiment votre code. –