apeescape2.com
  • メイン
  • 人とチーム
  • 革新
  • 仕事の未来
  • プロジェクト管理
バックエンド

プレーンな古いRubyオブジェクトでエレガントなRailsコンポーネントを構築する

あなたのウェブサイトは前進していて、あなたは急速に成長しています。 Ruby / Railsは最良のプログラミングオプションです。あなたのチームはより大きく、あなたはすでに「ファットモデル、シンドライバー」という概念を残しています( ファットモデル、スキニーコントローラー )Railsアプリケーションのデザインスタイルとして。ただし、それでもRailsの使用をやめたくはありません。

問題ない。今日は、OOPのベストプラクティスを使用して、コードをよりクリーンで、より分離され、分離する方法について説明します。

アプリケーションをリファクタリングする価値はありますか?

まず、アプリケーションがリファクタリングに適しているかどうかを判断する方法を見てみましょう。



これは、コードのリファクタリングが必要かどうかを判断するために私が通常自分自身に尋ねるメトリックと質問のリストです。

  • 遅いテストユニット。 POROテストユニットは通常、十分に分離されたコードで高速に実行されるため、実行速度の遅いテストは、設計が不十分であるか、責任が過剰に結合していることを示していることがよくあります。
  • FATモデルまたはコントローラー。 200を超えるモデルまたはコントローラー コード行( 場所 ) これは一般的にリファクタリングの良い候補です。
  • 過度に長いコードベース。 30,000を超えるERB / HTML / HAMLをお持ちの場合 場所 またはRubyソースコード(なし GEM )50,000以上 場所 、リファクタリングが必要になる可能性が高いです。

次のようなものを使用して、Rubyソースコードの行数を確認してください。

find app -iname '*.rb' -type f -exec cat {} ;| wc -l

このコマンドは、/ appフォルダー内の拡張子が.rbのすべてのファイル(rubyファイル)を検索し、行数を出力します。コメント行はこの合計に含まれるため、この数値は推定値にすぎないことに注意してください。

もう1つのより正確で有益なオプションは、タスクを使用することです レーキ stats Railsは、コード行、クラス数、メソッド数、メソッドとクラスの比率、およびメソッドごとのコード行の比率の簡単な要約を公開します。

*bundle exec rake stats* +----------------------+-------+-----+-------+---------+-----+-------+ | Nombre | Líneas | LOC | Clase | Método | M/C | LOC/M | +----------------------+-------+-----+-------+---------+-----+-------+ | Controladores | 195 | 153 | 6 | 18 | 3 | 6 | | Helpers | 14 | 13 | 0 | 2 | 0 | 4 | | Modelos | 120 | 84 | 5 | 12 | 2 | 5 | | Mailers | 0 | 0 | 0 | 0 | 0 | 0 | | Javascripts | 45 | 12 | 0 | 3 | 0 | 2 | | Bibliotecas | 0 | 0 | 0 | 0 | 0 | 0 | | Controlador specs | 106 | 75 | 0 | 0 | 0 | 0 | | Helper specs | 15 | 4 | 0 | 0 | 0 | 0 | | Modelo specs | 238 | 182 | 0 | 0 | 0 | 0 | | Petición specs | 699 | 489 | 0 | 14 | 0 | 32 | | Routing specs | 35 | 26 | 0 | 0 | 0 | 0 | | Vista specs | 5 | 4 | 0 | 0 | 0 | 0 | +----------------------+-------+-----+-------+---------+-----+-------+ | Total | 1472 |1042 | 11 | 49 | 4 | 19 | +----------------------+-------+-----+-------+---------+-----+-------+ Código LOC: 262 Prueba LOC: 780 Ratio Código a Prueba: 1:3.0
  • コードベースで繰り返しパターンを抽出できますか?

動作中の分離

実際の例から始めましょう。

ジョギングする人々の天気を追跡するアプリケーションを作成するとします。メインページで、ユーザーは入力された時間を見ることができます。

各時間エントリには、日付、距離、期間、および追加の関連ステータス情報(天気、地形タイプなど)、および必要に応じて計算できる平均速度があります。

