Edited at

他言語経験者がRailsの案件にジョインしたときに、何を足掛かりにすべきか


はじめに

 この記事はOmotesando.rb #50で、参加者から質問を募集して、LTを行うという企画の中で、未経験者・初学者からのRailsプロジェクトに参画する際の質問が多かったことから、「実際に案件に参画するときにどのようなキャッチアップをしていけばよいか」という観点で話してみようと思い立ち、作成しました。


対象読者・想定プロジェクト

 Ruby/Railsのプロジェクトに参画したことのない、Ruby/Railsプロジェクト未経験なWeb技術者を対象としています。また、Ruby/Railsの知識については、Railsチュートリアルは一通り目を通しているという前提で書いています。また、想定しているプロジェクトとしては、既に案件はスタートしており、gitリポジトリの整備、環境等の整備が一通り終わっており、人の受け入れ態勢が整っているプロジェクトであることを想定しています。

 最初の関門である、環境構築の部分から書こうと思ったのですが、ここはプロジェクトによって様々なケースがあり、ボリュームが大きくなりそうだったので、今回の記事の中ではスキップして、環境構築は完了したという前提で話を勧めます。


全体を把握する

 プロジェクトに初めて入った時に、最初にやるべきことは全体の把握です。というのも、現実のRailsプロジェクトはRailsチュートリアルのようにシンプルではありません。むしろ基本に忠実に作られていることの方が稀です。いくつかのRuby/Railsプロジェクトに参画した経験があれば、どの程度しっかり作られているのかの勘が利くのですが、Rails未経験者・初学者の場合、そのあたりの感覚がよく分からないため、まず関わるプロジェクトがどの程度基本を押さえて作られているのかを把握しておく必要があります。

 全体を把握するのに、まず以下を見ておくとよいでしょう。


  • Gemfile / Gemfile.lock

  • routes.rb

  • app以下、lib以下

  • config/initializers以下

  • spec、もしくはtest以下


Gemfile / Gemfile.lock

 Railsでは多数のGem(ライブラリ)が使われていて、中規模ぐらいのRailsプロジェクトになってくると、依存するGemも含めて100以上のGemが使われていることも珍しくはありません。Railsチュートリアルでは、極力Gemを使わない構成になっているので、あまり触れることはありませんが、現実のプロジェクトではGemが大量に導入されています。

 ライブラリによってはRailsの挙動を大きく変更するものがあったり、プロジェクトのルールとしてGem特有の記法を強制するものもあるので、まずどのようなGemが使われているのかを把握しておく必要があります。

 Gemfileの中のgemを調べるときはrubygems.orgから、調べたいGem名を入力して、詳細画面からgithubやHomepage等のサイトに飛んで、README.md等のドキュメントを参照します。初学者のうちは分からないGemが多いと思いますので、調べるのも大変でしょうが、経験が蓄積されてくると全体の作りやプロジェクトのルールなども見えてくるので、ざっくりと概要をつかんでおくとよいと思います。

 以下は私がプロジェクトにジョインしたときにあったら注意すべきGemと対応例の一例です。

gem
対応例

devise
認証処理がある。ユーザ名・パスワードの管理のテーブルにUsers等が使われていると考えられるので、app/models以下からdeviseの文字列を検索する

omniauth-xxxx
外部認証連携が存在する。config/以下に認証連携用の設定が書き込まれていると考えておく。

acts_as_paranoid
フラグを立ててデータを消したことにする論理削除と呼ばれる概念がある。対象のモデルは強制的にdeleted_at is not nullのクエリが付加されるため、app/models以下からacts_as_paranoidの文字列を検索し、対象となるモデルを把握しておく。

paranoia
同上

ruby-grape
ActionControllerではないAPIエンドポイントを持っている。app/以下からGrape::APIを検索して、場所を把握しておく。また、エンドポイントはroutes.rbに書かれていることが多いため、合わせて参照しておく。

sidekiq
実際のURLリクエストとは別タスクとして実行される非同期処理がある。perform / perform_laterなどの文字列を検索し、非同期処理の内容自体やトリガー部分を確認しておく。

resque
同上

sequel
DB検索にActiveRecordを使っていない可能性がある。プロジェクトのルールを確認する。

composite_primary_keys
データベースの主キーがidではない可能性がある。app/models以下でprimary_keyで検索し、各テーブルの主キーを確認する。

sassc-rails
CSSの代わりにassets/stylesheetsにSASSが使用されている可能性がある。

haml-rails
ERBの代わりにhamlが採用されている。hamlの記法を用いてViewを書く必要がある。

slim-rails
ERBの代わりにslimが採用されている。slimの記法を用いてViewを書く必要がある。

simple_form
Form Objectと呼ばれる概念が導入されている可能性がある。

 一つ一つ詳細に見ている時間がないという場合には、そのGemがどういう用途で導入されているかぐらいは確認しておきましょう。

 The ruby toolboxというサイトで、Gem名で検索するとGemのカテゴリが分かるので、大まかに使用用途を推測することができます。


routes.rb

 RailsではRESTful URLでリソースベースのアクセスになるようにURL設計をしていくのが一般的ですが、現実のプロジェクトがしがらみなくRailsの推奨される構成でURL設計されているかというと、そういうわけではありません。例えば他のプロジェクトから移行するようなリプレース案件では、以前のURLと整合性がとれるようにURLをマッピングしなおさなければいけませんし、Railsを良く知らない設計者の趣味ですべてのURLをPOSTで記述しなければならないというトンデモルールが強制されている可能性すらあります。routes.rbを読んで、まずRails一般的なURL設計になっているかどうかを理解しておく必要があります。

 Rails GuidesのRailsルーティングを熟読し、URL定義のパターンを理解しておきましょう。普通にget/postなどのメソッドで個別に定義する表現、matchメソッドでパターンマッチさせる表現、resourcesを使った表現や、複数のresourcesメソッドをネスト構造にしたnested resourcesやnamespaceと組み合わせた表現など、色々あります。

 routes.rbを読む前にrake routesを叩いて、エンドポイントをざっくりと俯瞰しておきましょう。


app以下、lib以下


app以下

 app以下にはassets, controllers, models, views, helpers, mailersがRailsの標準のディレクトリとして作成されています。これらのディレクトリの中身については、後で詳しく確認するとして、その他にディレクトリが存在しないかを確認しましょう。例えばservicesというディレクトリが存在していたら、そのプロジェクトではServiceクラスという独自のレイヤが存在する可能性がありますし、formsというディレクトリが存在していたら、そのプロジェクトではForm Objectという独自のレイヤが存在する可能性があります。

 いずれもmodelクラスやcontrollerクラスが混沌としないように、プロジェクトの誰かが導入したものですので、これらの独自のレイヤをどのようなケースで使うべきなのかについては、導入した人物がまだ在籍しているのであれば直接質問する。そうでなければ、既存のコードから、どのようなメソッドが定義されていて、どのようなケースで、どこから呼ばれるのか、については把握しておく必要があります。

 ServiceクラスもForm Objectも賛否両論あるものですが、プロジェクトの中できちんとルールが統一されているのであれば、導入自体はあまり問題ではありません。問題となるケースとしては責務が曖昧である、もしくは使用用途を逸脱した神クラスになっている場合や、導入が中途半端であるものは使っていて、あるものは使っていないなどの混在が存在する場合です。そのような可能性も含めて、プロジェクトとして使うべきなのか・使わないべきなのかの意思統一をしておく必要があります。

参考文献

Railsで重要なパターンpart1:Sercice Object(翻訳)

Rails: Form Objectと#to_modelを使ってバリデーションをモデルから分離する(翻訳)


lib以下

 lib以下は古くからGemにするまでもないようなプロジェクトの内部だけで完結するプライベートなファイルの置き場として利用されています。よく見る例としてはomniauthの独自strategyのファイルの置き場や、隠れオレオレライブラリ、オープンクラスで既存の振る舞いを書き換えたモジュールなどが配置されていることがあります。プロジェクトのファイルはapp以下に並ぶので、見落としがちなのですがlib以下にファイルがある場合は、そのファイルがどこでrequireされていて、何をしているのかは簡単に把握しておくとよいでしょう。コードが分からなくても、git log / git blameや、チケットから目的が理解できる場合もあります。


config/initializers以下

 config/initializers以下はRailsが起動するプロセスの中で呼び出されます。起動プロセスの詳細についてはRailsガイドのRailsの初期化プロセスに詳しく載っているので、そちらを参照してください。ここもlib以下と同様に、app以外でプロジェクトのファイルが配置される可能性があるため見落としがちなのですが、Gemの初期化処理や、lib以下に置いたファイルのrequire処理など、アプリケーションの重要な手掛かりとなるコードが置かれていることがあります。


spec(もしくはtest以下)

 参画しているプロジェクトにテストコードが十分に書かれていれば、キャッチアップを早める足がかりとなります。テストコードがどの程度書かれているかについては、rake statsで、本体コードとテストコードのコード行数の比率を見てみましょう。あくまで個人的な目安ですが、本体コードとテストコードが同程度書かれていれば、正常系程度の最低限のテストコードが用意されていると見込める。本体コードの2~3倍程度テストコードがあれば、十分な分量のテストコードが用意されていると判断しても良いでしょう。

 注意点としては過去にはテストコードが用意されていたが、メンテナンスする文化がなくなり、現在はテストコードはFailするものばかりで役に立っていないようなケースもあるため、まずはテストコードを手元で実行して、全部Passするかの確認はしておきましょう。大抵のRailsプロジェクトはbundle exec rake specbundle exec rspec spec/**/*_spec.rbで動くようになっていますが、特殊な初期化が必要なために起動は別のコマンドで行っていたり、巨大なプロジェクトではRSpecが多すぎるため、分割して実行しているような場合もあります。プロジェクトでどのようにテストコードを実行するかの手順は確認しておきましょう。


