如何将参数传递给define_method?


Answers:


198

传递给define_method的块可以包含一些参数。这就是您定义的方法接受参数的方式。当您定义一个方法时,您实际上只是在给该块起个昵称并在类中保留对其的引用。参数随块一起提供。所以:

define_method(:say_hi) { |other| puts "Hi, " + other }

好吧,这仅仅是纯粹的狂热。干得好,凯文·科斯特纳。
Darth Egregious

90

...以及是否需要可选参数

 class Bar
   define_method(:foo) do |arg=nil|                  
     arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> nil
 a.foo 1
 # => 1

...尽可能多的参数

 class Bar
   define_method(:foo) do |*arg|                  
     arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> []
 a.foo 1
 # => [1]
 a.foo 1, 2 , 'AAA'
 # => [1, 2, 'AAA']

...的组合

 class Bar
   define_method(:foo) do |bubla,*arg|
     p bubla                  
     p arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> wrong number of arguments (0 for 1)
 a.foo 1
 # 1
 # []

 a.foo 1, 2 ,3 ,4
 # 1
 # [2,3,4]

... 他们全部

 class Bar
   define_method(:foo) do |variable1, variable2,*arg, &block|  
     p  variable1     
     p  variable2
     p  arg
     p  block.inspect                                                                              
   end   
 end
 a = Bar.new      
 a.foo :one, 'two', :three, 4, 5 do
   'six'
 end

更新资料

Ruby 2.0引入了双splat **(两颗星),它(我引用)可以做到:

Ruby 2.0引入了关键字参数,而**的作用类似于*,但用于关键字参数。它返回带有键/值对的哈希。

...当然,您也可以在define方法中使用它:)

 class Bar 
   define_method(:foo) do |variable1, variable2,*arg,**options, &block|
     p  variable1
     p  variable2
     p  arg
     p  options
     p  block.inspect
   end 
 end 
 a = Bar.new
 a.foo :one, 'two', :three, 4, 5, ruby: 'is awesome', foo: :bar do
   'six'
 end
# :one
# "two"
# [:three, 4, 5]
# {:ruby=>"is awesome", :foo=>:bar}

命名属性示例:

 class Bar
   define_method(:foo) do |variable1, color: 'blue', **other_options, &block|
     p  variable1
     p  color
     p  other_options
     p  block.inspect
   end
 end
 a = Bar.new
 a.foo :one, color: 'red', ruby: 'is awesome', foo: :bar do
   'six'
 end
# :one
# "red"
# {:ruby=>"is awesome", :foo=>:bar}

我试图用关键字参数splat和double splat来创建一个示例:

 define_method(:foo) do |variable1, variable2,*arg, i_will_not: 'work', **options, &block|
    # ...

要么

 define_method(:foo) do |variable1, variable2, i_will_not: 'work', *arg, **options, &block|
    # ...

...但是这行不通,似乎有限制。当您考虑它时,splat运算符将“捕获所有剩余的参数”,而double splat将“捕获所有剩余的关键字参数”,因此将它们混合会破坏预期的逻辑。(我没有任何参考资料可证明这一点!)

2018年8月更新:

摘要文章:https : //blog.eq8.eu/til/metaprogramming-ruby-examples.html


有趣-特别是第4个区块:它确实在1.8.7上工作!第一块在1.8.7中不起作用,第二块有错字(应该是a.foo 1而不是foo 1)。谢谢!
索尼桑托斯

1
感谢您的反馈,错字是固定的,...关于红宝石1.9.3和1.9.2的所有示例都可以使用,我很肯定在1.9.1上也可以(但没有尝试)
等同

我在stackoverflow.com/questions/4470108/…上将此答案与可接受的答案结合在一起。以弄清楚如何在运行时覆盖(而不是覆盖)使用可选args和块的方法,并且仍然能够使用args调用原始方法并阻止。啊,红宝石。具体来说,我需要在开发环境中覆盖Savon :: Client.request,以便对主机(只能在生产环境中访问)的单个API调用。干杯!
pduey 2012年

59

除了Kevin Conner的回答:块参数不支持与方法参数相同的语义。您不能定义默认参数或块参数。

仅在Ruby 1.9中使用新的“ stabby lambda”语法替代了此语法,该语法支持完整的方法参数语义。

例:

# Works
def meth(default = :foo, *splat, &block) puts 'Bar'; end

# Doesn't work
define_method :meth { |default = :foo, *splat, &block| puts 'Bar' }

# This works in Ruby 1.9 (modulo typos, I don't actually have it installed)
define_method :meth, ->(default = :foo, *splat, &block) { puts 'Bar' }

3
实际上,我相信define_method上的块参数确实支持splat,它也可以提供一种定义默认参数的方法。
Chinasaur

1
Chinasaur关于允许使用splat的块参数是正确的。我在Ruby 1.8.7和1.9.1中都确认了这一点。
Peter Wagenet,09年

谢谢,我忘记了这一点。立即修复。
约尔格W¯¯米塔格

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.