Rails の開発で知っとくと便利な関数

実際の開発で知ったことやレビューでよく指摘を受ける事を備忘録を兼ねてメモ。
動作はRuby2.3.1とRails4.2.6で確認。

## tryメソッド (ActiveSupport)
みんな大好き(なハズ)tryメソッド。
Rails開発では、viewでモデルインスタンスの値を返す場面などで大活躍します。

```
[1] pry(main)> user = User.find_by(id: 9999)
=> nil
[2] pry(main)> user.try(:name)
=> nil
[3] pry(main)> user.name
NoMethodError: undefined method `name' for nil:NilClass
```

viewでNoMethodErrorが発生すると500エラーページが出てしまうので、それを予防するためによく使います。

## mapメソッドの&修飾詞
配列の値全部に処理を入れたい場合など、業務のちょっとしたかゆい場面で活躍します。

```
# 数値配列の中身を文字列に変換したい場合
[1] pry(main)> [2, 4, 6, 8].map(&:to_s)
=> ["2", "4", "6", "8"]

# 複数のUserインスタンスからnameのみを抽出する
[2] pry(main)> users = User.where(id: [1,2,3])
[3] pry(main)> users.map(&:name)
=> ["ほげ１", "ほげ２", "ほげ３"]
```

## pluckメソッド (ActiveRecored)
今年になってから知ったメソッド。
map(&:hoge)とかで抽出していたけど、どう考えてもこっちの方が良かった。

```
[1] pry(main)> User.where(id: [1,2,3]).pluck(:name)
=> ["ほげ１", "ほげ２", "ほげ３"]

# 複数項目も可能
[2] pry(main)> User.where(id: [1,2,3]).pluck(:id, :name)
=> [[1, "ほげ１"], [2, "ほげ２"], [3, "ほげ３"]]
```

## transposeメソッド
このメソッドは配列を行列に見立てて、行と列を入れ替える動きをする。
応用する事で2つの配列の1つをkey、もう1つをvalueとしたHashを作れる。

```
[1] pry(main)> hash_keys = %w(2 4 6)
=> ["2", "4", "6"]
[2] pry(main)> hash_values = %w(a b c)
=> ["a", "b", "c"]
[3] pry(main)> hash = Hash[ [hash_keys, hash_values].transpose ]
=> {"2"=>"a", "4"=>"b", "6"=>"c"}

# valuesをModelインスタンスにすると任意のkeyを持ったhashを作れる
[1] pry(main)> hash_keys = %w(a b c).map(&:to_sym)
=> [:a, :b, :c]
[2] pry(main)> hash_values = User.where(id: [1,2,3])
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (1, 2, 3)
=> [#<User:0x007f9dcf9a99c0 id: 1, name: "ほげ１">, #<User:0x007f9dcf9a9538 id: 2, name: "ほげ２">, #<User:0x007f9dcf9a90b0 id: 3, name: "ほげ３">]
[3] pry(main)> hash = Hash[ [hash_keys, hash_values].transpose ]
=> {:a=>#<User:0x007f9dcf9a99c0 id: 1, name: "ほげ１">, :b=>#<User:0x007f9dcf9a9538 id: 2, name: "ほげ２">, :c=>#<User:0x007f9dcf9a90b0 id: 3, name: "ほげ３">}
[4] pry(main)> hash[:b]
=> #<User:0x007f9dcf9a9538 id: 2, name: "ほげ２">
```

## split(//)メソッド
splitメソッド自体は普通に使われると思うが、引数に`//`を渡すと文字列を1文字区切りの配列にしてくれる。

```
[1] pry(main)> "abcde".split(//)
=> ["a", "b", "c", "d", "e"]

# マルチバイト文字にも対応
[2] pry(main)> 'あいうえお'.split(//)
=> ["あ", "い", "う", "え", "お"]


### 別件になるけど、この記事のためにsplit関数を調べていた気づいた事

# 1. 区切り文字に半角スペースを渡すとタブ文字や改行でも区切ってくれる
[3] pry(main)> "ab cd\tef\ngh".split(' ')
=> ["ab", "cd", "ef", "gh"]

# 2. 恥ずかしながらsplitに第二引数があることを知らなかった
[4] pry(main)> "abcde".split(//, 3)
=> ["a", "b", "cde"]
```

## group_byメソッド
便利なのに自分の周りではイマイチ使われない気がするメソッド。
抽出したデータをさらに区分けしたい場合などに便利です。

```
# 抽出したユーザ情報を性別で分ける
[1] pry(main)> User.where(id: [1,2,3]).group_by{|u| u.sex == 1 ? :male : :female}
=> {:male=>[#<User...(以下略)>], :female=>[#<User...(以下略)>]}

# with_indexを利用して配列のindexをkeyとしたhash化も
[2] pry(main)> %w(a b c d).group_by.with_index{|v,i| i}
=> {0=>["a"], 1=>["b"], 2=>["c"], 3=>["d"]}

# Modelから抽出した際の順番を保持したい場合などにも便利
[3] pry(main)> users = User.where(id: [1,2,3])
[4] pry(main)> users.map(&:id).group_by.with_index{|v,i| i}
=> {0=>["ほげ１"], 1=>["ほげ２"], 2=>["ほげ３"]}
```

使いドコロが難しいけど、上手く使うと余計なループや分岐が減らせます。

## grepメソッド、grep_vメソッド (grep_vはRuby2.3〜)
Ruby2.3で追加されたgrep_vメソッドをキッカケに知ったメソッド。
これを使ってリファクタリング出来そうな箇所が結構あるな。。。

```
# 配列の各要素に対し、正規表現で一致する要素を配列で返す
[1] pry(main)> %w(hoge piyo hogege bar hogegege).grep(/\Ahoge/)
=> ["hoge", "hogege", "hogegege"]

# ブロックを渡すと、正規表現に一致する要素に対し実行した結果を返す
[2] pry(main)> %w(hoge piyo hogege bar hogegege).grep(/\Ahoge/){|r| r.to_sym}
=> [:hoge, :hogege, :hogegege]

# Ruby2.3で追加されたgrep_vは、正規表現に一致しない要素を返す
[3] pry(main)> %w(hoge piyo hogege bar hogegege).grep_v(/\Ahoge/)
=> ["piyo", "bar"]
```


## between?メソッド (Comparable module)
日付や数値などで範囲判定したい場合に使うと楽できる・・かもしれない。
仕事ではGWや年末年始などの日付判定に使っています。

```
# GWの判定ロジック(2016/04/29〜2016/05/08)
[1] pry(main)> Time.now.between?(Time.parse('2016/04/29 00:00:00'), Time.parse('2016/05/08 23:59:59'))
```

## SQLの直接実行
便利というかみんな知ってるやり方かと思いきや、意外と若手の子が知らなかったので。

```
[1] pry(main)> users = ActiveRecord::Base.connection.select_all('SELECT id,name FROM users WHERE id IN (1,2,3)')
   (0.2ms)  SELECT id,name FROM users WHERE id IN (1,2,3)
=> #<ActiveRecord::Result:0x007f9dcf91b7...

# 戻りがActiveRecord::Resultのため扱いが違うので注意
[2] pry(main)> users.each{|u| p u['name']}
"ほげ１"
"ほげ２"
"ほげ３"
```