Rubyの文法とRailsの仕組みを初めて勉強して、教材に書かれているサンプルコードをとりあえず理解して動くものが作れた(それを作ったというかはさておき)ときに書いたのが、Ruby on Railsを学ぶときに常に意識しておけばよかった10のことでした。
その後、オリジナルアプリを作って、エンジニアの方に質問したりフィードバックをもらったり、やっぱり上達したいなら写経なのかなあと黒田さんの「改訂3版基礎Ruby on Rails」を頭から全部読んでサンプルアプリを作りました。
そこでなんとなく見えてきたのが、とりあえず動くものを作れる状態と、リーダビリティや保守性、設計などを意識して作れる状態にはかなり乖離がありそうだと言うことです。
調べながらとか、実装できそうにない機能はドロップしながらなら、アプリを作れる初心者レベルから卒業するためにどうしていけばいいか、今分かっていることをまとめてみました。
Rubyの文法
メソッド
1つのメソッドに複数のロジックを書かずに別のメソッドに抽象化させる
1つのメソッドの中に複数のロジックを書くと、そのメソッドで何をやっているのか直感的に分かりづらくなります。よって、ロジックや操作ごとにメソッドを作って、それを呼び出す形にした方がコードが見やすくなりました。
例えば、if文で条件により操作を分岐させているときに、2つ以上の操作をそのメソッド内に直接書くのではなく、操作ごとにメソッドを作って、それを呼び出す形にするなどです。
定数
ロジックで用いる具体的な数字は定数に格納する
ロジックを作るときに、生の数字を書いてしまうと初見の人には伝わらないため、名前をつけた定数に格納することで、その数字が何を意味するのか分かるようにしました。
例えば、5回目の返信後に自動メールを送る、というロジックを書くときに、5という数字をそのままロジック内に書くのではなく、FIFTH_REPLY = 5
と定数に格納するようにしました。
Rails
Routing
ルーティングの書く順番だけではなくネストできるところはする
routes.rb
は上から順に読み込んでいくということを失念していて、適当に空いているところにルーティングを書いていたら、エラーがでたことがありました。
そのときに、このルーティングを優先して欲しいから上に書こう、というやり方ではなく、独自URLを設定しているルーティングはresources
にネストして書いた方が分かりやすくなりました。
Controller
CRUDと7つのアクションの関係
コントローラーは基本的にはCRUD(作成/表示/更新/削除)という4つの機能を担うとして、それとresourcesメソッドで用意される7つのアクションの関係がよく分かっていませんでしたが、下記のように整理した時にすっきりしました。
CRUD | ||
---|---|---|
Create | new 作成ページ | create 作成 |
Read | index 一覧表示 | show 詳細表示 |
Update | edit 編集ページ | update 更新 |
Destroy | delete 削除 |
7つのアクションは並列にあるというより、CRUDのいずれかに属していると考える。これを踏まえて、ルーティングのresources
にonly
やexpect
オプションを付けることで、どのリソースを使っているかを可視化する癖ができました。
クラスを継承してsuperメソッドを使う
devise
を使うと、ユーザーが必要事項を入力をしたら会員登録するというロジックがすでにDevise::RegistrationsController
にありますね。これを一部のユーザーが会員登録した場合は、登録せずにthankyouページにリダイレクトしたい場合にどうするか。
Devise::RegistrationsController
に分岐のロジックを書くのではなく、RegistrationsController < Devise::RegistrationsController
とクラスを継承させて、一部のユーザーはredirect_to thankyou_path
、それ以外の問題のないユーザーはsuper
メソッドでdevise
の元々のcreate
メソッドを呼び出すようにすると、すごく読みやすくなりました。
Model
最初は何でもロジックをcontrollerに書いていたのですが、ビジネスロジックはmodelに書こうということでそうします。すると、modelが肥大化します。そこでどうするか。
クラスをまたぐ処理はconcernに
複数のクラスで使いたい処理のまとまりはconcern化して、取り込むクラスにmix-inしました。例えばEmailのチェックのバリデーションや、料金プランなど。インスタンスが存在しなくて、処理のまとまりだけ書きたいものと考えると結構ありますね。
複数のmodelにまたがる処理はservice層に書く
modelとcontrollerの中間にあるのがserviceと言われていますが、service層を設けていいのかの判断基準に悩みます。notificationの処理などはservice層に移すということで意見にあまり相違ない気がします。
modelからデータを取り出す方法としてscopeを使う
modelから、一部の条件だけのデータを絞り込んで使いたいときはよくあります。例えば、有料会員のみとか、公開記事のみ、とかですね。その場合、where句を多用して条件を毎回書かなくても、scopeに定義すればメソッドのように使えるのでそうするようになりました。
View
viewの場合は、共通パーツ化するのと、viewにロジックを書かないようにするためにどうすればいいか、という観点です。
複数のviewで用いるパーツはレイアウトファイルや部分テンプレートに分ける
これはHTMLやCSSの経験があったら、なんとなくとっつきやすい考えですね。そんなに迷うことはないです。
Helperメソッドに定義する
viewにはなるべくロジックを書かないように、ヘルパーメソッドに定義します。例えば、ページ数によってtitleタグを動的にとる場合などですね。
Test
どのくらいテストコードを書いて、どの粒度でアサートすべきか
MiniTest
&FactoryGirl
でやりました。いろいろ考えたところ、その作っているアプリや機能で実現したいことがまずあって、それをメインのユースケースで実現できていることをまずはassert
するのがいいのかなと思います。
また、課金とかお金が関わるところは重点的にやりつつ、あとはどれだけ工数をかけれるかとの相談。
コードリーディング
複数人で作業していたり、すでにあるアプリの追加開発を行う場合は、他の人が書いたコードを読むことになります。このときに、どう読み解いていくのか。
整理すると、こんな風に分解していくといいのかなと思います。
- 文法(
def ~ end
、if ~ else ~ end
など)
これは基本的なRubyの文法を押さえて、どんな書き方か覚えているレベルまで持っていく必要がありそうです。
- 変数(
article
など)
ローカル変数とインスタンス変数のスコープで分からなくなることもありますが、だいたい変数は名詞なので分かります。
- メソッド(
new
、redirect_to
など)
Railsやその他Gemでデフォルトで定義されているメソッドなのか、自分たちで新規で定義したメソッドなのかの判断がつかない時は、毎回テキストエディターで全ファイルを検索します。Railsのソースコード自体を読むのはなかなか難易度が高い。。 メソッドはだいたい動詞なので分かります。
- modelクラス(
User
、Dialog
など)
そのアプリにあるmodelクラスやテーブルをざっとみて、こんなクラスがあるのかーと一度頭に入れた上でコードを読むとだいたい分かります。あとは、先頭が大文字ですね。
- カラム(
:name
など)
上記と同じく、テーブルのカラムをざっと見ておけば、だいたい分かります。あと、シンボルのことが多いです。
まとめ
実体験にもとづいて気づいたことをまとめたので、網羅性はないと思いますが、ざっとこんなところを意識して、ただ動けばいいから卒業していきたいなと思っています。