日々Derailment

技術的なことの備忘録。脱線事故が起きまくってるので、なるべくrails wayを守れるよう頑張ります。

Rails初心者が個人的にもっと早く聞きたかったシリーズ1 type: :uuid

シリーズとか書いているけどシリーズ化するかは分かりません。

最近、改修という名の過去の自分と戦う羽目になるケースが非常に多いです。
気分はコロッセオディアボロです。
f:id:ys3128:20190707004047p:plain 1年経って見返してもよく分かるんですが、非常に無駄が多いんですね。
(無駄に気付けるってことは、それだけ成長したといえば聞こえはいいですが。)
ただ、無駄はよくない。リソースが少ない状況なので尚更。
そんなわけで今になって思う、もっと早く聞きたかったことをまとめておく。

何億番煎じか分からないけど、今回はuuidについて。

uuidとは

知らない人のために簡単に書いておくと
重複しない一意なid
のことです。

細かい原理とかはここで省きます。

いつ使うのか

Railsではデフォルトのprimary_keyがintegerのオートインクリメンタルになっている。
で、だいたいrails_wayに素直に従っていれば
/users/:id
とかのルーティングになっているので
/users/1
とかになっているわけですね。

これは簡単に別のユーザーのIDが推測できてしまうなどなど、色々な問題があります。
そこでuuidの出番です。

環境

rails 5.2.3
ruby 2.5.3
postgresql 9.6.4

導入方法

1.マイグレーションファイルを作成する
(ファイル名は分かりやすければなんでも良い)

rails g migration enable_uuid_extention

2.マイグレーションファイルの編集 & マイグレーション

# xxxxxxxxxxxxxxxxxx_enable_pgcrypto_extention.rb
class EnablePgcryptoExtention < ActiveRecord::Migration[5.2]
  def change
    enable_extension 'pgcrypto'
  end
end
rails db:migrate

3.モデルを作成する

rails g model Hoge name:string

4.マイグレーションファイルを編集&マイグレーション

class CreateHoges < ActiveRecord::Migration[5.2]
  def change
    create_table :hoges, id: :uuid do |t| # id: :uuidを追記する
      t.string :name

      t.timestamps
    end
  end
end
rails db:migrate

5.レコードの作成

irb(main):001:0> Hoge.create(name: "sample")
   (0.2ms)  BEGIN
  Hoge Create (29.8ms)  INSERT INTO "hoges" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["name", "sample"], ["created_at", "2019-07-06 04:42:08.809181"], ["updated_at", "2019-07-06 04:42:08.809181"]]
   (7.2ms)  COMMIT
=> #<Hoge id: "ff67302a-8319-42e6-b94e-be7b0241cde1", name: "sample", created_at: "2019-07-06 04:42:08", updated_at: "2019-07-06 04:42:08">

関連付け

class HogeRelationships < ActiveRecord::Migration[5.2]
  def change
    create_table :hoge_relationships do |t|
      t.references :hoge, foreign_key: true, type: :uuid # type: :uuidを指定する
      t.references :fuga, foreign_key: { to_table: :hoges }, type: :uuid

      t.timestamps
    end
  end
end

ちなみに今回の項目とは全く関係ないけど、
foreign_key: { to_table: :テーブル名 }
カラム名とは別のテーブルを指定できます。
同じモデル同士の関連付けでカラム名を変えたい場合などに使いましょう。

まとめ

サービス開発ではuuidほぼマストかなと思いますが、自分で調べてみないとこれ分からないですよね。
デフォルトでuuidがあると知らなかった頃は

 create_table :users, id:false do |t|
   t.string :id, null: false
  end

とか定義して、before_createでSecureRandomで作ったuuid値をセットするとかいう愚行をおかしてました。
(内輪向けのアプリでまだよかったけど)

パフォーマンスに関しても、通常のインクリメンタルと比べて低下は2割程度、というのを以前見かけました。 (ソースは探し直したけど見当たらず、、、。)
状況によると思いますが、それでidの重複を気にしなくて良くなるなら、全然OKなのかなという気がします。