【问题标题】:Dynamically define many methods with same implementation but arbitrary arguments动态定义许多具有相同实现但任意参数的方法
【发布时间】:2013-09-29 13:41:01
【问题描述】:

我有很多这样的方法:

def create_machine(name, os_type_id, settings_file='', groups=[], flags={})
  soap_method = "#{self.class.name.split('::').last.to_underscore}_#{__method__}".to_sym
  args = method(__method__).parameters.map { |arg| arg[1] }
  soap_message = Hash[args.map { |arg| [arg, eval(arg.to_s)] }]
  VirtualBoxAPI.send_request(@cl.conn, soap_method, @this.merge(soap_message))
end

def register_machine(machine)
  soap_method = "#{self.class.name.split('::').last.to_underscore}_#{__method__}".to_sym
  args = method(__method__).parameters.map { |arg| arg[1] }
  soap_message = Hash[args.map { |arg| [arg, eval(arg.to_s)] }]
  VirtualBoxAPI.send_request(@cl.conn, soap_method, @this.merge(soap_message))
end

它们具有相同的实现,但不同数量的不同参数。在数十个类中的每一个中都会有数十个这样的方法。所以我想我会使用一些元编程来最小化代码重复。 我试图通过define_method 做到这一点,并希望得到这样的结果:

vb_method :create_machine, :args => [:name, :os_type_id], :optional_args => [:settings_file, :groups, :flags]

但我找不到将任意数量的命名(非 splat)参数传递给 define_method 的方法(我认为 splat 参数会使记录方法变得难以甚至不可能,也会使生成的 API 不方便)。

处理这个问题的最佳方法是什么(使用 Ruby 2.0)?

UPD 另一种方法是定义一个方法 vb_method:

def vb_method(*vb_meths)
  vb_meths.each do |meth|
    define_method(meth) do |message={}|
      soap_method = "#{self.class.name.split('::').last.to_underscore}_#{meth}".to_sym
      VirtualBoxAPI.send_request(@cl.conn, soap_method, @this.merge(message))
    end
  end
end

然后这个类就会有这样的调用:

vb_method :create_machine, :register_machine

但在这种情况下,我需要始终以哈希作为参数调用方法:

machine = vb.create_machine(name: 'my_vm', os_type_id: 'Windows95')

这正是我想要避免的,因为我认为在这种情况下生成的 API 无法记录并且使用起来不方便。

【问题讨论】:

  • 您是否研究过另一种名为 method_missing 的魔术方法? :)
  • @ArupRakshit 我猜 method_missing 基本相同,因为它不提供动态操作命名参数的可能性。
  • 这看起来像是一个糟糕的 hack,因为您基本上是通过依赖特定的参数名称传递给 API 来使用参数列表作为 Hahes。只需对create_machineregister_machine 的参数使用普通哈希。然后使用元编程创建您的通用方法将很容易。
  • @Max 我不确定我明白你的意思。一个例子真的很有帮助。

标签: ruby


【解决方案1】:

停止尝试避免选项哈希。这就是做事的“Ruby 方式”。它们并非不可能记录,几个主流 Ruby 库都以这种方式使用它们(首先想到的是 ActiveRecord 和 Mysql2)。

请注意,您可以为选项哈希提供默认参数,该参数用作文档并允许您减少代码重复。

另外,考虑一下如果您可以(以某种方式)将任意数量的命名参数传递给define_method,您的代码将如何工作。用户如何记住哪些参数是哪些?他们需要记住以这种方式定义的所有不同方法的所有不同位置参数的顺序和含义。当您有许多具有不同含义的参数的类似方法时,很难保持一切正常。关键字参数(本质上是 Ruby 的选项哈希)是专门为避免这种情况而创建的。

如果您担心错误检查,请定义一个辅助方法来检查选项哈希是否存在丢失/无法识别的键并引发信息异常:

def validate_options(known, opts)
  opts.each_key { |opt| raise "Unknown option: #{opt}" unless known.include?(opt) }
  known.each { |opt, required| raise "Missing required option: #{opt}" if required and not opts.include?(opt) }
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-14
    • 2013-07-03
    • 1970-01-01
    • 2011-10-15
    • 1970-01-01
    • 1970-01-01
    • 2019-06-08
    相关资源
    最近更新 更多