3
0

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 5 years have passed since last update.

Ruby on RailsAdvent Calendar 2018

Day 5

HTML生成用のコントローラーもどきを作る

Last updated at Posted at 2018-12-04

Railsのシステムを作っていて、コントローラーからPDFKitに流してPDFを生成していたのですが、あるときそれをメールに添付する必要が出てきました。ActionMailerで二重に実装するのもアホらしいし、かといって普通のコントローラーに再アクセスさせるのも認証の加減で厄介になるしといろいろ考えた挙げ句、コードで駆動する、コントローラーのようなものを作ることで対応することにしました。

おことわり

これはRails 4.2で実践してみたものなので、他のバージョンでは書き方が違う可能性があります。

直接ERBでの問題点

もちろん、コントローラーと無関係にERBを呼ぶことでHTMLを生成することも可能です…が、それではコントローラーのビューと比べて機能的に足りないことだらけでした。

  • ヘルパーが使えない
  • 渡す値の取りまとめが煩雑になる
  • アセットなどの位置が取れない

ということで、ある程度以上複雑なビューを組み立てるには、コントローラーと同じ枠組みに載せるしかなさそうです。

既存のものを参考にする

コントローラー以外で同様にビューから生成できるものとして、ActionMailerがあります。それを見ていると、AbstractController::Baseを基底クラスにして、各種のモジュールを加えてメーラーを構築していました。これに則れば、組み立てていけそうです。

実際に書いてみたコード

試行錯誤しつつ、足りないモジュールを足していったところ、以下のようなコードが出来上がりました。

class Renderrers::Base < AbstractController::Base
  abstract!

  include Rails.application.routes.url_helpers
  include AbstractController::Rendering
  include AbstractController::Logger
  include AbstractController::Helpers
  include AbstractController::AssetPaths
  include AbstractController::Callbacks

  include ActionView::Layouts

  helper :application

  class_attribute :template

  # パス設定のコピー
  %i|asset_host assets_dir|.each do |sym|
    __send__(:"#{sym}=", ActionController::Base.public_send(sym))
  end

  # URL設定のコピー
  class << self
    delegate :default_url_options, to: :ApplicationMailer
  end

  def render
    run_callbacks(:process_action) do
      html = render_to_string(template, layout: 'pdf')
      PDFKit.new(html).to_pdf
    end
  end

  # ビューの処理に必要となる情報
  def action_name
    'print'
  end

  def controller_name
    self.class.name.to_s.demodulize.underscore
  end
end

引っかかった点としては、

  • アセットの設定やURL設定は、他所から借りてくる必要がありました。
  • コールバックを動作させるために、run_callbacksの中でrender_to_stringする必要がありました。
  • ここでは省略していますが、active_decoratorのような、ActionControllerに追加のコードを組み込むgemを使っている場合、そのモンキーパッチ用コードも入れる必要があります。

なお、コントローラーと同じく1回renderするために1インスタンスが必要となりますので、都度newする必要があります。

3
0
0

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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?