2010-11-09 97 views
2

我正在努力寻找类似于下面示例的代码(但实际上做了一些有用的工作)。传递给def_my_method的块当然是在类的上下文中创建的,当我想在具有实例方法的实例的上下文中评估它时。我该怎么做呢?Ruby:将块传递给定义实例方法的类宏

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) 

回答

3

您可以使用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是我给了该文件的arbitary名),我认为这是你想要的这个例。

+1

你不需要包装的proc,你可以做'STRINGS.map {| str | instance_exec(str,&format_proc)}'。 – sepp2k 2010-11-09 18:55:40

+0

@ sepp2k - 哦,是的,这是一个更清晰的方式 - 谢谢。我已将您的建议纳入答案。 (任何感兴趣的人都可以看看编辑历史,看看我使用包装器'lambda'传递给'map'的原始想法。) – matt 2010-11-09 19:21:17

+0

instance_exec听起来很完美,但它不是核心Ruby是它吗?我的谷歌搜索表明它是在Rails 2中引入的对象的扩展。我简化的例子是纯Ruby,但真正的事情是Rails - 但Rails 1.2!所以我看不到任何方式来使用它。 :-( – 2010-11-11 10:38:21

1
 

... 

class Test 
- include Formatters 
+ extend Formatters 

... 
 

应该做的伎俩。

+0

它适用于我的简单示例,但只能通过将实例方法更改为类方法,这绝对不是我在实际代码中可以执行的操作。 – 2010-11-11 12:09:19