週あたりの平均速度と距離を示すレポートが必要です。入口の平均速度が合計の平均速度よりも速い場合は、SMSでユーザーに通知します(この例では、 Nexmo RESTful API SMSを送信します)。

メインページでは、次のようなエントリを作成するために、ジョギングしている距離、日付、時刻を選択できます。

estadísticasページもあります。これは基本的に週ごとのレポートで、1週間あたりの平均速度と距離が含まれています。

  • あなたはオンラインでサンプルを見ることができます ここに 。

コード

aplicaciónのディレクトリ構造これに似ています:

⇒ tree . ├── assets │ └── ... ├── controllers │ ├── application_controller.rb │ ├── entries_controller.rb │ └── statistics_controller.rb ├── helpers │ ├── application_helper.rb │ ├── entries_helper.rb │ └── statistics_helper.rb ├── mailers ├── models │ ├── entry.rb │ └── user.rb └── views ├── devise │ └── ... ├── entries │ ├── _entry.html.erb │ ├── _form.html.erb │ └── index.html.erb ├── layouts │ └── application.html.erb └── statistics └── index.html.erb

モデルについては説明しませんUsuarioそれは異常なことではないので、私たちはそれを使っているので モットー 認証を実装します。

Entradaモデルに関しては、アプリケーションのビジネスロジックが含まれています。

それぞれEntrada Usuarioに属します。

入力ごとに次の属性の存在を検証しますdistancia、períodode_tiempo、fecha_horaおよびestatus。

エントリを作成するたびに、ユーザーの平均速度をシステム内のすべてのユーザーの平均と比較し、SMSを介してユーザーに通知します。 Nexmo (外部ライブラリを使用する場合を示したかったのですが、Nexmoライブラリの使用方法については説明しません)。

  • 要旨サンプル

Entradaに注意してくださいモデルには、ビジネスロジックだけではありません。また、いくつかの検証と呼び出しも処理します。

entries_controller.rb株式を持っています クルーエル メイン(更新なしですが)。 EntriesController#index現在のユーザーのエントリを取得し、作成日でレコードを並べ替えます。EntriesController#create新しいエントリを作成します。 EntriesController#destroyの明白なまたは責任について議論する必要はありません。 :

  • 要旨サンプル

ながらstatistics_controller.rb週次レポートの計算を担当しますStatisticsController#indexログインしたユーザーのエントリを取得し、#group_byを使用して週ごとにグループ化します。クラスにあります 列挙可能 Railsで。次に、いくつかのプライベートメソッドを使用して結果を装飾してみてください。

  • 要旨サンプル

ソースコードは自明であるため、ここではビューについてあまり説明しません。

以下は、ログインしたユーザー(index.html.erb)のエントリを一覧表示するビューです。これは、入力ハンドラーでインデックスアクション(メソッド)の結果を表示するために使用されるパターンです。

  • 要旨サンプル

render @entriesを使用していることに注意してくださいパーシャル、共有コードをパーシャルパターンにする_entry.html.erbコードを保持できるように ドライ と再利用可能:

  • 要旨サンプル

同じことが_formaにも当てはまります。部分的。 (新規および編集された)アクションで同じコードを使用する代わりに、再利用可能な部分フォームを作成します。

  • 要旨サンプル

週報ページの見方についてstatistics/index.html.erbいくつかの統計を示し、いくつかのエントリをグループ化することにより、ユーザーの毎週のアクティビティについてレポートします。

  • 要旨サンプル

そして最後に、 ヘルパー 入力の場合、entries_helper.rb、2つ含まれます ヘルパー readable_time_periodおよびreadable_speedこれにより、属性が読みやすくなります。

  • 要旨サンプル

これまでのところ、それほど複雑なことはありません。

あなたのほとんどは、これをリファクタリングすることは原則に反していると主張することができます 接吻 そしてそれはシステムをより複雑にするでしょう。

では、このアプリは本当にリファクタリングする必要がありますか?

絶対に 番号 、ただし、サンプル目的でのみ検討します。

結局のところ、次のセクションと、アプリケーションがリファクタリングを必要としていることを示す特性を見ると、この例のアプリケーションはリファクタリングの有効な候補ではないことが明らかになります。

生命の輪

構造パターンを説明することから始めましょう MVC Railsで。

