我有building .net systems with ruby / rake的一个小但不断增长的框架,我已经有一段时间了。在此代码库中,我有以下内容:
require 'rake/tasklib'
def assemblyinfo(name=:assemblyinfo, *args, &block)
Albacore::AssemblyInfoTask.new(name, *args, &block)
end
module Albacore
class AssemblyInfoTask < Albacore::AlbacoreTask
def execute(name)
asm = AssemblyInfo.new
asm.load_config_by_task_name(name)
call_task_block(asm)
asm.write
fail if asm.failed
end
end
end
该代码遵循的模式在框架中重复约20次。每个版本的不同之处在于正在创建/调用的类的名称(而不是AssemblyInfoTask,它可能是MSBuildTask或NUnitTask),以及execute方法的内容。每个任务都有自己的执行方法实现。
我一直在修复这种代码模式中的错误,每次需要修复时我都要重复修复20次。
我知道可以做一些元编程魔术,并从一个位置为我的每个任务连接这个代码......但是我很难让它工作。
我的想法是,我希望能够打出这样的话:
create_task :assemblyinfo do |name|
asm = AssemblyInfo.new
asm.load_config_by_task_name(name)
call_task_block(asm)
asm.write
fail if asm.failed
end
这将连接我需要的一切。
我需要帮助!提示,建议,有人愿意解决这个问题......我怎样才能不断重复这种代码模式呢?
更新:您可以在此处获取完整的源代码:http://github.com/derickbailey/Albacore/提供的代码是/lib/rake/assemblyinfotask.rb
答案 0 :(得分:4)
好的,这里有一些元编程可以做你想要的(在ruby18或ruby19中)
def create_task(taskname, &execute_body)
taskclass = :"#{taskname}Task"
taskmethod = taskname.to_s.downcase.to_sym
# open up the metaclass for main
(class << self; self; end).class_eval do
# can't pass a default to a block parameter in ruby18
define_method(taskmethod) do |*args, &block|
# set default name if none given
args << taskmethod if args.empty?
Albacore.const_get(taskclass).new(*args, &block)
end
end
Albacore.const_set(taskclass, Class.new(Albacore::AlbacoreTask) do
define_method(:execute, &execute_body)
end)
end
create_task :AssemblyInfo do |name|
asm = AssemblyInfo.new
asm.load_config_by_task_name(name)
call_task_block(asm)
asm.write
fail if asm.failed
end
元程序员工具箱中的关键工具是:
class<<self;self;end
- 获取任何对象的元类,以便您可以在该对象上定义方法define_method
- 因此您可以使用当前局部变量定义方法也很有用
const_set
,const_get
:允许您设置/获取常量class_eval
:允许您使用def
定义方法,就像您在class <Classname> ... end
区域一样答案 1 :(得分:1)
像这样的东西,在ruby 1.8.6上测试:
class String
def camelize
self.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join
end
end
class AlbacoreTask; end
def create_task(name, &block)
klass = Class.new AlbacoreTask
klass.send :define_method, :execute, &block
Object.const_set "#{name.to_s.camelize}Task", klass
end
create_task :test do |name|
puts "test: #{name}"
end
testing = TestTask.new
testing.execute 'me'
核心部分是“create_task”方法,它: