背景

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

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

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

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

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

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

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

user.rb

class User < ApplicationRecord
  has_many :devices, dependent: :nullify
end
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行でかけちゃう

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つしかないのであれば&&でつないで1行で書いてしまう. これは, コードを解析してくれるrubocopによって自動的にこの文法にしてくれる. また, error_responseの中身は以下のようになっている.

apprecation_controller.rb

def error_response(code, message = nil)
  render json: {
    status: code,
    message: message
  }
end

余談

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

空白文字列に対してはblank?が真になるので厳密には違う. 参考
また,blank?,とpresent?はrubyではなくrailsのメソッドで,レコードに対する処理の話である.

変数のように関数を使う

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をこんな感じで定義して, こんな感じでぶっこめば動く. オブジェクト指向にあまり慣れていなくて, なんかスネイクで書かれると変数, キャメルだと関数みたいな感覚があるのでこれをみると気持ち悪く感じる.

追記

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

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

users_controller.rb

# deviceとuserを紐付ける
device.user = user
device.save

これぞrubyというような書き方. 他の言語だと, deviceuser_idカラムにuseridカラムの値をいれないと…みたいなことを考え始める必要があるが, 正しく関連づけができているrubyでは”紐づける”という文字通りに書ける.

rescuebeginしなくてよい

users_controller.rb

def create
  # 色々処理
  rescue Exception::StandardError => e
    error_response(500, e.message)
end

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

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

application_controller.rb

def current_user
  @current_user ||= @authorization.user
end

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

感想

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