Ruby on Railsでオシャレな処理を書く

## 背景

自分は現在院生2回生で, 4回生のころから京都のゲーム・Web会社で働いている. 今も細々と続けている中で, Ruby on Railsに触れる機会があったのでその知見を貯めておく.

また, Ruby on Railsは型が決まった書き方があるので, 少ない行数に多くの情報を詰め込むことができて, 書き終わった後の見通しがとてもいいと思う. そんなオシャレな書き方を少し紹介したい.

## 使用するデータのスキーマとモデル

今回紹介するのは, SNSアプリのAPIの中身の一部である. 

以下に, 端末の情報を持つ`device`テーブルと, ユーザーの情報をもつ`user`テーブルを定義する.

```ruby:schema.rb
ActiveRecord::Schema.define(version: 20_190_219_052_352) do
  create_table "devices", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.bigint "user_id"
    t.string "uuid"
    t.integer "user_agent"
  end

  create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
    t.string "email", null: false
    t.string "password_digest", null: false
  end
end
```

`user`と`device`の関係は1対多であり, `belongs_to`と`has_many`で関連付けされている. 以下にそれぞれもモデルを示す. 

```ruby:user.rb
class User < ApplicationRecord
  has_many :devices, dependent: :nullify
end
```

```ruby:Device.rb
class Device < ApplicationRecord
  belongs_to :user, optional: true

  # deviceとuser_idを紐づける
  def update_user_id(user)
    user.devices << self
  end

  def shape_response
    { uuid: uuid }
  end

end
```

## オシャレな文法たち

上記データ構造をもとにおしゃれな文法たちを紹介する.

### エラー処理が1行でかけちゃう

```ruby:users_controller.rb
# deviceが存在しない または　deviceがuser_idと紐づいている
device = Device.find_by(uuid: params[:device][:uuid])
error_response(404, "device is not found") && return if device.blank? || device.user.present?
```

ifのあと処理が1つしかないのであれば`&&`でつないで１行で書いてしまう. これは, コードを解析してくれる`rubocop`によって自動的にこの文法にしてくれる. また, `error_response`の中身は以下のようになっている.

```ruby:apprecation_controller.rb
def error_response(code, message = nil)
  render json: {
    status: code,
    message: message
  }
end
```

#### 余談

`nil?` + `empty?` = `blank?` ↔︎ `present?`

空白文字列に対しては`blank?`が真になるので厳密には違う. [参考](https://qiita.com/tanagoda/items/541110cb9821734b0623)
また,`blank?`,と`present?`はrubyではなくrailsのメソッドで,レコードに対する処理の話である.

### 変数のように関数を使う

```ruby:users_controller.rb
  # userのバリデーションを確認
  user = User.new(user_params)

  # 色々処理

end

private

def user_params
  params.require(:user).permit(:name, :mail, :password, :password_confirmation)
end
```

rubyでは最後の返り値をreturnしなくでいいので, `user_params`をこんな感じで定義して, こんな感じでぶっこめば動く. オブジェクト指向にあまり慣れていなくて, なんかスネイクで書かれると変数, キャメルだと関数みたいな感覚があるのでこれをみると気持ち悪く感じる. 

#### 追記

オブジェクト指向の定義をちゃんと理解しきれていないのかもしれませんが, 変数とメソッドの違いを意識しないところにオブジェクト指向を感じました.

### リレーションを最大限に活用する

```ruby:users_controller.rb
# deviceとuserを紐付ける
device.user = user
device.save
```
これぞrubyというような書き方. 他の言語だと, `device`の`user_id`カラムに`user`の`id`カラムの値をいれないと...みたいなことを考え始める必要があるが, 正しく関連づけができているrubyでは"紐づける"という文字通りに書ける.

### `rescue`は`begin`しなくてよい

```ruby:users_controller.rb
def create

  # 色々処理

  rescue Exception::StandardError => e
    error_response(500, e.message)
end
```
scaffoldとかでgenerateするときは`begin`~`resucue`~`end`が律儀に書かれているが, `begin`は`rescue`に飛ぶ範囲をしていするためだけのものであるので, 関数内の`StandardError`のみを返す場合は最後にちょこっとかいておけばOK.

### 値がなかったらとってくる

```ruby:application_controller.rb
def current_user
  @current_user ||= @authorization.user
end
```
これは`@current_user`が存在していたらそのまま`@current_user`を返して, 存在していなかったら`@authorization.user`を取ってきて`@current_user`に代入して返す関数である. 初めてみるとわけわからん. 自己代入の考え方.

## 感想

rubyの書き方は型がかちっとしていて, 最初びくびくしながら書いて, 案の定間違った書き方みたいなことが多々ある. 慣れればどうやってもきれいなコードになりそうなので, ルールをしっかり覚えたい.