【问题标题】:Calling private methods by symbol name in Ruby在 Ruby 中通过符号名称调用私有方法
【发布时间】:2016-09-09 03:57:45
【问题描述】:

我有一个方法的符号名称,我想用一些参数调用它。我真正想做的归结为这段代码sn-p:

method.to_proc.call(method)

在这种情况下,method 是对象上的方法的符号名称。在我的例子中,我试图调用一个恰好在对象上是私有的方法。

这是我得到的错误输出:

>$ ruby symbol_methods.rb 
symbol_methods.rb:33:in `call': private method `test_value_1' called for "value":String (NoMethodError)
    from symbol_methods.rb:33:in `block (2 levels) in <main>'
    from symbol_methods.rb:30:in `each'
    from symbol_methods.rb:30:in `block in <main>'
    from symbol_methods.rb:29:in `each'
    from symbol_methods.rb:29:in `<main>'

这是一个演示此行为的独立示例:

data = [
  ["value", true],
  ["any value here", true],
  ["Value", true],
]

def matches_value(string)
  string == "value"
end

def contains_value(string)
  string.gsub(/.*?value.*?/, "\\1")
end

def matches_value_ignore_case(string)
  string.downcase == "value"
end

#tests
[:matches_value, :contains_value, :matches_value_ignore_case].each_with_index do |method, index|
  test = data[index]
  value = test[0]
  expected_result = test[1]
  result = method.to_proc.call(value)  # <<== HERE
  puts "#{method}: #{result == expected_result ? 'Pass' : 'Fail'}: '#{value}'"
end

重要的位在标记为#tests 的块中。 data 变量是一组输入和预期结果。 test_value_* 方法是私有方法,是要运行的测试。

我尝试过public_send(method, value)method.to_proc.call(value),但都导致private method 错误。

在这种情况下,调用名为符号的私有方法的正确方法是什么?我正在寻找解释和语法正确的答案。

【问题讨论】:

    标签: ruby testing symbols proc dispatch


    【解决方案1】:

    请改用send

    puts "#{method}: #{send(method, value) == expected_result ? 'Pass' : 'Fail'}: '#{value}'"
    

    【讨论】:

    • @mudasobwa 这里的重点是调用私有方法。 public_send 恰恰是行不通的。
    • @sawa 哦,确实,很抱歉。
    • @delta 这相当于send(method, value == expected_result ? 'Pass' : 'Fail') - 它应该是send(method, value) == expected_result ? 'Pass' : 'Fail'。父母很重要。
    • @sawa 感谢您的解释。这就是我的问题的第一部分:“我正在寻找解释和语法正确的答案。”
    • @MichaelGaskill 谢谢!我会解决的。
    【解决方案2】:

    经过大量搜索,我找到了一个替代答案,而不是Object#send,它具有意想不到的功能优势。解决方案是使用Object#method 返回符号名称的Method 对象。

    Method 对象是类似Proc 的可调用对象,因此它实现了#call 接口,非常符合要求。 Object 在其接口中定义了许多这样有用的助手。

    在原始问题的上下文中,它是这样工作的:

    #tests
    [:test_value_1, :test_value_2, :test_value_3].each do |method|
      data.each do |test|
        value = test[0]
        expected_result = test[1]
        puts "#{method}: #{self.method(method).call(value) == expected_result ? 'Pass' : 'Fail'}: '#{value}'"
      end
    end
    

    重要的一点是:

    self.method(method).call(value)
    

    这会将符号名称转换为Method 对象,然后以value 作为参数调用该方法。就功能而言,这send method solution 大致等效。但是,有一些差异需要注意。

    send 会更有效率,因为在转换为Method 时没有开销。 Method#callsend 使用不同的内部调用机制,看起来send 的调用开销也更少。

    使用Object#method意想不到的功能Method 对象很容易转换为Proc 对象(使用Method#to_proc)。因此,它可以作为第一类对象存储和传递。这意味着它可以代替块提供或作为回调提供,从而有助于实现灵活的调度解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-27
      • 1970-01-01
      • 2020-06-02
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 2013-01-23
      相关资源
      最近更新 更多