Fix rendering Action Text HTML in new threads

Because `ActionText::Content.renderer` is implemented as a
`thread_cattr_accessor`, any default value set in the main thread will
be inaccessible from other threads.  Therefore, use a `cattr_accessor`
to store the default renderer, and fall back to it when `renderer` has
not been set by e.g. `with_renderer`.

Fixes #40757.
This commit is contained in:
Jonathan Hefner 2020-12-07 15:41:44 -06:00 committed by GitHub
parent 9b6008924d
commit ef61c9c8a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 12 additions and 2 deletions

View File

@ -46,7 +46,7 @@ module ActionText
initializer "action_text.renderer" do
ActiveSupport.on_load(:action_text_content) do
self.renderer = Class.new(ActionController::Base).renderer
self.default_renderer = Class.new(ActionController::Base).renderer
end
%i[action_controller_base action_mailer].each do |abstract_controller|

View File

@ -8,6 +8,7 @@ module ActionText
extend ActiveSupport::Concern
included do
cattr_accessor :default_renderer, instance_accessor: false
thread_cattr_accessor :renderer, instance_accessor: false
delegate :render, to: :class
end
@ -22,7 +23,7 @@ module ActionText
end
def render(*args, &block)
renderer.render_to_string(*args, &block)
(renderer || default_renderer).render_to_string(*args, &block)
end
end
end

View File

@ -116,6 +116,15 @@ class ActionText::ContentTest < ActiveSupport::TestCase
assert_not defined?(::ApplicationController)
end
test "renders with layout when in a new thread" do
html = "<h1>Hello world</h1>"
rendered = nil
Thread.new { rendered = content_from_html(html).to_rendered_html_with_layout }.join
assert_includes rendered, html
assert_match %r/\A#{Regexp.escape '<div class="trix-content">'}/, rendered
end
private
def content_from_html(html)
ActionText::Content.new(html).tap do |content|