Skip to content

Commit

Permalink
Fix duplicate view template loading
Browse files Browse the repository at this point in the history
Currently, when rendering an ActionView::Template, the template source
is loaded from the disk twice.

During one rendering process, Template#source is called at least twice
(once in encode! and once in strict_locals!), each of these calls runs
@source.to_s. As @source is usually a Template::Sources::File, #to_s
hits the disk to load the template.

It is unnecessary to load the file from disk more than once just to
check whether the "# locals:" comment is present.
  • Loading branch information
nhasselmeyer committed May 2, 2024
1 parent 26e53f4 commit 2ed2f23
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 4 deletions.
9 changes: 5 additions & 4 deletions actionview/lib/action_view/template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ def mime_types_implementation=(implementation)
NONE = Object.new

def initialize(source, identifier, handler, locals:, format: nil, variant: nil, virtual_path: nil)
@source = source.dup
@_source = source.dup
@source = nil
@identifier = identifier
@handler = handler
@compiled = false
Expand Down Expand Up @@ -293,7 +294,7 @@ def inspect
end

def source
@source.to_s
@source ||= @_source.to_s.dup
end

LEADING_ENCODING_REGEXP = /\A#{ENCODING_FLAG}/
Expand Down Expand Up @@ -376,11 +377,11 @@ def strict_locals?
# to ensure that references to the template object can be marshalled as well. This means forgoing
# the marshalling of the compiler mutex and instantiating that again on unmarshalling.
def marshal_dump # :nodoc:
[ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @format, @variant ]
[ @_source, @identifier, @handler, @compiled, @locals, @virtual_path, @format, @variant ]
end

def marshal_load(array) # :nodoc:
@source, @identifier, @handler, @compiled, @locals, @virtual_path, @format, @variant = *array
@_source, @identifier, @handler, @compiled, @locals, @virtual_path, @format, @variant = *array
@compile_mutex = Mutex.new
end

Expand Down
10 changes: 10 additions & 0 deletions actionview/test/template/template_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ def test_template_does_not_lose_its_source_after_rendering_if_it_does_not_have_a
assert_equal "Hello", @template.source
end

def test_template_only_loads_template_source_once
source_loaded = 0
source = Class.new do |klass|
klass.define_method(:to_s) { source_loaded += 1; "hello" }
end.new
@template = new_template(source)
render
assert_equal 1, source_loaded
end

def test_locals
@template = new_template("<%= my_local %>", locals: [:my_local])
assert_equal "I am a local", render(my_local: "I am a local")
Expand Down

0 comments on commit 2ed2f23

Please sign in to comment.