【问题标题】:method.to_proc doesn't return from enclosed functionmethod.to_proc 不从封闭函数返回
【发布时间】:2018-03-20 11:19:34
【问题描述】:

我试图通过提取一个包含保护子句的方法来干燥 Rails 控制器,以便在发生错误时从控制器方法中过早返回。我认为这可能使用to_proc,就像这个纯Ruby sn-p:

def foo(string)
  processed = method(:breaker).to_proc.call(string) 
  puts "This step should not be executed in the event of an error"
  processed
end

def breaker(string)
  begin
   string.upcase!
  rescue
    puts "Well you messed that up, didn't you?"
    return
  end
  string
end

我的想法是,在breaker 方法上调用了to_proc,在rescue 子句中调用早期的return 语句应该可以避开foo 的执行。但是,它没有用:

2.4.0 :033 > foo('bar')
This step should not be executed in the event of an error
 => "BAR" 
2.4.0 :034 > foo(2)
Well you messed that up, didn't you?
This step should not be executed in the event of an error
 => nil 

请问有人可以吗

  1. 解释为什么这不起作用

  2. 建议一种实现这种效果的方法?

提前致谢。

编辑:当人们想知道我为什么要这样做时,上下文是我试图在 Rails 控制器中干掉 createupdate 方法。 (我试图对此采取积极态度,因为这两种方法都大约 60 LoC。糟糕。)两种方法都具有这样的块:

some_var = nil
if (some complicated condition)
  # do some stuff
  some_var = computed_value
elsif (some marginally less complicated condition)
  @error_msg =  'This message is the same in both actions.'
  render partial: "show_user_the_error" and return
# rest of controller actions ...

因此,我想将其提取为一个块,包括从控制器操作中提前返回。我认为使用 Proc 可能可以实现这一点,当它不起作用时,我想了解原因(感谢 Marek Lipa)。

【问题讨论】:

  • 1.这是因为Method#to_proc 返回lambda,它在return 和参数控制方面表现得更像方法。
  • 想知道是否可能是这种情况,那么名字很不幸......
  • 不是真的,lambda proc 也是,但不同的类型。
  • 是的,但是我失去了半个小时的生命,如果该方法被称为to_lambda:/
  • 这个方法被称为to_proc的原因是当你使用string.each(&:upcase)这样的表达式时,它是内部调用的标准方法。在这种情况下,它可以使用 &method(:breaker) 直接将这个方法“几乎”作为一个块传递。

标签: ruby-on-rails ruby


【解决方案1】:

怎么样

def foo(string)
  processed = breaker(string) 
  puts "This step should not be executed in the event of an error"
  processed
rescue ArgumentError
end

def breaker(string)
  begin
    string.upcase!
  rescue
    puts "Well you messed that up, didn't you?"
    raise ArgumentError.new("could not call upcase! on #{string.inspect}")
  end
  string
end

毕竟这可以说是一个非常好的异常用例。

【讨论】:

  • 非常有争议。 :)
【解决方案2】:

混淆的一部分似乎是Proclambda 与闭包(块)明显不同。

即使您可以将Method#to_proc 转换为标准Proc,例如Proc.new 这只会导致 LocalJumpError 因为 return 在这种情况下是无效的。

您可以使用next 突破标准Proc,但结果将与您现在拥有的lambda 相同。

Method#to_proc 返回lambda 的原因是lambda 比标准Proc 更能代表方法调用

例如:

def foo(string)
  string
end 
bar = ->(string) { string } #lambda 
baz = Proc.new {|string| string } 
foo
#=> ArgumentError: wrong number of arguments (given 0, expected 1)
bar.()
#=> ArgumentError: wrong number of arguments (given 0, expected 1)
baz.()
#=> nil

由于您要将方法转换为 proc 对象,因此我不确定您为什么还希望更改行为,因为这可能会导致歧义和混乱。请注意,出于这个原因,您也不能朝另一个方向前进,例如lambda(&baz) 不会产生 lambda 或提及 Here

现在我们已经解释了所有这些以及为什么不应该这样做,是时候记住在 ruby​​ 中没有什么是不可能的,所以这在技术上是可行的:

def foo(string)
  # place assignment in the guard clause
  # because the empty return will result in `nil` a falsey value
  return unless processed = method(:breaker).to_proc.call(string) 
  puts "This step should not be executed in the event of an error"
  processed
end

def breaker(string)
  begin
   string.upcase!
  rescue
    puts "Well you messed that up, didn't you?"
    return
  end
  string
end

Example

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多