それは通常、https://www.toptal.com/jogging/show/1のようなリクエストを行うことによって検索エンジンから始まります。

Webサーバーは要求を受信し、rutasを使用します。何を定義するかcontrolador使用する。

個人財務勘定科目表

コントローラーは、ユーザーリクエスト、データ配信、Cookie、セッションなどを分析してから、modeloを尋ねます。データを取得します。

modelosこれらは、データベースと通信し、データを保存および検証し、ビジネスロジックを実行し、手間のかかる作業を行うRubyクラスです。ビューは、ユーザーが見ることができるものです:HTML、CSS、XML、Javascript、JSON。

Railsライフサイクルリクエストのシーケンスを表示したい場合は、次のようになります。

MVCライフサイクルを切り離すレール

私が達成したいのは、POROを使用して抽象化を追加し、create/updateアクションのパターンを次のようなものにすることです。

レール図作成フォーム

そして、アクションについてはこれに似たものlist/show :

Railsダイアグラムリストクエリ

POROの抽象化を追加することで、責任間の完全な分離を保証します 鎌 、Railsが完全には習得していないもの。

ガイドライン

新しいデザインを実現するために、以下のガイドラインを使用しますが、これらは手紙に従わなければならない規則ではないことに注意してください。それらは、リファクタリングを容易にする柔軟なガイドラインと考えてください。

  • ActiveRecordモデルには、関連付けと定数を含めることができますが、それ以外は含めることができません。 つまり、呼び出し(サービスオブジェクトを使用してそこに呼び出しを追加する)と検証(使用する)はありません。 フォームオブジェクト モデルの名前と検証を含めるため)。

  • コントローラを薄層として保持し、常にサービスオブジェクトを呼び出します。 ロジックを含めるためにサービスオブジェクトを呼び出し続けたいのに、なぜコントローラーを使用するのか疑問に思う人もいるかもしれません。さて、コントローラーは持っているのに良い場所です ルーティング HTTP、パラメーター解析、認証、コンテンツネゴシエーション、正しいサービスまたはエディターオブジェクトの呼び出し、例外のキャッチ、応答のフォーマット、および正しいHTTPコードステータスの返送。

  • サービスはオブジェクトを呼び出す必要があります クエリ 、および状態を保存しません。 非クラスインスタンスメソッドを使用します。で保存されたパブリックメソッドはほとんどないはずです 鎌 。
  • クエリ オブジェクトで作成する必要があります クエリ 。 オブジェクトメソッド クエリ オブジェクトを返す必要があります。 ハッシュ または アレイ 、ただしActiveRecordアソシエーションではありません。

  • 使用を避ける ヘルパー 、デコレータをより適切に使用します。 どうして?の一般的な問題 ヘルパー Railsでは、それらを非の束に変えることができるということです OO 、スペース名を共有し、互いにオーバーラップします。しかし、それははるかに悪いことです、あなたがポリモーフィズムを使用できないという事実は ヘルパー Rails-コンテキストやタイプごとに異なる実装を提供し、ヘルパーをバイパスまたはサブ分類します。種類は ヘルパー Railsでは、特定のユースケースではなく、一般的にユーティリティメソッドに使用する必要があります。プレゼンテーションロジックのモデル属性をフォーマットする方法。それらを軽く、簡単にフォローできるようにしてください。デコレータ/デリゲートの方が優れています。**なぜですか?結局のところ、心配はRailsの一部のようであり、乾燥する可能性があります( 乾かす )複数のモデル間で共有される場合のコード。ただし、より大きな問題は、懸念がモデルオブジェクトをよりまとまりのあるものにしないことです。コードだけがより適切に編成されています。つまり、モデルAPIに実際の変更はありません。

  • 抽出してみてください 貴重なアイテム モデルの コードをよりクリーンに保ち、関連する属性をグループ化します。

  • ビューごとに常にインスタンス変数を渡します。

リファクタリング

始める前に、何か他のことについて話し合いたいと思います。リファクタリングが始まると、通常、次のように疑問に思うことになります。 「それは良いリファクタリングですか?」

責任をより分離または分離しているように感じる場合(コードと新しいファイルを追加することを意味する場合でも)、これは良いことです。結局のところ、アプリケーションを切り離すことは非常に良い習慣であり、適切な単体テストを簡単に行うことができます。

