3 votes

Comment utiliser la métaprogrammation Ruby pour refactoriser ce code commun ?

J'ai hérité d'un projet avec beaucoup de tâches Rake mal écrites que je dois nettoyer un peu. Comme les fichiers Rake sont énormes et souvent sujets à des dépendances bizarres et absurdes, je simplifie et isole un peu les choses en refactorisant tout en classes.

Plus précisément, ce modèle est le suivant :

namespace :foobar do
  desc "Frozz the foobar."
  task :frozzify do
    unless Rake.application.lookup('_frozzify')
      require 'tasks/foobar'
      Foobar.new.frozzify
    end
    Rake.application['_frozzify'].invoke
  end

  # Above pattern repeats many times.
end

# Several namespaces, each with tasks that follow this pattern.

En tasks/foobar.rb J'ai quelque chose qui ressemble à ça :

class Foobar
  def frozzify()
    # The real work happens here.
  end

  # ... Other tasks also in the :foobar namespace.
end

Pour moi, c'est une excellente chose, car cela me permet de séparer les dépendances des tâches les unes des autres et de les déplacer vers un autre emplacement entièrement, et j'ai pu simplifier radicalement les choses et isoler les dépendances. Le Rakefile n'a pas de problème de require jusqu'à ce que vous essayiez réellement d'exécuter une tâche. Auparavant, cela causait de sérieux problèmes car vous ne pouviez même pas lister les tâches sans que cela n'explose.

Mon problème est que je répète cette expression très fréquemment. Remarquez les modèles suivants :

  • Pour chaque espace de nom :xyz_abc il existe une classe correspondante dans tasks/... dans le fichier tasks/[namespace].rb avec un nom de classe qui ressemble à XyzAbc .

  • Pour chaque tâche d'un espace de nom particulier, il existe une méthode portant le même nom dans la classe de l'espace de nom associé. Par exemple, si l'espace de nom :foo_bar a une tâche :apples vous vous attendez à voir def apples() ... à l'intérieur de la FooBar qui se trouve elle-même dans tasks/foo_bar.rb .

  • Chaque tâche :t Définit une "méta-tâche". _t (c'est-à-dire le nom de la tâche préfixé d'un trait de soulignement) qui est utilisé pour effectuer le travail réel.

Je veux toujours être en mesure de spécifier un desc -pour les tâches que je définis, et qui sera différente pour chaque tâche. Et, bien sûr, j'ai un petit nombre de tâches qui ne suivent pas du tout le modèle ci-dessus, donc je vais les spécifier manuellement dans mon Rakefile.

Je suis sûr que cela peut être remanié d'une manière ou d'une autre pour que je n'aie pas à répéter le même idiome encore et encore, mais je manque d'expérience pour voir comment cela pourrait être fait. Quelqu'un peut-il me donner un coup de main ?

4voto

molf Points 34978

Quelque chose comme ça devrait marcher pour vous.

# Declaration of all namespaces with associated tasks.
task_lists = {
  :foobar => {
    :task_one => "Description one",
    :task_two => "Description two",
    :frozzify => "Frozz the foobar",
    :task_three => "Description three" },
  :namespace_two => {
    :task_four => "Description four",
    :etc => "..."} }

# For every namespace with list of tasks...
task_lists.each do |ns, tasks|
  # In the given the namespace...
  namespace ns do
    # For every task in the task list...
    tasks.each do |task_name, description|
      # Set the task description.
      desc description
      # Define the task.
      task task_name do
        unless Rake.application.lookup("_#{task_name}")
          # Require the external file identified by the namespace.
          require "tasks/#{ns}"
          # Convert the namespace to a class name and retrieve its associated
          # constant (:foo_bar will be converted to FooBar).
          klass = Object.const_get(ns.to_s.gsub(/(^|_)(.)/) { $2.upcase })
          # Create a new instance of the class and invoke the method
          # identified by the current task.
          klass.new.send(task_name)
        end
        Rake.application["_#{task_name}"].invoke
      end
    end
  end
end

Mise à jour : ajout de descriptions.

(Notez que je ne l'ai pas testé, il peut donc y avoir de petites erreurs).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X