app以下を詳細に確認していく

 全体を軽く見通した後は、app以下の個別のファイルについて目を通していきます。

 個別のファイルについて詳細に目を通すのは実作業に取り掛かる段にして、先に確認しておくのは以下のようなことです。


app/controllers以下


  • application_controller.rbに定義されている共通のコールバック

  • application_controller.rbにMix-inされている各種モジュールの意味

  • app/controllers/concernsディレクトリ以下にあるモジュール群

  • controllers以下の各コントローラに定義されているコールバック

  • controllers以下の各コントローラにMix-inされているモジュール


app/models以下


  • application_record.rbに定義されている共通のコールバック

  • application_record.rbにMix-inされている各種モジュールの意味

  • app/models/concernsディレクトリ以下にあるモジュール群

  • models以下の各モデルに定義されているコールバック

  • models以下の各モデルにMix-inされているモジュール

  • models以下の各のhas_one / has_many / belongs_toの対応関係

  • 各種バリデーションの充足度

application_controller.rbやapplication_record.rbに定義されているコールバックはコントローラ・モデルに影響するため、個別のモデルだけを見ていても分からないような振る舞いをすることがあります。またRubyではMix-inと呼ばれる方法でinclude / extend / prepend等でモジュールが差し込まれることがあり、これがapplication_controller.rbやapplication_record.rbに入っていると、すべてのクラスで影響を受けるようになっています。

モジュールはGemによって利用可能なものだったり、concerns以下のディレクトリに配置されたものを呼び出したりするものが一般的なので、concernsディレクトリを見ておけば振る舞いを変えるようなモジュールを先に見つけておくことができるでしょう。

application_record.rbについては、Rails5以降で導入された概念なので、もしかするとActiveRecord::Base.include AwesomeModuleのような形でモジュールがincludeされているケースもあります。このような記述をしているときは、大抵イレギュラーな処理が挟み込まれているので、includeされているモジュールが何者なのかは把握しておけると良いです。

コールバックについてはRailsチュートリアルにもあるので、細かい説明は不要かと思いますが、より詳細に知りたい場合はRailsガイドのActiveRecordコールバックに目を通しておきましょう。

バリデーションについては、プロジェクトによっては全く書かれていなかったり、とても厳密に記述していたりと記述がまちまちなので、参画するプロジェクトがどの程度しっかりと書かれているのかを見ておきましょう。ベースとなる設計書や、バリデーションのガイドラインがプロジェクトとして用意されているのであれば、当然それに従うのが良いと思います。


app/views以下

 views以下については、コントローラとメソッドの対応が、ディレクトリとファイル名の関係になっていることが多いので、コントローラから追っていけば対応関係は大体把握できるので、特に留意点もないのですが、layoutsファイルにapplication.html.erb以外のファイルがある場合は、controller側からlayoutメソッドを呼び出している箇所を検索しておくとよいです。views以下で使われているテンプレートエンジンはプロジェクトによってerbだったり、slimだったり、hamlだったり、jbuilderだったり、rablだったりしますが、記述の仕方が違うだけで、最終的にはhtmlやjsonファイルに変換されるという点では本質的には同じことなので、記述方法は公式ドキュメントで学びましょう。


app/helpers以下

 ActionView内でレシーバなしで呼び出されているメソッドがあれば、Gemのメソッド、もしくはapp/helpers以下に定義されている可能性が高いです。application_helper.rbにまとめて書かれていて、他のモジュールは空という場合もあります。Railsではconfig.action_controller.include_all_helpersを明示的にfalseにしない限り、どこのhelperに記述しても結果は同じということもあり、どのhelperに書くかはプロジェクト次第なところがあります。まずはhelpers以下のファイルをざっと読んでみて、どういう指針で定義されているかは見ておきましょう。


app/assets以下、もしくはapp/javascript以下

 まだ多くのRailsプロジェクトはapp/assets以下で管理されていると思いますが、最近はWebpackerなどを導入するプロジェクトもあり、app/javascript以下にJavascriptが配置されていることもあります。ビルド時に何か問題が発生したときに、sprocketsなのかwebpackなのかは知っておく必要があります。


まとめ

 以上、Railsのプロジェクトに参画しておくときに見ておくとよいものを、簡単にまとめてみました。

 ここまで一通り読んでおくと、エンドポイントから機能をたどる時は、routes.rb → app/controllers/ → app/models → app/viewsと辿って、該当の機能を追うことができますし、新しい機能を追加するときも、周りの記述方針と合わせることができます。Railsに慣れないうちは、残念ながら目の前にあるコードが全てだと思うので、良し悪しを判断することは難しいと思いますが、まずは全体的に一貫性のある、統一感のあるコードが書けるようになることから始めましょう。