我如何使用Ruby元编程来重构这个公共代码?
我继承了一个项目,其中有很多写得很糟糕的Rake任务,需要清理一下。因为rakefile是巨大的,并且经常容易产生奇怪的、无意义的依赖关系,所以我通过将所有内容重构为类来简化和隔离这些东西 具体而言,该模式如下所示:我如何使用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
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。