ロジックをコントローラーからモデルに移動するなどのことについては説明しません。これは、Rails(通常はSkinnyコントローラーとFATモデル)を快適に使用できることを前提としているためです。

この記事を簡潔にするために、テストの方法については説明しませんが、これはテストすべきではないという意味ではありません。

それどころか、あなたはすべきです 常にテストから始める 先に進む前に、物事が順調に進んでいることを確認します。これは何かです 必須、 特にリファクタリングを行う場合。

Windowsに最適なコマンドライン

その後、変更を実装し、テストがコードの関連部分を通過することを確認できます。

貴重品を抽出する

まず、価値のあるオブジェクトは何ですか?

マーティンファウラー 説明:

値オブジェクトは、お金や日付範囲オブジェクトなどの小さなオブジェクトです。それらの重要な機能は、参照セマンティクスではなく、値セマンティクスに従うことです。

概念がそれ自体の抽象化に値し、その平等が価値観ではなくアイデンティティに基づいている状況に陥ることがあります。この例としては、Rubyの日付、URI、パス名などがあります。貴重なオブジェクト(またはドメインモデル)の抽出は非常に便利です。

なぜわざわざ?

値オブジェクトの大きな利点の1つは、コードの表現力を高めるのに役立つことです。コードはより明確になる傾向がありますが、少なくとも、適切な命名規則がある場合はそうなる可能性があります。値オブジェクトは抽象化されているため、コードがより明確になり、エラーが少なくなります。

もう1つの利点は 不変性 。オブジェクトの不変性は非常に重要です。値オブジェクトで使用できる特定のデータセットを格納するとき、私は通常、データが操作されるのが好きではありません。

これはいつ役に立ちますか?

この質問に対する完全な答えはありません。あなたにとって最善のこと、そして与えられた状況で最も理にかなっていることをしてください。

これ以外にも、その決定を下すのに役立つガイドラインがいくつかあります。

メソッドのグループが関連していると思うなら、まあ、貴重品はもっと高価です。この表現力は、値オブジェクトが特徴的なデータセットを表す必要があることを意味します。これは、平均的な開発者がオブジェクトの名前を見るだけで推測できます。

これどうやってやるの?

貴重品は特定の規則に従う必要があります。

  • 貴重品には複数の属性が必要です。
  • 属性は、オブジェクトのライフサイクル全体を通じて不変である必要があります。
  • 同等性は、オブジェクトの属性によって決定されます。

この例では、属性を抽象化するために、値オブジェクトEntryStatusを作成しますEntry#status_weatherおよびEntry#status_landformこのように見える独自のクラスに:

  • 要旨サンプル

注:これは単なるPORO(Plain Old Ruby Object)であり、ActiveRecord::Baseから継承されません。属性のリーダーメソッドを定義し、起動時にそれらを割り当てています。また、()メソッドを使用してオブジェクトを照合するために、同等の組み合わせを使用します。

モデルを変更できますEntry作成した値オブジェクトを使用するには:

  • 要旨サンプル

メソッドを変更することもできますEntryController#createそれに応じて新しい値オブジェクトを使用するには:

  • 要旨サンプル

サービスオブジェクトの抽出

Serviceオブジェクトとは何ですか?

Serviceオブジェクトの役割は、ビジネスロジックの特定のスペースでコードを保持することです。スタイルとは異なり 「ファットモデル」 、少数のオブジェクトに多くのメソッドが含まれている場合、必要なすべてのロジックに対して、サービスオブジェクトを使用すると、それぞれが固有の目的を果たす多くのクラスが生成されます。

