web-dev-qa-db-ja.com

パーシャルのレンダリング/レーキタスクでの表示/バックグラウンドジョブ/モデルでのRails 4

Railsのパーシャルとビューをrakeタスク/バックグラウンドジョブ/モデルでレンダリングする方法についてたくさん読みました。 StackoverflowとWebで見つけたものの大部分は、Rails 3で機能するアプローチについて説明していますが、それらは時代遅れのようで、(実験にかなりの時間を費やしても)機能しませんでした。

では、どうすればよいでしょうかRails 4のバックグラウンドジョブでパーシャルをレンダリングしますか?

これが私がこれまでに考え出した最良のアプローチです(コンソールに示されています)。

c = ApplicationController.new
result = c.render_to_string(partial: 'tweets/Tweet', locals: {Tweet: Tweet.first})
# =>
#   Tweet Load (0.8ms)  SELECT "tweets".* FROM "tweets" ORDER BY "tweets"."id" ASC LIMIT 1
#   Author Load (0.6ms)  SELECT "authors".* FROM "authors" WHERE "authors"."id" = $1 ORDER BY "authors"."id" ASC LIMIT 1  [["id", 1]]
#   Status Load (0.6ms)  SELECT "statuses".* FROM "statuses" WHERE "statuses"."Twitter_id" = 367523226848866304 LIMIT 1
#   Rendered tweets/_Tweet_body.html.slim (17.5ms)
#   Rendered tweets/_resolved_Tweet.html.slim (23.7ms)
#   Rendered tweets/_Tweet.html.slim (28.1ms)
# ActionView::Template::Error: undefined method `Tweet_path' for #<#<Class:0x007fb21bf797a0>:0x007fb21cb009e8>
# from /Users/thomasklemm/.rbenv/versions/2.0.0-p195/lib/Ruby/gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/routing/polymorphic_routes.rb:129:in `polymorphic_url'

何か案は?前もって感謝します!

Update:上記のTweet_pathは実際には定義されていません。このエラーは、特定のコントローラーから継承するビューに存在するインスタンス変数を使用してパス= link_to 'Tweet', [@project, Tweet](スリムテンプレート)にリンクすることで発生しましたが、このコンテキストの外部でレンダリングされた場合は発生しません。代わりに= link_to 'Tweet', [Tweet.project, Tweet]の適切な関連付けを介してこれを解決しました。

24
Thomas Klemm

これが私がたくさんのソースからコンパイルしたものであり、Rails 4で私のために働くものです。

このレンダラークラスを使用すると、バックグラウンドジョブ、サービスオブジェクト、モデル、ワーカーなど、任意のコンテキストでRails 4つのビューとパーシャルをレンダリングできるはずです。

# app/services/renderer.rb
# Render views and partials in rake tasks,
# background workers, service objects and more
#
# Use:
#
# class MyService
#   def render_stuff
#     result = renderer.render(partial: 'tweets/Tweet', locals: {Tweet: Tweet.first})
#     # or even
#     result = renderer.render(Tweet.first)
#   end
#
#   private
#
#   def renderer
#     @renderer ||= Renderer.new.renderer
#   end
# end
#
class Renderer
  def renderer
    controller = ApplicationController.new
    controller.request = ActionDispatch::TestRequest.new
    ViewRenderer.new(Rails.root.join('app', 'views'), {}, controller)
  end
end

# app/services/view_renderer.rb
# A helper class for Renderer
class ViewRenderer < ActionView::Base
  include Rails.application.routes.url_helpers
  include ApplicationHelper

  def default_url_options
     {Host: Rails.application.routes.default_url_options[:Host]}
  end
end

更新

より簡単な解決策があるようです: http://makandracards.com/makandra/17751-render-a-view-from-a-model-in-Rails

ApplicationController.new.render_to_string(
  :template => 'users/index',
  :locals => { :@users => @users }
)
# Mind the weird syntax to set @ variables in :locals.

アップデート2

render_anywhere というgemがあり、モデル、バックグラウンドジョブ、レーキタスクなど、どこからでも「レンダリング」を呼び出すことができます。

アップデート3

Rails 5では、レンダラーが抽出されており、バックグラウンドジョブやその他の場所からスタンドアロンで使用できます。

ApplicationController.renderer.render(
  partial: 'messages/message',
  locals: { message: message }
)

Rails <= 4.2の場合、この機能は backport_new_renderer gem でバックポートできます。

38
Thomas Klemm

ジョブにRails環境をロードしていることを確認してください。それがすでに完了している場合は、次のようなことを試すことができます。

include Rails.application.routes.url_helpers
3
Hderms