読者です 読者をやめる 読者になる 読者になる

ボクココ

サービス開発を成功させるまでの歩み

Startup Rails で知っておいた方がいいかもしれない 5 つのこと Tech編

ども、@kimihom です。

今回はこのイベントで発表させていただいた。

第3回スタートアップRails勉強会 - connpass

そのスライドは以下。

以下はいつも通りの補足的なことを書こうと思う。

1, form_for を使おう

form_for。これこそが Rails においてのキーワードであり、Rails のレールに乗る一番の方法だと考えている。その理由はスライド上に書いてあるんだけど、より具体的なところを解説しようと思う。

Routing。これを突き詰めると、 new と edit でかく form_for を同一のものにできる。Rails Guide に載ってるんだけど、ここはほとんどの人は見逃していると思う。

## 新しい記事の作成
# 長いバージョン
form_for(@article, url: articles_path)
# 短いバージョン(レコード識別を利用)
form_for(@article)
 
## 既存の記事の修正
# 長いバージョン
form_for(@article, url: article_path(@article), html: {method: "patch"})
# 短いバージョン
form_for(@article)

いちいち Form用の HTML を書いている時点で、何か設計がうまくいっていないことになる。

Model。 form_for の中で各項目で設定する f.text_field ** といったところ。ここで、もし form_for で指定したオブジェクトに値が入っていれば、デフォルトでその値を入れることができる。こういうのも勝手にやってくれるのは、本当に便利でわざわざ一個ずつフォームの Value に 対応する Model の値を入れていたりしたら、あなたはもう Rails のレールに乗っていない。

I18N 。locales に以下のようにいれれば、検証エラー時にその値を表示してくれる。エラー内容もモデルの Vallidation を入れてあげれば、そのモデルに erros が入っている。この messages 内にそのエラーメッセージを勝手に生成してくれる。とても便利だ。エラーメッセージを自前で表示している時点で、あなたはもう Rails のレールに乗っていない。

  activerecord:
    attributes:
      contact:
        name: 名前
        company: 会社名
        email: メールアドレス
        memo: メモ

Ajaxform_forremote: true をするだけで、Ajax の POST や PUT が実現できる。私たちがしなければならないのは、そのあとの ajax:success などのイベントを定義し、そのコールバック処理を書いてあげればいいだけなのだ。これを、jQuery や他のフレームワークで ajax でやっている時点で、あなたはもう Rails のレールに乗っていない。

ActiveModel

ActiveRecord の上位概念的な形として、 ActiveModel がある。これの使いどきとして、データとして保存はしたくないのだけど、何らかのデータの塊を作りたいって場合に便利だ。例えば Form のパラメータ管理などだ。例えば会員登録のフォームとかは、一気にたくさんの情報を入れるため、テーブルが分かれていて対応するモデルが複数あるときがあるだろう。そんな時はひとまず 会員登録用パラメータとして ActiveModel を定義し、そこで検証内容を諸々定義した上で form_for のオブジェクトに入れる。そうすればたくさんのモデルが関連するフォームのパラメータを綺麗に扱うことが可能だ。

class RegisterParam
  include ActiveModel::Model
  include ActiveModel::Validations::Callbacks

  attr_accessor :name, :company, :email, :email_confirm, :tel 

  before_validation :set_default

  validates :name, presence: true, length: {maximum: 255}
  validates ....

  def user_param
    {name: self.name}
  end

  def company_param
    {name: self.company, tel: self.tel}
  end

こんな感じで ActiveModel 内にメソッドを定義してあげれば、検証が通った後にそれぞれの ActiveRecord オブジェクトに渡して作成すれば良いのだ。そうすれば、複雑なフォームでも Rails のレールに乗ることができる。

ActiveModel に関しての記事はこちら。

www.bokukoko.info

ActiveRecrod のスコープ

これに関しては割と資料どおりかな。ともかく controller を肥大化させないために、そしてより美しくレールに乗るために、カスタムスコープを定義しよう。

特に Where 系や Order 系は、他のコントローラ内でも呼び出すことが多く、その度に定義するのは大変だ。かといって一つ一つの ActiveReocrd の Select 系の処理を Model 内に書いてしまえば、どんどん Model が肥大化してしまう。

まるでパズルのように カスタム Scope を定義してあげよう。それが美しい Rails コードにつながってくる。

Memory Bloat

Controller から View に渡す時に、 @users みたいな形で @で定義すれば渡すことができる。ただそれに甘えてしまって、何でもかんでも @ でViewに渡してしまうと、その分メモリを消費するので、遅くなるしメモリの消費が激しくなる。

View に渡す @ は必要なものだけ渡すようにしよう。

S3のダイレクトアップロード に関しても、まずは JavaScript の File APIでファイルの内容や容量を検証し、その後に S3 にダイレクトアップロードしよう。ここら辺の記事に関しては、このブログで紹介している。

www.bokukoko.info

効率的で、コードとしても美しいし、サーバーとしても美しいコードを書こう。

N + 1 クエリ

まず検証なんだけど、もう Scout を入れれば容易に発見できる。

Scout や Bullet で見つけた N + 1に対応するために、 ActiveRecord の preload, eager_load を使おうと言うことだ。他にも includes があるけど、これはうまい具合にやってくれるって感じなので、ちゃんと使い分けがわかっていれば preload, eager_load のどちらかを使うべきだと考えている。

ぜひ適切な ActiveRecord のクエリを書くようにしよう。

destroy 系の処理に関しては、対応の id だけを持っておいてバックグラウンドで処理させるか、AWS Lambda などで別で実行させるようにする。こうすれば delete_allが書けて N+1 を解決する削除系の処理を実現できる。

Rails のレールに乗らない選択は悪いことか?

んでここがブログ記事の特別編。Rails のレールに乗らない選択ってのが最近流行ってるけど、それに関して私の思うところがある。

一言で言えば、レールから外れることは全く悪くない。これは、まさにレールなのか砂利道なのかって話である。レールに乗れば遠くまで行けるけど人の敷いたレールまでしかたどり着けない。レールに乗り切った後にレールを外れて、より良い道を模索するってのは Rails を突き詰めていけばきっとあるはずだ。

ではなぜ私があえて今一度 Rails のレールに乗ることが大事だと強調しているのか。私が危惧しているのは、他の企業の事例を聞いて、レールから外れることが当たり前だと考え、本来はレールに乗るべきだった選択を見誤ってしまう初心者・中級者の方が多く見受けられる点があるためだ。Rails のレールに乗らない選択をするからにはそれなりの覚悟がいることなんだけど、そうじゃない雰囲気を最近は感じてしまうのである。

私は Rails のレールに乗って極限まで遠くまで行けた後に、自分だけの小道を切り開いていった方がいいと感じている。遠くまで行く前に自分の道を切り開いてしまうと、そこまでたどり着くことができないからだ。

終わりに

私だけ初心者向け Tip って感じだったけど、参考になった方が一人でもいたようでよかった。でも本来は LT はインスピレーションを与えるって目的が一番正しいような気がしている。その点が今回の個人的な反省点。

より良い発表テーマが見つかれば、また発表したいと思う。

Startup Rails は3回連続で参加しているので、これからも機会があれば参加したい。