【问题标题】:How do I rescue an exception from inside an Enumerator?如何从枚举器内部抢救异常?
【发布时间】:2014-09-08 23:56:30
【问题描述】:

我正在 Rails 中编写一个应用程序,我想从 API 中获取大量信息——我通过 Enumerator 对象以 CSV 导出的形式流式传输这些信息。我想抢救在枚举器中调用的错误。

控制器:枚举器

def csv_lines( url )
    Enumerator.new do |y|
        per_page = 200

        # Parse parameters and get shelf information
        _params = BrowseScraper.get_params(url)
        shelf = BrowseScraper.get_preso( _params, 0 )
            total_items = shelf['response']['total_results']['all'].to_i
            total_pages = ( total_items / per_page.to_f ).ceil
            shelf_info  = BrowseScraper.crawl_ids( shelf['response']['query']['category'] )

        y << BrowseScraper.csv_header(url, shelf_info, total_items, ["Tool ID", "Name", "Price", "URL"])

        total_pages.times { |i| y << BrowseScraper.csv_body( _params, per_page, i+1) }
    end
end

以下函数会引发错误,但我无法在枚举器之外捕获它们:

模型:方法

def self.get_params
  response = open(url)
  raise if response.code != 200
end

控制器:显示

def export
    url = params[:url]
    raise StandardError, "Please enter a Browse URL below" if !url || url.empty?

    respond_to do |format|
        format.csv do
            render_csv(url)
        end
        format.html { render_csv(url) }
    end
rescue => e
    flash[:error] = e.message
    redirect_to scraper_path
end

private
    def render_csv( url )
        set_file_headers
        set_streaming_headers

        response.status = 200

        # Rails should iterate this enumerator
        self.response_body = csv_lines(url)
    end

    def set_file_headers( name = "browse_export" )
        headers["Content-Type"] ||= 'text/csv'
        headers["Content-Disposition"] = "attachment; filename=\"#{name}.csv\""
        headers["Content-Transfer-Encoding"] = "binary"
        headers["Last-Modified"] = Time.now.ctime.to_s
    end

    def set_streaming_headers
        #nginx doc: Setting this to "no" will allow unbuffered responses suitable for Comet and HTTP streaming applications
        headers['X-Accel-Buffering'] = 'no'
        headers["Cache-Control"] ||= "no-cache"
        headers.delete("Content-Length")
    end

修复export 中引发的错误有效。挽救 Enumerator 中的错误是可行的(例如:

Enumerator do |y|
  begin
    y << BrowseScraper.get_params(_params)
  rescue => e
    Rails.logger.error "Failed to get parameters: #{e.message}"
  end
end

如何在 Enumerator 之外抢救异常,以便可以使用 flash 消息正确重定向用户?如何从 Enumerator 对象中传递异常? 不让我用 Enumerator 来拯救它的原因是什么:

def method
    Enumerator do |y|
        y << BrowseScraper.get_params(_params)
    end
rescue => e
    Rails.logger.error "Error in Enumerator is #{e.message}"
end

【问题讨论】:

  • Enumerator 类没有什么特别之处,您可以在包装开始/救援块中拯救未捕获的异常。在您的 method 方法中,您正在拯救 StandardError - 省略异常类时的默认值。你确定你的代码会引发这种异常或其子类吗?
  • 嘿@DavidUnric,感谢您的回复。我确保只抛出StandardErrors 或编写基于StandardErrors 的类。我什至可以看到我正在挽救Enumerator 中的正确错误。但我无法在外面拯救它。这是一个实例问题,我需要在 Enumerator 的第一个实例上拯救它吗?
  • 除非异常在 Enumerator 中被救出,否则它会传播到外部范围。以下代码在运行 def method; (1..10).each {|i| raise StandardError if i &gt; 5; p i}; rescue; p 'gotcha'; end 时会打印数字 1 到 5,然后字符串 'gotcha' 作为异常被救出。
  • 您需要更具体一些,并举例说明您实际使用的是Enumerator 的哪个实例。
  • 该实例似乎由self.response_body 调用,然后迭代枚举器。我想我提出问题的方式不再有效。看起来你可以从Enumerators 中拯救出来,但由于迭代是由response_body 完成的,我没有办法访问我需要拯救它的部分......所以我想我需要做我的拥有覆盖它的response_body 或类似的东西。

标签: ruby-on-rails ruby exception enumerator rescue


【解决方案1】:

我想我已经弄清楚这里发生了什么。当您在 Enumerator 中编写代码时,该块实际上并未在 Enumerator 中执行。因此,如果我在 Enumerator 中添加一个rescue,就没有关系了。

这是因为 Enumerator 中的 |y| 实际上是一个产生屈服的 yielder 对象(更多关于 Enumerator documentationEnumerator::Yielder documentation 的内容。

你必须事先抢救东西。

【讨论】:

    猜你喜欢
    • 2021-10-24
    • 2012-07-23
    • 1970-01-01
    • 2017-07-12
    • 1970-01-01
    • 1970-01-01
    • 2013-02-15
    • 2012-07-09
    • 1970-01-01
    相关资源
    最近更新 更多