時々、クライアントは私たちが本当に気に入らない機能要求を私たちに与えます。私たちがクライアントを好きではないということではありません。私たちはクライアントを愛しています。この機能が気に入らないわけではありません。クライアントから要求された機能のほとんどは、ビジネスの目標と収入に完全に一致しています。
機能リクエストを解決する最も簡単な方法は悪いコードを書くことであり、頭のてっぺんに洗練されたソリューションがないため、機能リクエストが気に入らない場合があります。これは私たちの多くを送ります Rails開発者 無駄な検索で RubyToolbox 、 GitHub 、開発者ブログ、および スタックオーバーフロー 自分自身について気分を良くする宝石、プラグイン、またはサンプルコードを探しています。
セレンスクリプトのページオブジェクトモデル機能リクエストを解決する最も簡単な方法は悪いコードを書くことであるため、機能リクエストが気に入らない場合があります。 つぶやき
さて、私はあなたに言うためにここにいます:悪いコードを書いても大丈夫です。時々、悪いRailsコードは、時間の制約の下で実装されたよく考えられていないソリューションよりも、美しいコードにリファクタリングする方が簡単です。
これは、私の恐ろしいバンドエイドソリューションから問題をマッサージするときに私が従うのが好きなRailsリファクタリングプロセスです。
別の見方をするために、ここに Gitコミットログ ステップバイステップでリファクタリングされた機能の場合:
そして、これはについての別の興味深い記事です 大規模なリファクタリング ApeeScapeネットワークの同僚から。
それがどのように行われるか見てみましょう。
ステップ1。 ビューから開始します
新機能のチケットを開始するとします。クライアントは私たちに言います: 「訪問者は、ウェルカムページでアクティブなプロジェクトのリストを表示できる必要があります。」
このチケットには目に見える変更が必要なので、作業を開始するのに適切な場所はビューです。問題は単純で、私たち全員が何度も解決するように訓練されています。私はそれを解決するつもりです 間違った方法 ソリューションを適切な領域にリファクタリングする方法を示します。 問題の解決 間違った方法 適切な解決策がわからないというこぶを乗り越えるのに役立ちます。
まず、Project
というモデルがあると仮定します。 active
というブール属性を使用します。すべてのリストを取得したいProjects
ここでactive
はtrue
に等しいので、Project.where(active: true)
を使用して、each
でループすることができます。ブロック。
app/views/pages/welcome.haml: %ul.projects - Project.where(active: true).each do |project| %li.project= link_to project_path(project), project.name
私はあなたが言っていることを知っています:「それはコードレビューに合格することは決してないだろう」または「私のクライアントは確かにこれのために私を解雇するでしょう。」はい、このソリューションは、Model-View-Controllerの関心の分離を破り、追跡が困難なデータベース呼び出しが発生する可能性があり、将来的に維持が困難になる可能性があります。しかし、それを行うことの価値を考慮してください 間違った方法 :
ステップ2。 パーシャル
それをした後 間違った方法 、私は自分自身について気分が悪く、自分の悪いコードを分離したいと思っています。この変更が明らかにビューレイヤーの懸念事項にすぎない場合は、恥を部分的にリファクタリングすることができます。
app/views/pages/welcome.haml: = render :partial => 'shared/projects_list' app/views/shared/projects_list.haml: %ul.projects - Project.where(active: true).each do |project| %li.project= link_to project_path(project), project.name
それは少し良いです。明らかに、ビューでのモデルクエリの間違いをまだ犯していますが、少なくともメンテナが後でやって来て私の恐ろしい部分を見ると、特にRailsコードの問題に簡単に取り組むことができます。明らかに馬鹿げたものを修正することは、実装が不十分でバグのある抽象化を修正するよりも常に簡単です。
明らかに馬鹿げたものを修正することは、実装が不十分でバグのある抽象化を修正するよりも常に簡単です。 つぶやきステップ3。 ヘルパー
Railsのヘルパーはを作成する方法です DSL(ドメイン固有言語) ビューのセクション。代わりにcontent_tagを使用してコードを書き直す必要があります haml またはHTMLですが、他の開発者が15行の非印刷ビューコードを睨むことなくデータ構造を操作できるという利点があります。
ここでヘルパーを使用する場合は、おそらくli
をリファクタリングします。鬼ごっこ。
app/views/shared/projects_list.haml: %ul.projects - Project.where(active: true).each do |project| = project_list_item(project) app/helpers/projects_helper.rb: def project_list_item(project) content_tag(:li, :class => 'project') do link_to project_path(project), project.name end end
ステップ4。 それをコントローラーに移動します
ひどいコードは、ビューの問題だけではないかもしれません。それでもコードの匂いがする場合は、ビューからコントローラーに移行できるクエリを探してください。
app/views/shared/projects_list.haml: %ul.projects - @projects_list.each do |project| = project_list_item(project) app/controllers/pages_controller.rb: def welcome @projects = Project.where(active: true) end
ステップ5。 コントローラーフィルター
クレジットカード番号ビザマスターカードをハッキングする方法
コードをコントローラーに移動する最も明白な理由before_filter
またはafter_filter
複数のコントローラーアクションで複製したコード用です。コードをに移動することもできます コントローラーフィルター コントローラーアクションの目的をビューの要件から分離する場合。
app/controllers/pages_controller.rb: before_filter :projects_list def welcome end def projects_list @projects = Project.where(active:true) end
ステップ6。 アプリケーションコントローラー
すべてのページにコードを表示する必要がある場合、またはすべてのコントローラーでコントローラーヘルパー関数を使用できるようにする場合は、関数をApplicationControllerに移動できます。変更がグローバルである場合は、アプリケーションのレイアウトも変更することをお勧めします。
app/controllers/pages_controller.rb: def welcome end app/views/layouts/application.haml: %ul.projects - projects_list.each do |project| = project_list_item(project) app/controllers/application_controller.rb: before_filter :projects_list def projects_list @projects = Project.where(active: true) end
ステップ7。 モデルへのリファクタリング
MVCのモットーは次のとおりです。 ファットモデル、スキニーコントローラー、およびダムビュー 。可能な限りすべてをモデルにリファクタリングすることが期待されており、最も複雑な機能が最終的にモデルの関連付けとモデルメソッドになることは事実です。モデルでフォーマット/表示を行うことは常に避ける必要がありますが、データを他のタイプのデータに変換することは許可されています。この場合、モデルにリファクタリングするのに最適なのはwhere(active: true)
です。スコープに変換できる句。スコープを使用すると、呼び出しがより美しく見えるだけでなく、delete
のような属性を追加することにした場合に役立ちます。またはoutdated
、すべてのwhere
を探す代わりに、このスコープを変更できます条項。
app/controllers/application_controller.rb: before_filter :projects_list def projects_list @projects = Project.active end app/models/project.rb: scope :active, where(active: true)
ステップ8。 モデルフィルター
モデルのbefore_save
には特に用途はありません。またはafter_save filters
この場合ですが、私が通常行う次のステップは、機能をコントローラーとモデルメソッドからモデルフィルターに移動することです。
別の属性num_views
があるとします。 num_views > 50
の場合、プロジェクトは非アクティブになります。ビューでこの問題を解決することはできますが、ビューでデータベースを変更することは不適切です。コントローラーで解決できますが、コントローラーはできるだけ薄くする必要があります。モデルで簡単に解くことができます。
app/models/project.rb: before_save :deactivate_if_over_num_views def deactivate_if_over_num_views if num_views > 50 self.active = false fi end
注:self.save
の呼び出しは避けてくださいモデルフィルターでは、これにより再帰的な保存イベントが発生するため、アプリケーションのデータベース操作レイヤーはとにかくコントローラーである必要があります。
ステップ9。 ライブラリ
場合によっては、機能が十分に大きいため、独自のライブラリが必要になることがあります。多くの場所で再利用されているため、または個別に開発を行うのに十分な大きさであるため、ライブラリファイルに移動することをお勧めします。
ライブラリファイルをに保存しても問題ありません lib / ディレクトリですが、大きくなるにつれて、実際のディレクトリに転送できます RubyGem !コードをライブラリに移動する主な利点は、モデルとは別にライブラリをテストできることです。
とにかく、プロジェクトリストの場合、scope :active
を移動することを正当化できます。 Project
からの呼び出しモデルをライブラリファイルに変換し、Rubyに戻します。
app/models/project.rb: class Project 50 self.active = false end end end
注:self.included
Railsモデルクラスがロードされ、クラススコープに変数k
として渡されると、メソッドが呼び出されます。
このRubyon Railsリファクタリングチュートリアルでは、15分以内にソリューションを実装し、ユーザーテストのためにステージングして、機能セットに受け入れるか削除する準備をしました。リファクタリングプロセスの終わりまでに、最も厳しいレビュープロセスにも合格する、複数のモデルにわたってリスト可能でアクティブ化可能なアイテムを実装するためのフレームワークをレイアウトするコードが完成しました。
独自のRailsリファクタリングプロセスで、自信がある場合は、パイプラインの数ステップをスキップしてください(たとえば、ビューからコントローラー、またはコントローラーからモデルにジャンプします)。コードの流れはビューからモデルへであることに注意してください。
愚かに見えることを恐れないでください。現代語と古いCGIテンプレートレンダリングアプリケーションを区別するのは、私たちがすべてを行うということではありません 正しい方法 毎回、時間をかけてリファクタリング、再利用、取り組みの共有を行っています。
関連: タイムスタンプの切り捨て:Ruby on Rails ActiveRecord Tale