18272

[code]

    require 'open-uri'
    urls = %w(url1, url2, url3)
    urls.each do |url|
      res = open(url)
    end
    
[/code]

open(url)で例外が発生しないときは、その場でresを返し、  
例外が発生したら次のurlを試したいのですが、  
どう書いたらいいでしょうか？


CC BY-SA 3.0
2015-10-31T09:41:22.490
18270
2015-11-03T15:59:21.223
12896
question
ruby
rubyで繰り返しの中で例外処理


使用文脈がわかりませんのでテストしてませんが、  
こんな感じでいいかと思います。

[code]

    urls = %w(url1 url2 url3)
    urls.each do |url|
        begin
            res = open(url)
            return res
        rescue
            next
        end
    end
    
[/code]


CC BY-SA 3.0
2015-10-31T10:13:44.620
18272
2015-10-31T10:13:44.620
5044
18270
answer

[code]

    require 'open-uri'
    urls = %w(url1, url2, url3)
    us = urls.each
    begin
      u = us.next rescue nil
      res = open(u)
    rescue
      retry
    end
    
[/code]


CC BY-SA 3.0
2015-10-31T10:36:04.150
18274
2015-11-03T15:59:21.223
2015-11-03T15:59:21.223
4010
4010
18270
answer


イテレータを終了させるためには`return`でなく`break`を使う必要があります。

ruby の`return`は **メソッド** を終了させて値を返すものです。(省略されることがほとんどかと思いますが)

[code]

    urls = %w(url1 url2 url3)
    urls.each do |url|
      begin
        res = open(url)
        break res
      rescue
        next
      end
    end
    
[/code]


CC BY-SA 3.0
2015-10-31T10:39:11.980
18275
2015-10-31T10:39:11.980
9575
18270
answer


Ruby on Rails に
[suppress](http://api.rubyonrails.org/classes/Kernel.html#method-i-suppress)
というメソッドがありまして、それを真似てみます。

[code]

    require 'open-uri'
    
    urls = %w(url1 url2 url3)
    
    def suppress
      yield 
      rescue
    end
    
    res = nil
    urls.each do |url|
      break if ( res = suppress{ open(url) } )
    end
    
[/code]

全ての URL でエラー(例外)となる場合は nil になります。


CC BY-SA 3.0
2015-10-31T12:01:33.707
18276
2015-10-31T12:12:33.103
2015-10-31T12:12:33.103
18270
answer


おそらくですが、やりたいことは「複数のURLの中から有効なURLを見つけたい」という処理ではないかと予想します。

僕が実装するなら、まずURLの有効無効をチェックするメソッドを定義します。

[code]

    require 'open-uri'
    
    def valid_url?(url)
      open(url)
    rescue SocketError, OpenURI::HTTPError 
      # サーバーが見つからない、もしくはページが見つからない場合はfalse
      false
    end
    
[/code]

例外クラスを指定せずにrescueしてしまうと、本来rescueすべきでない例外までrescueしてしまう恐れがあるため、目的に応じた例外クラスを指定する方がベターです。

次に、「URLを見つけたい」のであれば、「each + 変数に格納」ではなく、findやselectを使った方がシンプルかつ明示的なコードになります。

[code]

    urls = %w(url1 url2 url3)
    
    # 最初に見つかった1件を取得
    valid_url = urls.find{|url| valid_url?(url) }
    
    # 最後に見つかった1件を取得
    valid_url = urls.reverse.find{|url| valid_url?(url) }
    
    # 有効なURLをすべて配列として取得
    valid_urls = urls.select{|url| valid_url?(url) }
    
[/code]

まとめるとこんな実装になります。

[code]

    require 'open-uri'
    
    def valid_url?(url)
      open(url)
    rescue SocketError, OpenURI::HTTPError 
      false
    end
    
    urls = %w(url1 url2 url3)
    valid_urls = urls.select{|url| valid_url?(url) }
    
[/code]

質問内容とは若干視点の異なる回答になりましたが、コードの可読性、再利用性、拡張性、堅牢性といった点ではこうしたコードの方が良い結果をもたらすと思います。

以上、ご参考までに。

## 参考URL

  * [[初心者向け] RubyやRailsでリファクタリングに使えそうなイディオムとか便利メソッドとか - Qiita](http://qiita.com/jnchito/items/dedb3b889ab226933ccf)
  * [Ruby - Railsアプリケーションにおけるエラー処理（例外設計）の考え方 - Qiita](http://qiita.com/jnchito/items/3ef95ea144ed15df3637)


CC BY-SA 3.0
2015-10-31T21:49:42.593
18286
2015-10-31T21:49:42.593
85
18270
answer