【问题标题】:Ruby: Passing a block to a class macro that defines instance methodsRuby:将块传递给定义实例方法的类宏
【发布时间】:2010-11-09 16:07:46
【问题描述】:

我正在努力编写类似于下面示例的代码(但实际上做了一些有用的事情)。传递给def_my_method 的块当然是在类的上下文中创建的,当我想在具有实例方法的实例的上下文中对其进行评估时。我该怎么做?

module Formatters
  # Really useful methods, included in several classes
  def format_with_stars(str)
    return "*** #{str} ***"
  end
end

class Test
  include Formatters
  STRINGS = ["aa", "bb"]

  def self.def_my_method(method_name, another_parameter, &format_proc)
    define_method(method_name) do
      # In reality, some more complex code here, then...
      return STRINGS.map(&format_proc)
    end
  end

  def_my_method(:star_strings, "another_parameter") { |str| format_with_stars(str) }
  # Define other methods
end

tt = Test.new
puts tt.star_strings
# Throws undefined method `format_with_stars' for Test:Class (NoMethodError)

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案1】:

    您可以使用instance_exec 在正确的上下文中执行传递的块。不要将 &format_proc 直接传递给对 map 的调用,而是传递一个使用实例 exec 调用它的块。

    类似这样的:

    def self.def_my_method(method_name, another_parameter, &format_proc)
      define_method(method_name) do
        # In reality, some more complex code here, then...
        return STRINGS.map{|str| instance_exec(str, &format_proc)}
      end
    end
    

    这会产生这个:

    $ ruby tt.rb 
    *** aa ***
    *** bb ***
    

    对我来说(tt.rb 是我给文件起的任意名称),我认为这就是你想要的这个例子。

    【讨论】:

    • 你不需要包装的过程,你可以做STRINGS.map {|str| instance_exec(str, &format_proc)}
    • @sepp2k - 哦,是的,这是一个更清晰的方法 - 谢谢。我已将您的建议纳入答案。 (任何有兴趣的人 - 看看编辑历史,看看我最初使用包装器 lambda 传递给 map 的想法。)
    • instance_exec 听起来很完美,但它不是核心 Ruby 吗?我的谷歌搜索表明它是 Rails 2 中引入的 Object 的扩展。我的简化示例是纯 Ruby,虽然真正的东西是 Rails - 但 Rails 1.2!所以我看不到任何使用它的方法。 :-(
    • 我想我已经用更多的谷歌搜索回答了我自己的问题。 eigenclass.org/hiki.rb?bounded+space+instance_exec 似乎与 Rails 2 中使用的实现相同。所以我将自己进行对象修补,我的问题就解决了。谢谢!
    • 我认为 instance_exec 可能起源于 Rails,但它是 ruby​​ 1.9 的一部分并且在 1.8.7 中,所以如果你使用的是最新的 ruby​​,你应该没问题。我上面的例子是用 1.8.7 完成的
    【解决方案2】:
    ... class Test - include Formatters + extend Formatters ...

    应该可以解决问题。

    【讨论】:

    • 它适用于我的简单示例,但只能通过将我的实例方法更改为类方法,这绝对不是我在真实代码中可以做到的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-05
    • 2012-06-10
    • 2021-07-13
    • 2014-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多