在异常外访问的局部变量

时间:2015-08-03 13:27:43

标签: ruby scope

当我添加超时块时,我开始发生undefined local variable or method `page'

begin
  Timeout::timeout(30) do
    page =  Nokogiri::HTML(open(url))
  end
rescue Timeout::Error
  Rollbar.error("Timeout on #{url}")
  return
end

# other code using page

我的Timeout代码/救援有问题吗?

3 个答案:

答案 0 :(得分:2)

块中定义的变量的范围限定为各自的块。例如:

1.times do
  some_var = 1
  p some_var # Prints 1
end

p some_var # Will throw an error

如果要将变量范围扩大到更高的上下文,则必须在块外部定义它:

some_var = nil
1.times do
  some_var = 1
  p some_var # Prints 1
end

p some_var # Prints 1

在你的情况下:

page = nil # Define the scope of the variable up here
begin
    Timeout::timeout(30) do
      page = Nokogiri::HTML(open(url)) # Now set that variable
    end
rescue Timeout::Error
    Rollbar.error("Timeout on #{url}")
    return
end

# other code using page

答案 1 :(得分:2)

首先:问题与您的begin/rescue/end阻止无关,所以让我们暂时删除异常处理:

Timeout::timeout(30) do
  page = Nokogiri::HTML(open(url))
end

page #=> undefined local variable or method `page'

这是因为do/end块创建了新的variable scope

  

(...)在其中创建的任何局部变量都不会泄漏到周围的范围。

您可以在块之外(即之前)定义page

page = nil

Timeout::timeout(30) do
  page = Nokogiri::HTML(open(url))
end

page #=> #<Nokogiri::HTML::Document...>

但还有其他选择。

如果您阅读了Timeout::timeout的文档,则会注意到它会返回该块的结果。所以你可以写:

page = Timeout::timeout(30) { Nokogiri::HTML(open(url)) }

OpenURI::OpenRead#open的文档也很有趣。它揭示了您可以将多个选项传递给open,特别是:

  

<强> :read_timeout

     

:read_timeout选项指定http连接的读取超时。

     

<强> :open_timeout

     

:open_timeout选项指定http连接的打开超时。

[有关详情,请参阅Net::HTTP#open_timeoutNet::HTTP#read_timeout

假设url是HTTP网址,您可以将timeout来电替换为:

begin
  page = Nokogiri::HTML(open(url, open_timeout: 30, read_timeout: 30))
rescue Timeout::Error
  Rollbar.error("Timeout on #{url}")
  return
end

或更具体:

begin
  page = Nokogiri::HTML(open(url, open_timeout: 30, read_timeout: 30))
rescue Net::ReadTimeout
  Rollbar.error("Timeout reading #{url}")
  return
rescue Net::OpenTimeout
  Rollbar.error("Timeout opening #{url}")
  return
end

请注意,begin 创建新的变量范围。

答案 2 :(得分:1)

变量page仅在timeout块中可见。您可以在阻止之前初始化page,例如page = nil或封装了方法中page值的检索。

def retrieve(url2)
  Timeout::timeout(30) do
    Nokogiri::HTML(open(url2))
  end
rescue Timeout::Error
  Rollbar.error("Timeout on #{url2}")

  nil
end

page = retrieve(url)