Ruby, Railsの存在確認イディオムまとめ

## はじめに
- このメソッドが存在したら
- この変数が配列で何か入っていたら
- Item.firstで何か見つかったら

というのは結構出てくるパターンで、簡潔に書ける場合も多いのでまとめました。

使うとコードが分かりにくくなる場合もあるので、容量用法を守って使いましょう。

## 基本の確認
### ローカル変数が定義されているか
#### 前提
定義されていないローカル変数は使おうとすると例外を起こす

```ruby
hoge #=> NameError: undefined local variable or method `hoge' for main:Object
```

#### defined?
定義されているか確認できる

```ruby
defined? hoge #=> false
```

### オブジェクトのメソッド存在チェック
#### 前提
存在しないメソッドを呼び出すと例外

```ruby
hoge = nil
hoge.fuga #=> NoMethodError: undefined method `fuga' for nil:NilClass
```

#### respond_to?
メソッドが存在しているか確認できる

```ruby
hoge.respond_to? :fuga #=> false
```

## 事例集
ここからRails（Active Support）の導入を前提とします。

### 便利なメソッド
#### present?

偽でなくsizeが0でなければtrue

Stringクラスは/\A[[:space:]]*\z/へのマッチも[falseになる](https://github.com/rails/rails/blob/20c91119903f70eb19aed33fe78417789dbf070f/activesupport/lib/active_support/core_ext/object/blank.rb#L104)

```ruby
nil.present? #=> false
[Item.first].present? #=> true
```

### compact
nilの要素を消してくれる

```ruby
[nil].present? #=> true
[nil].compact.present? #=> false
```

#### presence
`#present?`を実行しtrueだったらselfを返す

```erb
<% if items = current_user.items.order(id: :asc).limit(5).presence %>
  <h2>アイテム一覧</h2>
  <ul>
    <% items.each do |item| %>
      <li><%= item.name %></li>
    <% end %>
  </ul>
<% end %>
```

#### tap
tapはブロック内でselfが使え、selfが返ります。ただ、breakするとその値が返ります。あんまりいい例が思いつかなかった…。

```ruby
items = [{ id: 1, title: 'item1' }, { id: 2 }]
items.map{|item| "タイトルは" + item.tap{|i| break { title: 'no title' } unless i[:title]}[:title] }
```

### try
メソッドが無ければnilを返す

```ruby
titles = ['1', nil, '123']
title_lengths = titles.map{|title| title.try(:length) }
#=> [1, nil, 3]
```

### 便利な演算子 (Ruby)
#### and
左辺が真だと右辺が評価される（演算子の優先順位低）

```erb
<% if item = current_user.items.first and item.prirority == 100 %>
  <h2><%= item.name %></h2>
<% end %>
```

#### &&
左辺が真だと右辺が評価される（演算子の優先順位高）

```erb
<% if item.status == :open or item.user == current_user && item.status != :delete %>
  <h2><%= item.name %></h2>
<% end %>
```

#### ||
左辺が偽だと右辺が評価される（演算子の優先順位高）

```ruby
next_item = Item.order(id: :asc).where("items.id > ?", @item.id).first || Item.first
```

#### &. (Ruby 2.3↑）, try! (Active Support)
オブジェクトがnilでない場合は呼び出し、nilの時はnilを返す

```ruby
hoge = nil

hoge&.fuga #=> nil
hoge.try!(:fuga) #=> nil
```

こんなことも可能

```ruby
Item.first&.user&.themes&.first
```

#### ||=
定義されていなかったら、デフォルト値を代入

```erb:old.html.erb
<%= partial 'item' %>
```

```erb:new.html.erb
<%= partial 'item', locals: { hidden_header: true } %>
```

||=を使って未定義時にfalseを入れておくことで、oldからの呼び出しにも耐えられる。

```erb:_item.html.erb
<% hidden_header ||= false %>
<% unless hidden_header %>
  hidden_header is true
<% end %>
```

ただし変数に偽のものが入っていると考えられる時は、使えない

```ruby
hoge = nil
hoge ||= "test"
hoge #=> "test"
```

#### ? :
三項演算子は演算子の優先順位的に連続で使える

```ruby
a ? 'a is true' : b ? 'b is true' : c ? 'c is true' : 'all false'
```

caseやelsifを使った方が良いこともままある

### 後置if
erbでも便利

```erb
<div class="<%= :tab_on if action_name == 'index' %>">
```

### 後置rescue

```ruby
require 'open-uri'
res = open('http://hoge.huga').read #=> SocketError: Failed to open TCP connection to hoge.huga:80 (getaddrinfo: nodename nor servname provided, or not known)
res = open('http://hoge.huga').read rescue nil #=> nil
```

## おわりに
「これ書かなくても情報量的には充分だよね？」と思った時、ぐぐってみると結構解決策があったりして便利。

世の中一般には汎用的ではないが、自分のところでは役立つ場合は、ヘルパーなどで定義してしまうのも手。

よいRuby & Railsライフを！