Ruby on Rails 5 が出てから初めて Railsを触り、学ぶ過程で「知って良かった」と思うことをまとめました。
(ちなみに僕は20年前くらいにコードを書いていたことはありますが、長く事業系にいたのでコードを書く行為そのものをキャッチアップしながらRailsを学びました。その過程で感じたことです。)
#1. Railsは、Rail に乗るように理解するとスゲー楽に学習できる
基本的な設計思想を理解すると、とても楽になります。
Railsガイドは一見すると意味がわかりませんが、課題にぶち当たったときに読み解くと大変参考になります。
https://railsguides.jp/
特に「Rails をはじめよう」のページは、見出しだけで意味がわかるようになるまで、関連知識を先に調べた方が入りやすいと思います。
Railsを始めるにあたり、必須で理解しなければならないのは2点です。
- REST
- CRUDの実装に使われている
- 作成 (create)
- 読み出し (read)
- 更新 (update)
- 削除 (destroy)
- Controller の中で定義される action を理解するためにとても大事
- routes は REST の概念を用いて CRUD を実現するために、 controller#action を関連づけている
- CRUDの実装に使われている
- MVC
- モデル・ビュー・コントローラの略
- Railsの app フォルダの中には、models, views, controllers というディレクトリが存在し、とても大事
この2つの概念に沿って、「名前」で結びついているのがRailsです。
例えばユーザー情報を "User" という名前で定義したら、routes でも Model でも Contoller でも View でも、一貫して "User" という名称で関連づけされます。
Helper とか Concern とか 色々あるのは置いといて、まずは上記2点をきちんと理解すると、アプリケーションの構築設計がとても楽(あまり迷わない)になりました。
#2. Rails の app の中にフォルダを作ってもいい
逆説的になりますが、MVC に従おう(Railに乗ろう)とするほど、フォルダを勝手に作っていいのか悩みました。
でもこれは、勝手に作っていいんだそうです。配置したrbファイルは再帰的にロードされます。
以下の二つは役に立ちました。実際に Decoratorを取り入れて、ActiveDecorator を使って app/decorators に配置しています。
- 早く知ってたら良かったrailsの技
- Railsで導入してよかったデザインパターンと各クラスの役割について
MVCを拡張して他のデザインパターン(設計するフレームワークのこと)を取り入れることができるので、Rail に乗っかりながら「補助輪(例えです)」をつけることができます。
#3. Rails 特有の拡張機能は知っておくべき
Ruby のメソッドとは別に、Railsで拡張されたメソッドがたくさんあります。いわゆる便利機能です。
拡張機能はRailsガイドの「Active Support コア拡張機能」に記載があります。Qiita にも解説がたくさんあります。
中でも数値に .hours などをつけて日付演算できるのは、分かりやすくて感動しました。
now - 3.hours
- Active Support コア拡張機能
#4. ActiveRecord のデータの取得方法は「必要な時に取りに行く」方式になっている
ある程度のデータ量を扱い始めると、log に吐き出されるSQLの回数が気になり始めます。
Ruby on Rails では、データの取り扱いは Active Record と呼ばれる仕組みを使いますが、要はデータベースからデータを取ってきたり保存したりするのを楽チンにする方法です。
楽なのですが、いくつか知っておくと良いことがあります。
リレーションを管理しているだけでデータを取ってきている訳ではないことも、その一つです。データは必要になったタイミングで、都度、取りに行きます。そのためSQLが都度発行されます。
Railsガイドの「N + 1クエリ問題を解決する」ための includes 指定は早めに知っておいて損はないでしょう。なぜこの問題が生じるのかを理解すると、Rail に乗りやすくなります。
また同時に、scope という概念があることは、知識として知っておくと良いでしょう。複雑な条件を組み合わせる場合でも、問題を生じないよう、うまく書くことができるはずです。
- Active Record クエリインターフェイス
#5. Model の関連とセキュリティの考え方
ActiveRecord は優秀なので、データを簡単に取得できます。一方で、そのお作法(Rail)に従っているために、うまく出来ていると勘違いしやすい部分でもあります。
例えば記事を書くメディアサイトがあるとしましょう。 Article モデルを扱うControllerで、新たにユーザー情報を取得するのに User.find を使うとか、安直にやりそうになります。
Ruby on Railsでは、幸いにも、セキュリティ面で考えることが少なくなるよう工夫がされています。
それは例えば、充実した Gem の中からログイン処理を任せるgemを選ぶことが出来たり、クエリパラメータを管理する Strong Parameters などが実装されていることが挙げられます。
しかしそれは、セキュリティ面のことを考えなくていいことにはなりません。
Rails セキュリティガイドでは、Railに乗っていても考えるべきことがまとまっています。
特に、「6.7 権限昇格」の話は、controller にどう記述するかに依るため、きちんと知っておくべきだと思います。同じことを実現する方法が2つあるときに、「どちらを使うべきか」が判断できるようになり、「それはなぜか」が理解できるようになりました。
Rails セキュリティガイドの例では、
A:
@project = Project.find(params[:id])
と
B:
@project = @current_user.projects.find(params[:id])
の例が載っています。
もし権限を必要とする場合は、「Aに権限チェックを加える」と考えることもできますが、Bで考えられる頭になっている方が、Rails的に一貫しています。次のようにif文で権限チェックするよりも確実に、Railに乗っていられます。
「Aに権限チェックを加える」という発想で書いちゃいそうな記述:
@project = Project.find(params[:id])
redirect_to root_path if @current_user.id != @project.user_id
- Rails セキュリティガイド
#6. routes の namespace を controllers とセットで設計する
「Rails のルーティング」の「2.6 コントローラの名前空間とルーティング」の段落では、 namespace を使った例が書いてあります。例として、 /admin を分けたいことはあるよね、という話です。
ただ、この説明だけでは設計に落とし込むことはできませんでした。 routing だけではなくcontrollerとセットで考える必要があります。routes に namespace で admin を設けて contoller を分けたいのは、ログイン処理が異なるからです。
それは、もしかすると、同時に「表示側で編集できなくしたい」ということかも知れません。
そのとき、本当は routes で分けた admin には CRUD を可能にするが、 R(読み込み)だけを可能にする public 空間も同時に設けたいかも知れません。 routes と controller のフィルターで権限を制御することができると、権限管理周りはスッキリするのではないかと思います。
つまり、 routes で名前空間を分けた方がいいのは、URLの階層定義以上に、「controller でどういう制御をかけたいのか」に密接に繋がっていると知っておくのが良いと思います。
- Rails のルーティング
#7. 「コメント」は色んなところに付けるよね。それ、ポリモーフィック関連付けと言う
Rails ガイドの「Active Record の関連付け」では、「2.9 ポリモーフィック関連付け」で説明されています。 polymorphic 指定で使えるようになるのですが、説明で見ると抽象的で難しいです。
分かりやすいのは、「コメント」や「メッセージ」です。同じものがあちこちで使われる場合に役立つかも、と知っておくと良かったです。
ポリモーフィックの詳細は 「Railsのポリモーフィック関連とはなんなのか」が詳しいです。
ポリモーフィック関連付けを作らずに実装することが悪いわけではありません(突き詰めればSTIになるんでしょう)。ポリモーフィックはRailsでカバーされているように見えて、実装者依存を生みかねないものだと思います。ただ、きちんと知っておくととても便利で、分かっていればRailに乗れます。
- Active Record の関連付け (アソシエーション)
- Railsのポリモーフィック関連とはなんなのか
#8. Layout は Controllerとセットなので、画面切り替え楽チンじゃん
Ruby on Rails のViewは、Controller とセットです。もっとちゃんと言うと、Controller#action とセットです。
一方、Viewの一部である layout も Controller とセットです。なぜlayoutが分かれているのか最初はわかりませんでしたが、namespaceごと、もしくは Controller ごとに layout を割り当てることはできるので、管理画面を表示画面と異なるデザインにしたい場合に利用できます。
layoutが適用される順番はRailsガイドの「レイアウトとレンダリング」、「2.2.13 レイアウトの探索順序」に記載があります。指定をすれば、アクションごとにlayoutを変えることもできます。
小さな差異のためにLayoutを増やすのは問題ですが、 MVC モデルの Viewには、 app/views/layout が存在していて、そのlayout内のファイルは増やしてよくて、それもRailsが管理できると知ったことは良かったです。
共通テンプレートのなかで分岐を増やす前に、Controller レベルで分けられることを知っておくと、Railに乗りやすくなります。
- レイアウトとレンダリング
#9. migrate ファイルは時系列で増えていくものだ
rails generate scaffold を用いると、必要な初期テンプレートが生成されます。これはとても便利ですが、生成時に全て完璧に定義できたModelはありませんでした。
そのため、作成した後にカラムを追加したくなって migrate ファイルを増やすことになります。
最初は自動生成されたmigrateファイルを直接書き換えることに抵抗がありました。変更の度に rails g コマンドを叩いた方がいいのかも?と思ったこともあります。
しかしまだ本番環境に適用していないのなら、migrate ファイルの増加を抑える方法があります。一旦 rake db:migrate を実行した後にrollback してやり直す方法です。
development の段階では、その時点で完成されたものを作れるように rollback しながら開発することが大事になります。 そしてそのrollbackはよくあることだと理解しておくのが良いようです。
rake db:rollback
ちなみに、あまり起こることではありませんが、他人の絶賛開発進行中の作業branchをもらってきて migrate した場合には、そのブランチの作業者が次のコミットでmigrateファイルを整理して消してしまった場合、自分の migrate が壊れることがあります。
そうした時、status を表示することで、migrate の状態を知ることができます。up, down のステータスは、それぞれ適用されているか否かとなります。
rake db:migrate:status
Rails で migrate を実行すると、基本的に過去の状態を変更することはできません。したがって継続的にサービス改善を行うと、 db/migrate の中はすごい数のファイルが出来上がることになります。しかしそれは、時系列的な変更を積み上げることによってRailsがDB構造を適切に管理していることと同義です。
migrateファイルが一方的に増えるのがいいのかどうか分からなくても、それはそういうものだと思いましょう。とはいえ、必要なものを慎重に検討し、無駄なmigrateではないことを確認しながら進めるのが良いのだと知っておくと良いと思います。
- Active Record マイグレーション
- railsのrakeで作成したmigrationファイルとstatus履歴を削除する
10. 「どこに書くべきか?」迷ったら原点に戻る
rails generate で生成されたテンプレートから脱し、自分で考えたコードを書き始める頃、「User一覧を処理するコードは、Modelに書くべきか?Controllerに書くべきか?」のような悩みが生じてきます。
都度解決するのも大事ですが、迷ったら原点に戻って考えるようにしていることで、どのようにRailが敷かれているのか理解していけたと思います。
例えば「User一覧を処理するコードは、Modelに書くべきか?Controllerに書くべきか?」という疑問には、それがどのような経緯で処理するものなのかを考えることが大切だと思います。
Controllerはroutesを通して外部からのアクセスを処理します。つまり、外部からのアクセス要求に応じるユーザー一覧なら controllerに書くのだろうと考えられます。
特定の記事を「お気に入り」したユーザー一覧なら @articles.favorite_users になるでしょう。それはviewにいきなり現れるかも知れません。
Controllerで扱うわけではない、全ユーザーを対象にした処理であれば、Modelに、selfを使ってクラスメソッドを定義するべきかもしれません。
いずれの場合も、Railsには、ある1つの最善な方法があるはずだと知っていたことが良かったです。
Railsには、基本理念があり、Rails をはじめよう にも記載があります。それは次の2つです。
- 同じことを繰り返すな (Don't Repeat Yourself: DRY)
- 設定より規約が優先される (Convention Over Configuration)
これらを踏まえて、MVC/CRUDに沿って、どこに書くべきか考えていくことは、だんだんとRailsの考え方を理解することに繋がるでしょう。
つまりRailsがいう、
Railsは、最善の開発方法というものを1つに定めるという、ある意味大胆な判断に基いて設計されています。Railsは、何かをなすうえで最善の方法というものが1つだけあると仮定し、それに沿った開発を全面的に支援します。
という原点に戻り、その1つの方法へ戻ることが、もっとも最良の方法だと知っておくことが、いい結果につながるのだと思います。