我如何使用Ruby元编程来重构这个公共代码?

我如何使用Ruby元编程来重构这个公共代码?,ruby,metaprogramming,rake,Ruby,Metaprogramming,Rake,我继承了一个项目,其中有很多写得很糟糕的Rake任务,需要清理一下。因为rakefile是巨大的,并且经常容易产生奇怪的、无意义的依赖关系,所以我通过将所有内容重构为类来简化和隔离这些东西 具体而言,该模式如下所示: namespace :foobar do desc "Frozz the foobar." task :frozzify do unless Rake.application.lookup('_frozzify') require 'tasks/foob

我继承了一个项目,其中有很多写得很糟糕的Rake任务,需要清理一下。因为rakefile是巨大的,并且经常容易产生奇怪的、无意义的依赖关系,所以我通过将所有内容重构为类来简化和隔离这些东西

具体而言,该模式如下所示:

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.
tasks/foobar.rb
中,我看到了如下内容:

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

  # ... Other tasks also in the :foobar namespace.
end
对我来说,这很好,因为它允许我将任务依赖项彼此分离,并将它们完全移动到另一个位置,并且我能够极大地简化事情并隔离依赖项。在您实际尝试运行任务之前,Rakefile不会满足
要求。以前,这会导致严重的问题,因为你甚至不能列出任务,而不让它爆炸

我的问题是我经常重复这个成语。请注意以下模式:

  • 对于每个名称空间
    :xyz_abc
    ,在文件
    tasks/[namespace].rb
    tasks/..
    中都有一个对应的类,类名类似于
    XyzAbc

  • 对于特定命名空间中的每个任务,在关联的命名空间类中都有一个同名的方法。例如,如果名称空间
    :foo_bar
    有一个任务
    :apples
    ,您将看到
    def apples()…
    FooBar
    类中,该类本身位于
    tasks/foo_bar.rb

  • 每个任务
    :t
    都定义了一个“元任务”
    \u t
    (即,任务名称前缀为下划线),用于执行实际工作

我仍然希望能够为我定义的任务指定一个
desc
-描述,每个任务的描述都会有所不同。当然,我有一小部分任务根本不遵循上述模式,因此我将在我的Rakefile中手动指定这些任务


我确信这可以通过某种方式进行重构,这样我就不必一遍又一遍地重复同一个习惯用法,但我缺乏经验来了解如何做到这一点。有人能帮我一下吗?

像这样的东西应该适合你

# 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
更新:添加了说明


(请注意,我还没有测试过它,所以可能会有一些小错误。)

这看起来确实可行,但如果他走这条路,他会如何指定任务描述?@John,说得好。我用散列替换了任务数组。通过这种方式,还可以设置描述。然而,这可能变得越来越模糊,因此在某个时候可能会创建某种类型的API。