Как спасти исключение из Enumerator?

Я пишу приложение на Rails и хочу получить огромное количество информации от API, которую я передаю через объект Enumerator в виде экспорта CSV. Я хочу исправить ошибку, вызванную в Enumerator.

КОНТРОЛЛЕР: Счетчик

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

Следующие функции вызывают ошибки, но я не могу их отловить вне Enumerator:

МОДЕЛЬ: методы

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, чтобы я мог правильно перенаправить пользователя с помощью флэш-сообщения? Как передать исключение из объекта 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

person berfarah    schedule 08.09.2014    source источник
comment
В классе Enumerator нет ничего особенного, и вы можете спасать неперехваченные исключения в обертывающем блоке begin/rescue. В вашем методе method вы спасаете StandardError - значение по умолчанию, когда класс исключений опущен. Вы уверены, что ваш код вызывает такое исключение или его подкласс?   -  person David Unric    schedule 09.09.2014
comment
Привет, @DavidUnric, спасибо за ответ. Я убедился, что выбрасываю только StandardError или пишу классы, основанные на StandardError. Я даже вижу, что спасаю правильную ошибку в Enumerator. Но я не могу спасти его вне его. Является ли это проблемой экземпляра, когда мне нужно спасти его на первом экземпляре Enumerator?   -  person berfarah    schedule 09.09.2014
comment
Если исключение не спасено внутри Enumerator, оно распространяется на внешнюю область. Следующий код при запуске def method; (1..10).each {|i| raise StandardError if i > 5; p i}; rescue; p 'gotcha'; end будет печатать числа от 1 до 5, а затем будет спасена строка «попался», поскольку исключение будет спасено.   -  person David Unric    schedule 09.09.2014
comment
Вам нужно быть немного более конкретным и привести пример, какой экземпляр Enumerator вы на самом деле используете.   -  person David Unric    schedule 09.09.2014
comment
Похоже, экземпляр вызывается я. response_body, который затем перебирает перечислитель. Я думаю, что то, как я сформулировал вопрос, больше не актуально. Кажется, что вы можете спасти от Enumerators, но поскольку итерация выполняется response_body, у меня нет доступа к той части, где мне нужно ее спасти... Так что, я думаю, мне нужно сделать свой собственный response_body, который переопределяет это или что-то в этом роде.   -  person berfarah    schedule 09.09.2014
comment
У меня похожая проблема. Я не могу поймать исключение, сгенерированное внутри Enumerator, например: self.response_body = Enumerator.new { |y| поднять «Ошибка» } . Я пробовал «спасать» внутри Enumerator, а также в действии, ничего не получалось. Я использую Jruby и Torkbox. Я получаю уродливую страницу от Torkbox/JBOS, и я хотел бы настроить страницу ошибки с помощью рельсов. Кто-нибудь нашел решение этой проблемы?   -  person Exequiel    schedule 28.07.2015


Ответы (1)


Кажется, я понял, что здесь происходит. Когда вы пишете код в перечислителе, блок фактически не выполняется в перечислителе. Поэтому, если я добавлю rescue в перечислитель, это не имеет значения.

Это связано с тем, что |y| в Enumerator на самом деле является объектом yielder, который выполняет выдачу (подробнее об этом в документация по счетчику или Документация Enumerator::Yielder.

Вы должны спасти вещи заранее.

person berfarah    schedule 10.08.2015