どうして?メリットは何ですか?

  • 引き離します。 サービスオブジェクトは、オブジェクト間の分離を強化するのに役立ちます。

  • 可視性。 サービスオブジェクト(適切な名前が付けられている場合)は、アプリケーションの機能を示します。 servicesディレクトリを調べて、アプリケーションが提供する機能を確認できます。

  • クリーンなモデルとドライバー。 コントローラーはリクエストをオンにします( パラメータ 、session、cookies)引数で、それらをサービスに渡し、サービスの応答に応じてリダイレクトまたは終了します。モデルは関連付けと永続性のみを扱いますが。コントローラ/モデルからサービスオブジェクトへのコードの抽出はサポートします 鎌 そしてそれはさらにコードを分離するでしょう。モデルの責任は、関連付けを処理し、レコードを保存/削除するだけで済みますが、Serviceオブジェクトの責任は1つだけです( 鎌 )。これは、より良い設計とより良いテストユニットにつながります。
  • ドライ 変更を受け入れます。 私はサービスアイテムをできるだけシンプルで小さく保ちます。他のサービスオブジェクトと一緒にサービスオブジェクトを作成し、それらを再利用します。

  • テストスイートをクリーンアップして高速化します。 これらのサービスは、エントリポイント(呼び出されるメソッド)を持つ小さなRubyオブジェクトであるため、すばやく簡単にテストできます。複雑なサービスは他のサービスと複合されているため、テストを簡単に分離できます。また、サービスオブジェクトを使用すると、Rails環境全体をロードせずに、関連するオブジェクトを簡単に取得できます。

  • どこからでも利用できます。 サービスオブジェクトは、確かに、コントローラーからだけでなく、サービスオブジェクト、DelayedJob / Rescue / Sidekiq Jobs、Rakeタスク、コンソールなどからも呼び出されます。

一方で、完璧なものはありません。サービスオブジェクトの欠点の1つは、小さなアクションごとにやり過ぎになる可能性があることです。このような場合、コードが複雑になり、単純化されない可能性があります。

いつサービスオブジェクトを抽出する必要がありますか?

ここにも決まったルールはありません。

通常、サービスオブジェクトは、中規模から大規模のシステムに最適です。標準のCRUD操作を超えて、適切な量のロジックを備えたシステムです。

したがって、コードの一部がディレクトリに属していない場合は、コードを追加する場所で、コードを再検討して、サービスオブジェクトに移動した方がよいかどうかを確認することをお勧めします。

Serviceオブジェクトをいつ使用するかについてのいくつかのポインタを次に示します。

  • アクションは複雑です。
  • アクションは複数のモデルに到達します。
  • アクションは外部サービスと相互作用します。
  • 下線が引かれたモデルの主な関心事は行動ではありません。
  • アクションを実行する方法は複数あります。

サービスオブジェクトをどのように設計する必要がありますか?

サービスオブジェクトのクラスの設計は、必要がないため、比較的簡単です。 宝石 あなたは学ぶべきではありません DLS 新しいですが、可能であれば、多かれ少なかれ、すでに持っているソフトウェア設計スキルに頼ることができます。

通常、私は次のガイドラインと規則を使用してサービスオブジェクトを設計します。

  • オブジェクトの状態を保存しないでください。
  • クラスメソッドではなく、インスタンスメソッドを使用します。
  • パブリックメソッドはほとんどないはずです(できればサポートするメソッド) *シックル 。
  • メソッドは、ブール値ではなく、リッチなオブジェクト結果を返す必要があります。
  • サービスはディレクトリの下にありますapp/services 。強力なビジネスロジックドメインには、サブディレクトリを使用することをお勧めします。たとえば、ファイルapp/services/report/generate_weekly.rb定義しますReport::GenerateWeekly一方app/services/report/publish_monthly.rb Report::PublishMonthlyを定義します。
  • サービスは動詞で始まります(そしてサービスで終わりません):ApproveTransaction、SendTestNewsletter、ImportUsersFromCsv。
  • サービスは、呼び出されたメソッドに応答します。別の動詞を使用すると、少し冗長になることがわかりました。ApproveTransaction.approve()はうまく読みません。また、呼び出されるメソッドは、の事実上のメソッドです。 ラムダ 、 procs 、およびメソッドオブジェクト。

StatisticsController#indexを見ると、コントローラーにグループ化されたメソッドのグループ(weeks_to_date_from、weeks_to_date_to、avg_distanceなど)に気付くでしょう。それは良いことではありません。 statistics_controllerの外部で週次レポートを生成する場合は、その影響を考慮してください。

この例では、Report::GenerateWeeklyを作成します。 StatisticsControllerからロジックレポートを抽出します。

  • 要旨サンプル

