【Rails入門】ViewとModelの間にDecorator(Draper)を置く

今回は、Ruby on Rails(以降、Rails)で、誰もが頭を悩ませている、ある問題を1つ解決しましょう。

それは、

・ModelとViewのどっちに書くの問題

です。

非常に簡単な例で、Modelでは日時を”2018-07-12 18:12:34”で保持していますが、Viewでは”2018/7/12”と表示する場合を考えましょう。

この日時のフォーマットを変更するコードは、ModelViewのどっちに書くのでしょう。

私は、ModelとViewの間にDecoratorを1つ追加して、Decoratorにコードを書くことをおすすめします!

どういうこと?と思ったあなたは、この記事を読むべきです。

それでは、始めましょう。

Decoratorとは

ちょっと概念的な話から始めますので、まずは結論から。

Model→Viewの関係でコードを書いていたところにDecoratorを追加して、Model→Decorator→Viewの関係でコードを書くことをおすすめします。

ここでは、「Decoratorを追加する」と、どうして「ModelとViewのどっちに書くの問題」が解決できるのかを、説明します。

私が書く記事でたびたび登場している以下の表をご覧ください。

コンポーネント(構成要素)説明備考
Model(モデル)データベースを取り扱うデータベースへの格納方法は、Modelに隠ぺいする
View(ビュー)画面表示を取り扱う表示方法は、Viewに隠ぺいする
Controller(コントローラー)(ユーザーの入力を受けて)ModelとViewにアレコレ指示するデータベースへの格納方法や表示方法は知らない

これを見ると、日時のフォーマットを変更するコードは、ModelにもViewにも書ける気がします。

Modelを見ると「データベースへの格納方法は、Modelに隠ぺいする」と書かれています。

データベースに格納する”2018-07-12 18:12:34”という形式は、Viewには知られたくないということですから、Viewで日付が必要になったらModel側で適切なフォーマットにするべきという考え方ができます。

一方、Viewを見ると「表示方法は、Viewに隠ぺいする」と書かれています。

”2018/7/12”という表示方法はView側に書くべきとも考えられます。

どちらも正しい気もしますが、どちらも正しくない気がします。

まさに、頭を悩ませるModelとViewのどっちに書くの問題ですね。

私は、Decoratorをおすすめする立場ですので、次のように考えています。

Modelは「データ管理だけ」を担当し、Viewは「情報表示だけ」を担当する、という役割分担です。

そして新しく登場するDecoratorには、「(プログラムで管理しやすい)データ」を「(ユーザーが理解しやすい)情報」に変換する役割を持たせます。

そして、先ほどの日時のフォーマットを変更するコードはDecoratorに書き、ViewではDecoratorで用意したメソッドを利用します。

ModelとViewの間にDecoratorを追加して、データを情報に変換する役割を持たせれば、ModelとViewのどっちに書くの問題」が解決できそうですね!

Draperとは

ここからは、RailsでDecoratorを実現する方法を紹介していきましょう。

この記事では、Railsらしくgemをインストールして、Decoratorを実現します。

Decoratorを簡単に導入するためのgemはいくつかあります。

The Ruby Toolboxでは、「Rails Presenters」というカテゴリーで集約されています。

参考:https://www.ruby-toolbox.com/categories/rails_presenters

今回は、最もダウンロード数が多いDraperを使ってDecoratorを導入してみます。

Draper以外にも、CellsActiveDecoratorApotomodisplay_caseなどが登録されていますね。

実装方法や使いかたが異なりますので、最高のgemを探す!という方は試してみると良いでしょう。

動作を理解するためのWebアプリを作成する

DraperによるDecoratorを理解するために、RailsをインストールしてWebアプリを作りましょう。

(1)Railsをインストールします。

私は、以下の記事を参考に、VirtualBoxで作成した仮想パソコンにインストールしたLinux Mintに、Railsの開発環境を作成しました。

基本的には記事の手順に従って操作しますが、app/samurai/sample1ディレクトリを作成する代わりに、app/samurai/decorator-demoディレクトリを作成しました。

Railsを起動して、ブラウザで画面が表示されることを確認したら、いったんRailsを終了してから次に進みます。

初心者でもかんたん!Ruby on Rails の開発環境の構築手順(Mac/Windows 両対応)
更新日 : 2018年4月15日

Linux Mintのインストールについては、以下の記事で詳しく説明しています。

Linux Mint Cinnamonエディションを使ってみよう
更新日 : 2019年5月22日

Draperをインストールする

Railsのインストールが済んでいれば、Draperのインストールは簡単です。

(1)Gemfileの最終行に以下の内容を追加します。

(2)以下のコマンドを1行ずつ順番に入力します。

