2011-09-27 29 views
8

我知道我可以使用define_method动态地定义一个类的方法,并且我使用块的arity指定了这个方法所需的参数。我可以动态地定义一个带块的Ruby方法吗?

我想动态定义一个方法,它既接受可选参数又接受一个块。在Ruby 1.9中,这很容易,因为现在允许将块传递给块。

不幸的是,Ruby 1.8中不允许这样,所以下面将不工作:

#Ruby 1.8 
class X 
    define_method :foo do |bar, &baz| 
    puts bar 
    baz.call if block_given? 
    end 
end 

x = X.new 
x.foo("foo") { puts "called!"} #=> LocalJumpError: no block given 

yield更换明确block.call没有任何解决问题。
不幸的是,升级到Ruby 1.9并不适合我。这是一个难以解决的问题,还是存在解决方法?

回答

6

这个工程使用Ruby 1.8.7,而不是1.8.6:

class X 
    define_method(:foo) do |bar, &baz| 
    puts bar 
    baz.call if baz 
    end 
end 

测试用:

X.new.foo("No block") 
X.new.foo("With block") { puts " In the block!"} 
p = proc {puts " In the proc!"} 
X.new.foo("With proc", &p) 

给出:

No block 
With block 
    In the block! 
With proc 
    In the proc! 

(与1.8.6它给出syntax error, unexpected tAMPER, expecting '|'。)

如果你想可选参数以及块,你可以尝试这样的事:

class X 
    define_method(:foo) do |*args, &baz| 
    if args[0] 
     bar = args[0] 
    else 
     bar = "default" 
    end 
    puts bar 
    baz.call if baz 
    end 
end 

测试用:

X.new.foo 
X.new.foo { puts " No arg but block"} 

给出:

default 
default 
    No arg but block 
4

你可以做的是使用class_eval而不是define_method。这个缺点(除了不那么优雅)是你失去了词汇范围。但这通常不是必需的。

+0

我需要传递一个字符串到class_eval?我不能通过一个块,这将保持范围界定? – andrewdotnich

+2

@andrewdotnich好吧,那么你又回到了如何动态定义方法的问题。 –

+1

https://github.com/agrimm/zombie-chaser/blob/master/lib/zombie-chaser/chaser.rb有一个例子和一个链接到文件中间的博客文章。 –

相关问题