24
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Rails 6.1] ActionView::Component について

Last updated at Posted at 2019-06-19

Rails の github レポジトリを見ていたら
Introduce support for ActionView::Component( https://github.com/rails/rails/pull/36388
というプルリクを見つけてなかなか面白そうだったので読んでみました。

ActionView::Component を一言でまとめると、ビューコンポーネントを Rails のオブジェクトとして扱えるようにする、というもの。
3月から既に Github のプロダクション環境で使われているとか。

RailsConf でも発表された内容のようです。
https://www.youtube.com/watch?v=y5Z5a6QdA-M

以下、プルリクの内容をかいつまんで翻訳したものです。

背景

Rails アプリのビュー開発において次のような問題があった。

テスト

Rails ではビューのテストは統合テストかシステムテストが推奨されているが、これだとコストが大きいのですべてのビューをテストするというわけにもいかない。しかもこのテストの仕方だと、せっかくパーシャルに切り分けて共通化しても、テストは DRY じゃなくなって嬉しくない。

カバレッジ

テストのカバー率を求める一般的なツールではビューのカバー率を完全には判断できないのでテストがどれくらい書かれているか判断しづらいし、カバー率に実態とのギャップが生まれる。

データの流れ

オブジェクトの宣言と違い、ビューでは予期する値についての情報が無いので、そのビューが何をするものなのか理解が難しい。この問題は同じビューを異なる場所で使おうとしたときによく起きる。

基準

Ruby のコーディング基準のようなものをビューでは設定しづらい。

実装

コンポーネントの構築

  • app/components 配下に ActionView::Component の子クラスとして定義する。
  • クラスメソッドとして template メソッドを定義し、 ERB をヒアドキュメントで書く。
  • initialize も定義できるが、その他はすべてプライベートメソッドとする。
  • バリデータが使える。initialize 後、render 前にバリデーションがかかる。
class TestComponent < ActionView::Component
  validates :content, :title, presence: true

  def initialize(title:)
    @title = title
  end

  def self.template
    <<~'erb'
    <span title="<%= title %>"><%= content %></span>
    erb
  end

  private

  attr_reader :title
end

コンポーネントの呼び出し

  • ERB 内で TestComponent.new(title: "my title") などとしてインスタンス化し、render に渡す。
  • ブロック引数を与えると、コンポーネント内で content として扱うことができる。
<%= render(TestComponent.new(title: "my title")) do %>
  Hello, World!
<% end %>

これは次のような HTML を返す。

<span title="my title">Hello, World!</span>

エラー

上の例では title に対して presence: true しているので、title が空だと怒られる。

<%= render(TestComponent.new(title: "")) do %>
  Hello, World!
<% end %>
ActiveModel::ValidationError: Validation failed: Title can't be blank

テスト

コンポーネントはユニットテストできて、テストヘルパーとして render_component が用意されている。

def test_render_component
  assert_equal(
    %(<span title="my title">Hello, World!</span>),
    render_component(TestComponent.new(title: "my title")) { "Hello, World!" }.css("span").to_html
  )
end

利点

テスト

ActionView::Component によってビューがユニットテスト可能になる。1テストあたり25ミリ秒で終わるのに対し、統合テストだと最大で6秒かかる。

カバレッジ

ActionView::Component は一部のコードカバレッジツールで互換性がある。SimpleCov ではある程度うまくいっているっぽい。

データフロー

ビューのレンダリングに必要なコンテキストを定義できるので、 partial よりも再利用しやすい。

パフォーマンス

ベンチマークテストにおいて、 componentpartial よりも5倍の性能を発揮した。

Comparison:
           component:     6515.4 i/s
             partial:     1251.2 i/s - 5.21x  slower

こちらのデモレポジトリで試せる。
https://github.com/joelhawksley/actionview-component-demo


引用ここまで。

まとめ

ActionView::Component を使うと切り出したビューをオブジェクトとして定義でるようになります。
それによって、変数の検証が可能、ユニットテストができる、さらに partial よりも高速、といった恩恵を受けられます。
ちなみにこの内容は Rails 6.1 から使えるようになりそうです。

24
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?