Draperがインストールされました。

簡単なDecoratorを使ってみる

簡単な例ですが、Modelで保持している”2018-07-12 18:12:34”という日時を、Viewでは”2018/7/12”と表示するDecoratorを作ってみましょう。

まずは、bin/rails generate scaffoldコマンドです。

(1)「端末」で以下のコマンドを1行ずつ順番に入力します。

実行結果:

通常のscaffoldで作成されるディレクトリ/ファイルに加えて、以下の4つが作成されています。

Decoratorらしいディレクトリとファイルですね。

(2)以下のコマンドを1行ずつ順番に入力します。

(3)以下のコードを入力します。

(4)app/views/users/index.html.erbを変更します。

変更前:

変更後:

(5)app/views/users/show.html.erbを変更します。

「Created at」を表示するようにしました。

変更前:

変更後:

(6)以下のコマンドを入力します。

Userの一覧をブラウザで確認してみましょう。

(5)ブラウザで「http://localhost:3000/users」にアクセスします。

「Created at」が表示されていますね。

ここでいずれかの「show」をクリックすると、やはり「Created at」が表示されていることが確認できます。

ここからは「Created at」の表示を「2018/7/13」に変更するために、Decoratorを作ります。

Controllerを変更する

まずは、ControllerからViewに対して、通常のオブジェクトを渡す代わりに、Decorator付きのオブジェクトを渡すように変更します。

(1)app/controllers/users_controller.rbを変更します。

変更前:

変更後:

「http://localhost:3000/users」や「http://localhost:3000/users/1」にアクセスしたときに、Decoratorで機能を追加した(追加する予定の)@usersまたは@userを取得するようにしています。

先ほどと同じURLにアクセスしてみましょう。

(2)ブラウザで「http://localhost:3000/users」にアクセスします。

何も変わっていないことを確認してください。

無駄なことをしたのか!?と思うかもしれませんが、そうではありません。

ここでは、@usersをDecoratorで機能を追加した(追加する予定の)データに差し替えたにもかかわらず、何も変わらずにWebアプリが動作していることが重要です。

Decoratorを変更する

次に、Decorator(app/decorators/user_decorator.rb)を変更して、def created_atを定義し、機能を変更しましょう。

このように、インスタンス変数と同じ名前のメソッドを定義すると、ViewやModelを変更することなく、Decoratorを利用できます。

(1)app/decorators/user_decorator.rbを変更します。

変更前:

変更後:

(2)ブラウザで「http://localhost:3000/users」にアクセスします。

Decoratorのdef created_atが呼び出され、日付のフォーマットが変わっていますね。

(3)「山田太郎」の「show」をクリックして、http://localhost:3000/users/1にアクセスします。

こちらでもDecoratorのdef created_atが呼び出され、日付のフォーマットが変わっています。

ModelとViewを変更しない

ここまで、ControllerDecoratorを変更してきました。

一方、ModelとViewは変更していません。

それでも、表示を変更できたということは、初めに話題にしたModelとViewのどっちに書くの問題は、どっちにも書かない(Decoratorに書く)という結論に至ったことになりますね。

まとめ

今回は、DraperによるDecoratorの実装方法を簡単に紹介しました。

Decoratorを使うもっとも大きなメリットは、Modelを変更しなくても、Viewを変更しなくても、表示を変更できるという点です。

今回のようにcreated_atという、インスタンス変数と同じ名前のメソッドを定義するのもポイントでした。

このように、Decoraterで機能を追加したオブジェクトと、機能を追加する前のオブジェクトを、Viewで区別する必要がないように、Decoratorをうまく定義する、というのも、Decoratorパターンの大事な考えかたですので、覚えておきましょう。

それでは、また。

経験豊富なエンジニアに相談したいあなたへ

「IT業界の実情についてエンジニアの生の声を聞きたい、既にIT業務の仕事をしていて解決したい課題があるから相談にのってもらいたい」そんな要望はございませんか?

周りにエンジニアをやっている人がいないと、実際の現場のことがイメージできず不安ですよね。

侍エンジニア塾の無料体験レッスンでは、ご質問内容によって「現役エンジニア」があなたの開発したいサービスへ技術的なアドバイスや、未経験から内定を獲得する転職活動の極意をお伝えいたします。

下記の無料体験レッスン予約カレンダーよりお申し込みいただけます。あなたのご相談を心よりお待ちしております。

LINEで送る
Pocket

書いた人

侍テック編集部

侍テック編集部

おすすめコンテンツ

あなたにぴったりなプログラミング学習プランを無料で診断!

プログラミング学習の効率を劇的に上げる学習メソッドを解説