11
votes

Comment puis-je choisir la version d'un module à inclure de manière dynamique dans Ruby?

J'écris une petite application en ligne de commande Ruby qui utilise fileutils code> de la bibliothèque standard pour les opérations de fichiers. Selon la façon dont l'utilisateur appelle l'application, je veux inclure soit FileUtils code>, FileUtils :: DryRun code> ou FileUtils :: verbose code>.

Depuis include code> est privé, cependant, je ne peux pas mettre la logique de choisir dans l'objet méthode initialize code>. (Ce fut ma première pensée, car je pouvais transmettre les informations sur le choix de l'utilisateur en tant que paramètre nouveau code>.) Je suis venu avec deux options qui semblent fonctionner, mais je suis pas heureux avec soit: p>

  1. Définir une variable globale dans l'espace de noms de l'application basée sur le choix de l'utilisateur, puis faire inclure conditionnelle dans la classe: p>

    class Worker
      include FileUtils
      # shared Worker methods go here
    end
    class Worker::DryRun < Worker
      include FileUtils::DryRun
    end
    class Worker::Verbose < Worker
      include FileUtils::Verbose
    end
    
  2. Créer des sous-classes, où la seule différence est la version de FileUtils code> ils comprennent. Choisissez celui qui convient, selon le choix de l'utilisateur. P>

    class Worker
      case App::OPTION
      when "dry-run"
        include FileUtils::DryRun
        etc.
    


0 commentaires

3 Réponses :


8
votes

Et si c'est privé?

class Worker
  def initialize(verbose=false)
    if verbose
      (class <<self; include FileUtils::Verbose; end)
    else
      (class <<self; include FileUtils; end)
    end
    touch "test"
  end
end


4 commentaires

"Alors qu'est-ce qui" sonne bien pour moi. (C'était le exactement la chose la plus simple que je ne voyais pas.) Merci.


Oups, mon erreur. Le code que j'ai donné auparavant modifierait ouvrier de sorte que tout travailleur s utiliserait les mêmes paramètres. Maintenant, il utilise réellement la métaclass et permet des paramètres pré-travailleurs.


Au lieu de (classe << auto; inclure FileUtils; extrémité) Vous pouvez également utiliser étendre les fichiersUtils .


Merci d'édition. En fait, je suis heureux de l'avoir vu à la fois, car dans un cas, je pourrais souhaiter que le inclure pour être global à la classe et dans d'autres personnes par instance. @Konstantin - Merci pour la syntaxe alternative.



0
votes

Si vous souhaitez éviter le "commutateur" et injecter le module, la syntaxe

def initialize(injected_module = MyDefaultModule)
    extend injected_module
end


0 commentaires

1
votes

conditionnellement compris le module via les méthodes d'envoi fonctionne pour moi comme dans l'exemple testé ci-dessous:

class Artefact
  include HPALMGenericApi
  # the initializer just sets the server name we will be using ans also the 'transport' method : Rest or OTA (set in the opt parameter)
  def initialize server, opt = {}  
    # conditionally include the Rest or OTA module
    self.class.send(:include, HPALMApiRest) if (opt.empty? || (opt && opt[:using] opt[:using] == :Rest)) 
    self.class.send(:include, HPALMApiOTA) if (opt && opt[:using] opt[:using] == :OTA)    
    # ... rest of initialization code  
  end
end


0 commentaires