Ruby
Rails
Rails4

Rails初心者から卒業すべく意識し出したことをまとめてみた

More than 3 years have passed since last update.

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のいずれかに属していると考える。これを踏まえて、ルーティングのresourcesonlyexpectオプションを付けることで、どのリソースを使っているかを可視化する癖ができました。

クラスを継承して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

どのくらいテストコードを書いて、どの粒度でアサートすべきか

MiniTestFactoryGirlでやりました。いろいろ考えたところ、その作っているアプリや機能で実現したいことがまずあって、それをメインのユースケースで実現できていることをまずはassertするのがいいのかなと思います。

また、課金とかお金が関わるところは重点的にやりつつ、あとはどれだけ工数をかけれるかとの相談。


コードリーディング

複数人で作業していたり、すでにあるアプリの追加開発を行う場合は、他の人が書いたコードを読むことになります。このときに、どう読み解いていくのか。

整理すると、こんな風に分解していくといいのかなと思います。


  • 文法(def ~ endif ~ else ~ endなど)

これは基本的なRubyの文法を押さえて、どんな書き方か覚えているレベルまで持っていく必要がありそうです。


  • 変数(articleなど)

ローカル変数とインスタンス変数のスコープで分からなくなることもありますが、だいたい変数は名詞なので分かります。


  • メソッド(newredirect_toなど)

Railsやその他Gemでデフォルトで定義されているメソッドなのか、自分たちで新規で定義したメソッドなのかの判断がつかない時は、毎回テキストエディターで全ファイルを検索します。Railsのソースコード自体を読むのはなかなか難易度が高い。。 メソッドはだいたい動詞なので分かります。


  • modelクラス(UserDialogなど)

そのアプリにあるmodelクラスやテーブルをざっとみて、こんなクラスがあるのかーと一度頭に入れた上でコードを読むとだいたい分かります。あとは、先頭が大文字ですね。


  • カラム(:nameなど)

上記と同じく、テーブルのカラムをざっと見ておけば、だいたい分かります。あと、シンボルのことが多いです。


まとめ

実体験にもとづいて気づいたことをまとめたので、網羅性はないと思いますが、ざっとこんなところを意識して、ただ動けばいいから卒業していきたいなと思っています。