だからStatisticsController#index今ではきれいに見えます:

  • 要旨サンプル

Serviceオブジェクトパターンを適用することで、複雑で特定のアクションを中心にコードをグループ化し、より小さく明確なメソッドの作成を促進します。

宿題: 使用を検討してください 貴重なオブジェクト WeeklyReport Structの代わりに 。

オブジェクトの抽出 クエリ コントローラー

オブジェクトとは クエリ ?

オブジェクト クエリ クエリデータベースを表すPOREです。アプリケーションのさまざまな場所で再利用できると同時に、クエリロジックを非表示にすることができます。また、テスト用の優れた絶縁ユニットも提供します。

複雑なSQL / NoSQLクエリを独自のクラスに抽出する必要があります。

各オブジェクト クエリ 基準/ビジネスルールに基づいて一連の結果を返す責任があります。

この例では、クエリはありません( クエリ )複雑なので、オブジェクトを使用します クエリ それは効率的ではないでしょう。ただし、デモンストレーションの目的で、クエリをReport::GenerateWeekly#callで抽出します。作成しますgenerate_entries_query.rb:

  • 要旨サンプル

そしてReport::GenerateWeekly#callで、置き換えましょう:

def call @user.entries.group_by(&:week).map do |week, entries| WeeklyReport.new( ... ) end end

と:

def call weekly_grouped_entries = GroupEntriesQuery.new(@user).call weekly_grouped_entries.map do |week, entries| WeeklyReport.new( ... ) end end

オブジェクトパターン クエリ (クエリ)は、コントローラーを細く保ちながら、モデルロジックをクラスの動作に厳密に関連させておくのに役立ちます。彼らはただの プレーンな古いRubyクラス 、オブジェクト クエリ ActiveRecord::Baseから継承する必要はなく、クエリの実行のみを担当する必要があります。

サービスオブジェクトへのエントリの作成を抽出します

次に、新しいサービスオブジェクトへの新しいエントリを作成するロジックを抽出します。規則を使用して、CreateEntryを作成しましょう:

  • 要旨サンプル

そして今、私たちのEntriesController#create以下のとおりであります:

def create begin CreateEntry.new(current_user, entry_params).call flash[:notice] = 'Entry was successfully created.' rescue Exception => e flash[:error] = e.message end redirect_to root_path end

シェイプオブジェクトのその他の検証

今、物事はより面白くなり始めています。

ガイドラインでは、モデルに関連付けと定数を含めることに同意しましたが、それ以外は何もありません(検証や呼び出しはありません)。それでは、吹き出しを削除して、代わりにShapeオブジェクトを使用することから始めましょう。

ShapeオブジェクトはPORO(Plain Old Ruby Object)です。データベースと通信する必要がある場合は、コントローラー/サービスオブジェクトのコマンドを実行します。

なぜShapeオブジェクトを使用するのですか?

アプリケーションをリファクタリングする必要がある場合は、主な単一責任であることに留意することをお勧めします( 鎌 )。

鎌 クラスが持つべき責任に関して、より良い設計上の決定を下すのに役立ちます。

たとえば、データベーステーブルモデル(RailsのコンテキストではActiveRecordモデル)は、コード内の一意のデータベースレコードを表すため、ユーザーが行うことを気にする必要はありません。

これがShapeオブジェクトの出番です。

Shapeオブジェクトは、アプリケーションで形状を表す役割を果たします。したがって、各入力フィールドはクラスの属性として扱うことができます。これらの属性がいくつかの検証ルールを満たしていることを検証でき、「クリーンな」データを適切な場所に渡すことができます(たとえば、データベースモデルまたはクエリ検索ビルダー)。

Shapeオブジェクトはいつ使用する必要がありますか?

  • Railsモデルから検証を抽出する場合。
  • 1つの配信方法で複数のモデルを更新できる場合は、Shapeオブジェクトを作成する必要があります。

これにより、すべてのフォームロジック(命名規則、検証など)を1か所にまとめることができます。

Shapeオブジェクトを作成するにはどうすればよいですか?

  • 単純なRubyクラスを作成します。
  • 含まれるものActiveModel::Model (Rails 3では、代わりに名前、変換、検証を含める必要があります)。
  • 通常のActiveRecordモデルであるかのように、新しいシェイプクラスの使用を開始します。最大の違いは、このオブジェクトに格納されているデータを続行できないことです。

