如何将此代码转换为元编程,以便我可以停止复制它?

Der*_*ley 8 ruby metaprogramming dry

我有一个小的但不断增长的框架,用于构建带有ruby/rake的.net系统,我已经有一段时间了.在此代码库中,我有以下内容:

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
Run Code Online (Sandbox Code Playgroud)

该代码遵循的模式在框架中重复约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
Run Code Online (Sandbox Code Playgroud)

这将连接我需要的一切.

我需要帮助!提示,建议,有人愿意解决这个问题......我如何避免不得不一遍又一遍地重复这种代码模式?

更新:你可以在这里获得完整的源代码:http://github.com/derickbailey/Albacore/提供的代码是/lib/rake/assemblyinfotask.rb

ram*_*ion 4

好的,这里有一些元编程可以满足您的需求(在 ruby​​18 或 ruby​​19 中)

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
Run Code Online (Sandbox Code Playgroud)

元程序员工具箱中的关键工具是:

  • class<<self;self;end- 获取任何对象的元类,以便您可以在该对象上定义方法
  • define_method- 因此您可以使用当前局部变量定义方法

也有用的是

  • const_set, const_get: 允许您设置/获取常量
  • class_evaldef:允许您像在某个class <Classname> ... end区域一样定义方法