JavaScriptでDOM操作
DOMとは
DOMとはDocument Object Modelの略。
HTMLの構造をツリー構造として扱い、プログラミング言語からHTMLの要素にアクセスし操作するための仕組み。
WebブラウザがWebサーバから送られたHTML文書を解析し、DOMというデータ構造に変換している。
DOMのツリー構造
画像引用元 JavaScript HTML DOM
ツリーの各要素をノードという。
ノードには要素ノード(エレメントノード)、テキストノードがある。
要素ノードはHTMLの情報、テキストノードはテキスト部分の情報を持っている。
※Rubyはサーバーサイドで動く言語なので、ブラウザ側で動くJavaScriptでDOMを操作する。
DOMを操作する
最近はReactやVue.jsなどのフレームワークがスタンダードになりつつあり、この方法で操作する機会は減りつつあるが、現在でも使用されている。
私はDOM操作をざっくりではあるけど学んだ結果、「なるほど、そんな風に動かしてるんだ〜」とそれまで曖昧だった、ブラウザ側でどのようにHTMLを操作するかの理解を深めることができた。
それでは例示しつつざっくり触れる。
<button>更新</button>
このままではDOM操作ができない。識別子がないと要素を識別できないからだ。
DOMを操作するにはidやclassが必要
<button id="button-update">更新</button>
ここからDOMを取得するには、下のJavaScriptを使う
document.getElementById("button-update") // => "<button id='button-update'>更新</button>"
ブラウザ内の JavaScript エンジンには、基本的な文法解析機能のほかに、DOM 操作などブラウザ固有の処理を行うためのオブジェクトがあらかじめ用意されています。
その一つが、document オブジェクトです。これは、HTML 文書(つまり、画面に表示されている Web ページ)を表すオブジェクトで、DOM 操作のためのメソッドが含まれています。
引用元 JavaScript超基礎講座!DOM操作を学ぶ | Hypertext Candy
最初のdocument
がそのオブジェクト。
getElementById("id名")
というメソッドで指定されたidを持つノードをDOMとして取得する。
※ドキュメントの操作 - ウェブ開発を学ぶ | MDNによるとこのgetElementById("id名")
というメソッドは古い手法らしく、querySelector()
の方が推奨されているらしい。今回はあくまでも基本を抑えて理解するのが目的なのでgetElementById("id名")
を使います。
要素の中のテキストを取得したい場合はinnerText
メソッドを使う
const buttonUpdate = document.getElementById("button-update") buttonUpdate.innerText // => "更新"
テキストを書き変えたい場合は
const buttonUpdate = document.getElementById("button-update") buttonUpdate.innerText = "アップデート"
とすればいい。
イベントドリブン
何かをしたら何かが起こるようにすることを考える。
ここでは、インプットフォームに数字を入れてボタンをクリックすると、表示されている初期値0の数字が加算されていくものを作る。
HTML
<input type="number" name="amount" id="js-amount"> <button id="js-add-button">足す</button> <p id="js-result">0</p>
考え方
まず、各要素のDOMを取得する必要がある。
var currentValue = 0 // ここは変数 const jsAddButton = document.getElementById('js-add-button') const jsAmount = document.getElementById('js-amount') const jsResult = document.getElementById('js-result')
次にクリックしたらどう処理していくかを書く。
DOMにイベントを仕掛けるにはaddEventListener
を使う。第一引数にイベントの種類を指定する。
jsAddButton.addEventListener('click', () => { const amount = jsAmount.value // valueでinputに入力された値を取得 currentValue += parseInt(amount) // input内の値は実は文字列。parseIntで整数へ jsResult.innerText = currentValue // currentValueを上書き })
これらを合わせて
JavaScript
document.addEventListener('DOMContentLoaded', () => { var currentValue = 0 // ここは変数 const jsAddButton = document.getElementById('js-add-button') const jsAmount = document.getElementById('js-amount') const jsResult = document.getElementById('js-result') jsAddButton.addEventListener('click', () => { const amount = jsAmount.value // valueでinputに入力された値を取得 currentValue += parseInt(amount) // input内の値は実は文字列。parseIntで整数へ jsResult.innerText = currentValue // currentValueを上書き }) })
document.addEventListener('DOMContentLoaded', () => {})
はブラウザがDOMの解析を完了させてからというような意味合い。
これがないとうまく動かないことがある。
参考
JavaScript超基礎講座!DOM操作を学ぶ | Hypertext Candy
パーシャルでコレクションをレンダリングする/ローカル変数を渡す
パーシャルとは
部分テンプレートまたはパーシャルは、出力を扱いやすく分割するための仕組みです。パーシャルを使用することで、ビュー内のコードをいくつものファイルに分割して書き出し、他のテンプレートでも使いまわすことができます。
引用 Railsガイドより
学習する中で、重要かつこんがらがりやすい使い方や省略した書き方について記事にまとめます。
コレクションをレンダリングする
投稿一覧画面などを作る場合、各投稿の情報を投稿表示用のパーシャルへ渡し、繰り返し表示させる方法を使うと良い。
その時、繰り返し表示させるからといって
<% @posts.each do |post| %> <%= render partial: 'post', locals: { post: post } %> <% end %>
のように書くと、@postsの数だけrenderメソッドが実行されパフォーマンスが悪化する原因となる。
こういう場合は、
<%= render partial: 'post', collection: @posts %>
のようにcollection
オプションを使い、一度でレンダリングすると良い。
collection
オプションを使用するとcollection
オプションに指定した変数の要素の分だけ部分テンプレートが繰り返し表示される。
パーシャルはデータの繰り返し (コレクション) を出力する場合にもきわめて便利です。
:collection
オプションを使用してパーシャルにコレクションを渡すと、コレクションのメンバごとにパーシャルがレンダリングされて挿入されます。
引用 Railsガイドより
省略記法
また、collection
オプションを利用した書き方には以下の省略記法があり、よく使う。
<%= render @posts %>
この省略法を使うには以下の条件が必要。
- 呼び出すパーシャルがviewsフォルダ内の
posts
フォルダにある - パーシャル名が
_post.html.erb
であること - パーシャル内で使う変数が
post
であること
※各条件はpost
の場合。使用したいパーシャル名・変数名によって読み変えるべし。
ローカル変数を渡す
パーシャル内ではインスタンス変数を使うのは望ましくない。
パーシャルをレンダリングする時にローカル変数を引数として渡すとよい。
なぜパーシャル内でインスタンス変数を使うべきでないかというと、パーシャルの再利用性が低くなるため。
例えばパーシャル内でインスタンス変数を使った場合、コントローラー側でインスタンス変数に変更を加えると、パーシャル側も変更しなければならなくなる(変更しないとバグの原因になり得る)。ビューと特定のコントローラーの依存が強まり、また特定のモデルのデータに関連づけられてしまうので、別の場所で再利用できなくなる。
依存性を低くし、再利用性を高めるためローカル変数を渡す。
<%= render pertial: 'article', locals: { article: @article } %>
※localsオプションを書いている場合はpertialは省略できない
省略記法
<%= render 'article', article: @article %>
<%= render @article %>
注意
<%= render @posts %> # コレクションのレンダリング(要素の繰り返し表示)
と
<%= render @post %> # ローカル変数を渡したパーシャル
では意味と挙動が違うので注意。
参考
【Rails】 部分テンプレートの使い方を徹底解説! | Pikawaka - ピカ1わかりやすいプログラミング用語サイト
【Rails基礎】ややこしい部分テンプレートの省略形について簡単にまとめてみた|TechTechMedia
Rails パーシャル(部分テンプレート)へローカル変数を渡したいとき - Qiita
Rails部分テンプレート(パーシャル)まとめ - Qiita
Rails 5系でBootstrap 4系を導入する
Bootstrapとは
Bootstrapとは、WEBサイトやWEBページを効率よく開発するためのオープンソースのCSSフレームワーク。
HTML・CSS・JavaScriptから構成されている。WEBページでよく使われるフォーム・ボタン・メニューなどの部品がテンプレートとして用意されている。
レスポンシブ対応がされているので、Bootstrapを導入するだけで様々な端末に対応したモバイルファーストなウェブサイトを構築することができる。
注意点
Bootstrap 4系はJQueryに依存しており別途インストールする必要があるが、Bootstrap 5系からはjQueryとの依存関係がなくなりjQueryなしで使用できるようになっている。
Rails 6系からアセットパイプラインに変更が加えられ、デフォルトではSprocketsはJavaScriptを処理しなくなり、代わりにWebpackがデフォルトとして設定されている。
この記事では、Rails 5系でBootstrap 4系を導入する方法をまとめています。
導入方法の確認
Gemを使おう
Railsで外部のライブラリやフレームワークを導入する場合、ダウンロードしてそのまま使う方法とRails用に最適化されたGemを使う方法がある。
特段事情がない限りGemを使う方が良い。
RailsでGemを使う場合、利用したいGemライブラリをGemfileという定義ファイルに記載して、bundle installコマンドでライブラリのソースコードを取得する。この時bundlerというパッケージ管理ツールを使用している(bundle installはbundlerのコマンド)。
このbundlerが様々なライブラリのバージョンや依存関係を管理して、扱いやすくしてくれるというのが理由。
Gem同士の依存関係やバージョンによってはエラーがでて使えないことあるが、bundlerがこれを防いでくれる!(……こ、これが文明というヤツか!)
Bootstrap 4系はbootstrap-rubygem
※Bootstrap 3,Bootstrap 2を使用するならbootstrap-sassのREADMEの手順に従い導入してください。(なかなかややこしいけども!)
2021/10/08現在、Bootstrapの最新verは5.1.0。
bootstrap-rubygemのGitHubページのREADMEでは5系のインストール方法が記載されている。
それ以下のバージョンについて知りたい時はRubyGemsでGem名を検索すると良い。
最新verの詳細ページのバージョン履歴から、それぞれのバージョンの詳細(他のGemや環境との依存関係・ドキュメントへのリンクなど)を確認できる。
また、bootstrap-rubygemのREADMEのgem v5.1.0(下の画像の青い部分)が、RubyGemsの最新verの詳細ページへのリンクになっいて、ここからも確認できる。
導入手順
1. bootstrap、jquery-railsをインストール
Gemfile
gem 'bootstrap', '~> 4.6.0' gem 'jquery-rails'
と記述を加え、ターミナルで
$ bundle install
サーバー起動中ならここで再起動。
2. マニフェストファイルに記述する
application.scss
app/assets/stylesheets/application.cssをapp/assets/stylesheets/application.scssへリネームするか、前者を削除し後者を新規作成する。
app/assets/stylesheets/application.scss
@import "bootstrap";
と記載。BootstrapはCSSの機能を拡張する言語であるSassを使っているため、読み込めるようにする必要がある。
Sassを使う場合、マニフェストファイル(ざっくりいうと読み込むアセットを指定するもの。ここではapplication.scssのこと)にこのように記載する。
CSSでの*=require
や*=require_tree
といった記述法は使用しないこと。(application.cssをリネームした場合はこの記述が残っていると思うので、全て消去する)
application.js
また、Bootstrap 4はJQuery(JavaScriptのライブラリの一種)にも依存しているので、こちらもマニフェストファイルに記載する必要がある。
app/assets/javascripts/application.jsを作り、以下を書く。
//= require jquery3 //= require popper //= require bootstrap-sprockets
注意点としては、読み込み順。
通常マニフェストファイルは上から下へ順に読み込むので、bootstrap-sprocketsより上に jquery3を記載すること(エラーの原因になる)。
これで導入完了!
参考
GitHub - twbs/bootstrap-rubygem: Bootstrap 4 rubygem for Rails / Sprockets / Hanami / etc
bootstrap | RubyGems.org | your community gem host
File: README — Documentation for bootstrap (4.6.0)
Bootstrap · 世界で最も人気のあるフロントエンドフレームワーク
Railsアプリで Bootstrap 4 を利用する - Qiita
アセットパイプラインとマニフェストファイル
アセットパイプラインとは
ブラウザが認識できる言語は、HTML,CSS,JavaScriptの三種類。
しかし、Railsでアプリケーションをつくる場合、当然ながらERB(埋め込みRuby)を使用している。画像などのリソースもブラウザで表示させる場合がほとんどだ。
つまりそのままでは、ブラウザが認識・解釈し表示させることができない。
そこでRailsに限らず他の言語で作成されたWebアプリでも、ブラウザが認識できる言語へ変換する必要がある。
その仕組みこそがアセットパイプラインというフレームワーク。
アセットパイプラインは画像、CSS、JavaScriptといったアセットファイルを連結/圧縮することでRailsアプリを高速化します。また、より高級な言語で書かれたCSSやJavaScriptをコンパイルする機能も備えています。
引用元
【Rails】アセットパイプライン(Sprockets)の基本情報と実装方法 - AUTOVICE
Railsではsprockets-rails gem(SprocketsをRails用にカスタマイズしたもの)によって実装され、デフォルトで有効になっている。
※ Rails 6からはWebpackerが標準となっているが、Sprocketsも使われている。
アセットパイプラインの機能・処理
1 高級言語のコンパイル
2 アセットの連結
3 アセットの最小化
4 ダイジェスト(フィンガープリント)の付与
という順で処理される
高級言語のコンパイル
より高級な言語で記述されたコードはプリコンパイルされ、実際のアセットになる。(ブラウザが認識できるJavaScript,CSSファイルとして扱えるようにする。)
デフォルトでサポートされている言語は、CSSに代わるSass、JavaScriptに代わるCoffeeScript、CSS/JavaScriptに代わるERB。
アセットの連結
複数のJavaScript,CSSファイルをそれぞれ一つのファイルに連結することで、ブラウザがWebページをレンダリングするためのリクエスト数を減らすことができる。Webブラウザが同時に処理できるリクエスト数には限りがあるため、同時リクエスト数を減らすことができればその分読み込みが高速になる。
SprocketsはすべてのJavaScriptファイルを1つのマスター.jsファイルに連結し、すべてのCSSファイルを1つのマスター.cssファイルに連結する。
アセットの最小化
スペース、改行、コメントを削除してファイルを最小化(一種の圧縮)し、通信量を節約する。
Railsではsass-rails gemが自動的にGemfileに追加される。Sprocketsはアセット圧縮の際にこのgemを使用する。
ダイジェスト(フィンガープリント)の付与
コードの内容からハッシュ値を算出してファイル名の末尾に付与する。
このようにすると、コードが変更されればファイル名が変更されるので、ブラウザのキャッシュの影響で修正が反映されないという問題を防ぐことができる。
※アセットパイプラインはdevelopment環境とproduct環境で挙動が変わる。
development環境では、高級言語のコンパイル、ダイジェストの付与は逐次自動で行われる。処理速度は良くないが、開発者が自らコンパイルする必要はない。
アセットの連結と最小化は行われない。連結が行われず、ファイル数分のlink,scriptタグが生成されるのでデバッグしやすい。
production環境では全ての処理が行われる。
アセットの読み込み
RailsではCSSを読み込むにはstylsheet_link_tag、JavaScriptを読み込むにはjavascript_include_tagというヘルパーメソッドを使う。
共通のビューである、app/views/layouts/application.html.erbの<head>~</head>
内で読み込んでいる。
デフォルトでここから読み込んでいるのはapplication.css、application.js。
<%= stylesheet_link_tag "application", media: "all" %> <%= javascript_include_tag "application" %>
このapplication.css、application.jsとは、アセットパイプラインによって連結された結果のファイルを指す。
マニフェストファイル
ファイルをどう連結して出力するかは、app/assets配下のapplication.cssやapplication.jsといったマニフェストファイルに記述する。
どのファイルを読み込むのかを取りまとめたファイルを、マニフェストファイルと呼ぶ。
マニフェストファイルには ディレクティブ (directive: 命令、指示) を含めます。ディレクティブを使用して必要なファイルを指定し、それに基いて最終的に単一のCSSやJavaScriptファイルがビルドされます。Sprocketsはディレクティブで指定されたファイルを読み込み、必要に応じて処理を行い、連結して単一のファイルを生成し、圧縮します。
引用元
app/assets/javascripts/application.js
// ... //= require rails-ujs //= require turbolinks //= require_tree .
デフォルトでこのような記述がある。
JavaScriptのマニフェストファイルでは//=
で始まる行は、Sprocketの独自仕様でアセットパイプラインに指示(ディレクティブ)を与えるためのもの。
require_tree .
は「同階層に存在している全てのファイルを読み込む」という意味。
app/assets/stylesheets/application.css
/* ... *= require_self *= require_tree . */
require_self
は「自分自身を読み込む」という意味。
ここで注意が必要なのが読み込む順番。
基本的に上から下へ順に読み込むのだが、require_treeディレクティブで読み込まれるファイルの読み込み順序は指定できない。そのため、特定の読み込み順に依存しないようにする必要がある。順序を間違えるとエラーの原因となることがある。通常はrequire_tree .
は一番下に記述しておくのがいい。
CSSとSCSS(Sass)
まずは用語の確認から
CSS
HTMLで作られた文書構造にデザインを加えてWebページの見栄えを整える言語。「Cascading Style Sheets」 の頭文字をとったものであり、スタイルシートとも呼ばれる。
Sass
Sass(サス)はCSSを効率的に書くことができるメタ言語。CSSに対して機能を拡張する。Syntactically Awesome StyleSheetsの略。
CSSではHTMLのクラスがネストになっている箇所でも、入れ子の構造で記述できないため、分けて書くしかないので直感的に関係の把握がしづらい。
Sassだとコードを入れ子で記述できるので書く量を減らしたり、クラス名がかぶってしまい、ほかにもスタイルが適用されてしまうということを防げる。また、Sassでは変数を定義して使うことができるので、文字色や文字サイズが変わっても、全部の箇所を変更する手間が省ける。
SCSS記法とSASS記法がある。
SASS記法はインデント制御で記述する(記述量を必要最小限にできる)。SCSS記法ではCSSと同じように{}
を使い記述し、普通のCSSをそのまま記述しても動くのが利点。
SassはRailsでBootstrapを導入するときにも必要。
なぜここでSassについて言及するかというと、Sassはそのままではブラウザが解釈してくれない、つまりCSSへコンパイルさせる必要があるからだ。
マニフェストファイルの書き方に注意が必要。
Sassファイルを複数使用しているのであれば、Sprocketsディレクティブで読み込まずにSass @importルールを使用する必要があります。このような場合にSprocketsディレクティブを使用してしまうと、Sassファイルが自分自身のスコープに置かれるため、その中で定義されている変数やミックスインが他のSassから利用できなくなってしまいます。
引用元
RailsでSCSSを導入する
Railsはデフォルトでsass-railsというgemが導入されているで、ファイル名を変更し、追加したCSSファイルが全て読み込まれるよう記述していく。
デフォルトではapp/assets/stylesheets/application.cssがマニフェストファイルになっているので、これを削除して新しくapp/assets/stylesheets/application.scssを作る。
そこに読み込むファイルを@importを使って個別に読み込むファイルを記述する。 拡張子は省略。
@import "top"; @import "task"; @import "bootstrap-sprockets"; @import "bootstrap";
上記はあくまで例。
require_treeを使わないのは、ここでも読み込む順番の問題があるから。@importを使って必要なファイルのみ読み込む方がファイル管理を安全にできる。
参考
アセットパイプラインとマニフェストファイルについて - プログラミングの備忘録
【Rails】アセットパイプライン(Sprockets)の基本情報と実装方法 - AUTOVICE
アセットパイプラインの概要を理解する - Wataruの技術備忘録
SassとSASSとSCSSの違いについて | UX MILK
初学者(私!)にとっては「現場で使える Ruby on Rails 5速習実践ガイド」のChapter6のアセットパイプラインの説明がわかりやすかったです。
Railsガイド「ん?」→現場RailsのChapter6-8 アセットパイプライン参照→その後またRailsガイド でやっと腑に落ちました。
rails generateコマンド実行時に不要なファイルを生成しないよう設定する方法
rails gコマンドで生成されるもの
コントローラーやモデルを作成する場合、rails gコマンドを使うとコントローラーやモデルのファイルと共に、テストファイルなどの関連するファイルを自動で生成してくれる。
デフォルトで生成するものは以下の通り。
※ルーティングに関してはファイルを生成するのではなく、ルーティングを設定するという意味。
ただこのままだと開発を進める際に、自動で生成されるファイルに不要なものが含まれる場合がある。
不要なファイルを生成しないよう設定する方法
config/application.rbに設定を加えていく。
例えば、rails g controller コントローラー名(小文字複数形)
を実行する際、コントローラーファイルとビューのフォルダのみ(アクション指定時はファイルも)生成し、ルーティングを含め後は必要に応じて自分で用意したいという場合。
この場合、assets,helper,testファイルが不要となり、ルーティングも自動で設定されないようにすればいい。(rails gコマンドを使う場合不要になりやすい設定は、上の表からもわかる通り大体この辺り。コントローラーやマイグレーションファイルが不要なら、別のコマンドを使えばいいのだから)
config/application.rb
... module App名 class Application < Rails::Application config.load_defaults 5.2 # ここから config.generators do |g| g.skip_routes true # routes.rbを変更しない g.assets false # assets(CSS,JavaScript)ファイルを生成しない g.helper false # helperファイルを生成しない g.test_framework false # testファイルを生成しない end # ここまで config.generators.system_tests = nil end end
ちなみに
制限を加えていない、デフォルトの状態でrails g controller コントローラー名 アクション名
とアクションを指定して実行する場合、ビューではviews/コントローラー名/アクション名.html.erbが生成され、routes.rbにはget 'コントローラー名/アクション名'
が追加される。(このようなルート定義でも動きはするが、RESTfulの観点からは望ましくない。そのため、routes.rbに変更を加えないよう設定を変え自身でルーティングを設定していく方が、柔軟に開発できるしRESTfulに沿った記述にしていける。)コントローラーファイルには空のアクションが追加される。
アクションを指定しない場合、ビューはコントローラー名のフォルダのみ作成され、routes.rbに変更は加えられない。コントローラーファイルも当然変更なし。
rails g scaffold モデル名
を実行した場合、routes.rbにresourcesメソッドを使ってルーティングが一括で設定される。
参考サイト
Rails ジェネレータとテンプレート入門 - Railsガイド
rails generateで自動生成されるファイルの設定 - Qiita
Rails generate コマンドで生成するファイルを限定する方法 - Kazu Tech Blog
DIVE INTO CODE | rails g コマンドが行なっていること
実はこんなにある rails generate|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社
N + 1問題
N + 1問題とは
SQLが必要以上に実行されてしまい、パフォーマンスが落ちる問題。
どういう時に起こるかというと、外部キー制約でアソシエーションが定義された複数のテーブルの情報を、横断的に繰り返し処理を用いて取得しようとする場合。例えば、一対多の関係の場合(例ではUserモデルとPostモデル。1人のuserが複数のpostを持つ場合)、
app/models/user.rb
class User < ActiveRecord::Base has_many :posts # 複数形 end
app/models/post.rb
class Post < ActiveRecord::Base belongs_to :user # 単数形 end
Viewでuserのpostのcontent要素を表示したいとき
app/controller/users_controller.rb
class UsersController < ApplicationController def index @users = User.all end end
app/views/posts/index.html.erb
<% @users.each do |user| %> <%= user.post.content %> <% end %>
この時発行されるSQLは、まずControllerのindexアクションでUser.allの実行で一回。Userモデルの情報を全て取得。
その後、Viewのeach文を実行する際、まずuser_idが1のuserのpostの情報を得るためにPostのデータベースにアクセスするSQLが実行され、次の繰り返しでuser_idが2のuserのpostの情報を得るためのSQLが実行され……
と結局、全てのuserに関してpostを得る処理が繰り返しの都度実行される。
つまり、最初にuserの情報を得るために一回(1)
userそれぞれのpostの情報を得るためにuserの人数分の回数(N)
SQLが実行される。扱うデータの数量が大きくなるほど実行しなければならない回数が増えて処理が追いつかなくなってしまうという問題。
対応策
includesメソッドを使う
app/controller/users_controller.rb
class UsersController < ApplicationController def index @users = User.all.incledes(:posts) end end
と変更する。
こうすることで、userの情報とpostの情報をまとめて取得できるので、SQLの実行回数を大幅に減らせる。
参考
Active Record クエリインターフェイス - Railsガイド
【Rails】 N+1問題をincludesメソッドで解決しよう! | Pikawaka - ピカ1わかりやすいプログラミング用語サイト
Draper
Draperとは
Railsで利用できる、Decoratorを導入するためのGem。
※Decoratorとはソフトウェアのデザインパターンの一つ。既存のオブジェクトに新しい機能や振る舞いを動的に追加するためのもの。
どんな場合に使用するか
Modelの情報をViewで表示しようとする場合、Viewを装飾するメソッドを追加したいがModelに書くとModelが肥大化してしまう。もちろんViewに書くと重複が多くなり、可読性が低下する。
helperに書いても動きはするが、scopeの問題があり、名前空間がグローバルなのでメソッド名が衝突する危険性がある。また、メソッドを想定していない使い方をしてバグの原因になりかねない。
よって、以下のような使い分けをすると良い。
- helper Modelから独立し関係していない表示ロジック
- Decorator 一つのModelに関連した表示ロジック
導入
インストール
#Gemfile gem 'draper'
$ bundle install
次にデコレーター層を追加するために以下を実行
$ rails generate draper:install
この後は
$ rails generate decorator ○○(モデル名)
で指定したモデルに対応したデコレーターファイルが作成される。
app/decorators/○○_decorator.rb
class ○○Decorator < Draper::Decorator delegate_all #ここにメソッドを書く end
Modelがファットにならないようにするために使う。
Draperを使えばよりDRYに、よりオブジェクト志向的に書ける。
参考サイト
GitHub - drapergem/draper: Decorators/View-Models for Rails Applications
Decorator と Presenter を使い分けて、 Rails を ViewModel ですっきりさせよう - KitchHike Tech Blog
Decoratorの役割とDraperについて - Qiita