あなたが使用できることに注意してください 宝石 改革 、ただしPOROを続行して、entry_form.rbを作成します。これは次のようになります:

  • 要旨サンプル

そして、変更しますCreateEntry Formatオブジェクトの使用を開始するにはEntryForm:

class CreateEntry ...... ...... def call @entry_form = ::EntryForm.new(@params) if @entry_form.valid? .... else .... end end end

注意: ServiceオブジェクトからShapeオブジェクトにアクセスする必要はなく、コントローラーから直接Shapeオブジェクトを呼び出すことができると言う人もいます。これは有効な引数です。ただし、明確なフローが必要なため、常にServiceオブジェクトからFormオブジェクトを呼び出します。

呼び出しをサービスオブジェクトに移動します。

以前に同意したように、モデルに検証と呼び出しを含めたくありません。 Shapeオブジェクトを使用して検証を抽出しました。ただし、まだいくつかの呼び出しを使用しています(after_createモデル内Entry compare_speed_and_notify_user)。

モデルから呼び出しを削除したいのはなぜですか?

Rails開発者 彼らは通常、テスト中に電話の痛みに気づき始めます。 ActiveRecordモデルをテストしていない場合は、アプリケーションが大きくなり、呼び出しを呼び出したり回避したりするためにより多くのロジックが必要になるため、後で痛みに気付くようになります。

después_*呼び出しは、主にオブジェクトの保存または続行に関連して使用されます。

フォーム知覚のゲシュタルト原則

オブジェクトが保存されると、オブジェクトの目的(責任など)が実行されます。したがって、オブジェクトが保存された後も呼び出しが呼び出されているのが見える場合、これらはおそらく責任のあるオブジェクト領域から抜け出そうとしている呼び出しであり、それが問題に遭遇したときです。

この例では、入力ドメインに関係のないSMSをユーザーに送信しています。

この問題を解決する簡単な方法は、呼び出しを関連するサービスオブジェクトに移動することです。結局のところ、対応するユーザーにSMSを送信することは、サービスオブジェクトに関連していますCreateEntryエントリーモデルではありません。

これを行うことで、シャットダウンする必要がなくなります。compare_speed_and_notify_user私たちのテストで。これを簡単な問題にし、SMSを送信せずにエントリを作成し、クラスに固有の責任を持たせることで、優れたオブジェクト指向設計に従います( 鎌 )。

だから今CreateEntryこれに似たものです:

  • 要旨サンプル

ヘルパーの代わりにデコレータを使用する

コレクションは簡単に使えますが ドレーパー ビューモデルとデコレータについては、これまでと同じように、この記事ではPOROを使用します。

必要なのは、装飾されたオブジェクトのメソッドを呼び出すクラスです。

使用できますmethod_missingこれを実装するには、標準のRubyライブラリSimpleDelegatorを使用します。次のコードは、SimpleDelegatorの使用方法を示しています。ベースデコレータを実装するには:

% app/decorators/base_decorator.rb require 'delegate' class BaseDecorator

なぜ_hメソッド?

このメソッドは、ビューコンテキストのプロキシとして機能します。デフォルトでは、ビューコンテキストはビュークラスのインスタンスであり、これはActionView::Baseです。あなたはアクセスすることができます ヘルパー 次のようにビューの:

_h.content_tag :div, 'my-div', class: 'my-class'

より便利にするために、メソッドを追加しますdecorado a ApplicationHelper:

module ApplicationHelper # ..... def decorate(object, klass = nil) klass ||= '#{object.class}Decorator'.constantize decorator = klass.new(object, self) yield decorator if block_given? decorator end # ..... end

今、私たちは移動することができます ヘルパー EntriesHelperデコレータへ:

# app/decorators/entry_decorator.rb class EntryDecorator

そして、readable_time_periodを使用できます。およびreadable_speed次のように:

# app/views/entries/_entry.html.erb - + - +

リファクタリング後の構造

最終的にファイルが増えましたが、それは必ずしも悪いことではありません(これを覚えておいてください。最初から、この例はデモンストレーション目的であり、 番号 必然的にリファクタリングの良いユースケースでした):

app ├── assets │ └── ... ├── controllers │ ├── application_controller.rb │ ├── entries_controller.rb │ └── statistics_controller.rb ├── decorators │ ├── base_decorator.rb │ └── entry_decorator.rb ├── forms │ └── entry_form.rb ├── helpers │ └── application_helper.rb ├── mailers ├── models │ ├── entry.rb │ ├── entry_status.rb │ └── user.rb ├── queries │ └── group_entries_query.rb ├── services │ ├── create_entry.rb │ └── report │ └── generate_weekly.rb └── views ├── devise │ └── .. ├── entries │ ├── _entry.html.erb │ ├── _form.html.erb │ └── index.html.erb ├── layouts │ └── application.html.erb └── statistics └── index.html.erb

結論

このブログ投稿ではRailsに焦点を当てていますが、RoR(Ruby on Rails)はサービスオブジェクトや他のPOROに依存していません。このアプローチは、 フレームワークウェブ 、モバイルまたはコンソールアプリ。

使用する場合 MVC アーキテクチャとしては、ほとんどの変更がアプリケーションの他の部分に影響を与えるため、すべてがくっついて速度が低下します。また、ビジネスロジックをどこに配置するかを考える必要があります。モデル、コントローラー、またはビューのどこに配置する必要がありますか?

単純なPOROを使用することで、ビジネスロジックをモデルまたはサービスに移動しました。モデルまたはサービスはActiveRecordから継承しません。これは、サポートするより明確なコードがあることは言うまでもなく、すでに利益になっています。 鎌 より高速なユニットテスト。

クリーンなアーキテクチャでは、アプリケーションの動作を簡単に確認できるように、構造の中央/上部に使用ボックスを配置しようとします。また、モジュール化されて分離されているため、変更の採用が容易になります。方法を示したと思います プレーンな古いRubyオブジェクト さらに抽象化すると、懸念事項が分離され、テストが簡素化され、クリーンで保守可能なコードの作成に役立ちます。

関連: Ruby on Railsの利点は何ですか?プログラミングの20年後。 Railsを使用する

2020年の音楽産業の現状

収益と成長

2020年の音楽産業の現状
ビデオゲームの物理チュートリアル-パートIII:拘束された剛体シミュレーション

ビデオゲームの物理チュートリアル-パートIII:拘束された剛体シミュレーション

技術

人気の投稿
シニアコーポレートカウンセル
シニアコーポレートカウンセル
エンタープライズクライアントサービスディレクター、産業用製品およびサービス
エンタープライズクライアントサービスディレクター、産業用製品およびサービス
フリーランスのファイナンスコンサルタントが大企業をどのように打ち負かしているか
フリーランスのファイナンスコンサルタントが大企業をどのように打ち負かしているか
あなたのスキルを披露する–ポートフォリオを作成する方法
あなたのスキルを披露する–ポートフォリオを作成する方法
Reactフックをテストするための完全ガイド
Reactフックをテストするための完全ガイド
 
かんばんとTrelloを使用してソフトウェア開発を管理するための初心者向けガイド
かんばんとTrelloを使用してソフトウェア開発を管理するための初心者向けガイド
生存のための再編成:シナリオの構築
生存のための再編成:シナリオの構築
Android開発のためのReactNativeに飛び込む
Android開発のためのReactNativeに飛び込む
安全で健全–パスワードUXへのアプローチ方法
安全で健全–パスワードUXへのアプローチ方法
.NETプロジェクトをブートストラップして作成する方法
.NETプロジェクトをブートストラップして作成する方法
人気の投稿
  • ラズベリーパイホームサーバー2018
  • 分析のためにTwitterデータを取得する方法
  • Ruby onRailsはどのように機能しますか
  • SQLServerのパフォーマンスチューニング手法
  • あなた自身のグーグルグラスを作る
  • AC法人と法人としての違いは何ですか
  • node.jsは何に使用されますか
カテゴリー
計画と予測 モバイル Uiデザイン トレンド ヒントとツール プロジェクト管理 人とチーム モバイルデザイン デザイナーライフ ブランドデザイン

© 2021 | 全著作権所有

apeescape2.com