diff --git a/lib/liquid/tags/include.rb b/lib/liquid/tags/include.rb index f7400abd..b109bf3e 100644 --- a/lib/liquid/tags/include.rb +++ b/lib/liquid/tags/include.rb @@ -2,10 +2,10 @@ module Liquid class Include < Tag Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o - def initialize(tag_name, markup, tokens) + def initialize(tag_name, markup, tokens) if markup =~ Syntax - @template_name = $1 + @template_name = $1 @variable_name = $3 @attributes = {} @@ -24,8 +24,7 @@ module Liquid end def render(context) - source = _read_template_from_file_system(context) - partial = Liquid::Template.parse(source) + partial = load_cached_partial(context) variable = context[@variable_name || @template_name[1..-2]] context.stack do @@ -46,7 +45,21 @@ module Liquid end private - def _read_template_from_file_system(context) + def load_cached_partial(context) + cached_partials = context.registers[:cached_partials] || {} + template_name = context[@template_name] + + if cached = cached_partials[template_name] + return cached + end + source = read_template_from_file_system(context) + partial = Liquid::Template.parse(source) + cached_partials[template_name] = partial + context.registers[:cached_partials] = cached_partials + partial + end + + def read_template_from_file_system(context) file_system = context.registers[:file_system] || Liquid::Template.file_system # make read_template_file call backwards-compatible. diff --git a/performance/theme_runner.rb b/performance/theme_runner.rb index 98406b3c..802411a9 100644 --- a/performance/theme_runner.rb +++ b/performance/theme_runner.rb @@ -14,6 +14,17 @@ require File.dirname(__FILE__) + '/shopify/liquid' require File.dirname(__FILE__) + '/shopify/database.rb' class ThemeRunner + class FileSystem + + def initialize(path) + @path = path + end + + # Called by Liquid to retrieve a template file + def read_template_file(template_path, context) + File.read(@path + '/' + template_path + '.liquid') + end + end # Load all templates into memory, do this now so that # we don't profile IO. @@ -47,7 +58,7 @@ class ThemeRunner # Compute page_tempalte outside of profiler run, uninteresting to profiler page_template = File.basename(template_name, File.extname(template_name)) - compile_and_render(liquid, layout, assigns, page_template) + compile_and_render(liquid, layout, assigns, page_template, template_name) end end @@ -74,7 +85,7 @@ class ThemeRunner html = nil RubyProf.resume - html = compile_and_render(liquid, layout, assigns, page_template) + html = compile_and_render(liquid, layout, assigns, page_template, template_name) RubyProf.pause @@ -88,10 +99,11 @@ class ThemeRunner RubyProf.stop end - def compile_and_render(template, layout, assigns, page_template) + def compile_and_render(template, layout, assigns, page_template, template_file) tmpl = Liquid::Template.new tmpl.assigns['page_title'] = 'Page title' tmpl.assigns['template'] = page_template + tmpl.registers[:file_system] = ThemeRunner::FileSystem.new(File.dirname(template_file)) content_for_layout = tmpl.parse(template).render(assigns) diff --git a/test/liquid/tags/include_tag_test.rb b/test/liquid/tags/include_tag_test.rb index 101dd7bc..8bdb19c8 100644 --- a/test/liquid/tags/include_tag_test.rb +++ b/test/liquid/tags/include_tag_test.rb @@ -39,6 +39,15 @@ class OtherFileSystem end end +class CountingFileSystem + attr_reader :count + def read_template_file(template_path, context) + @count ||= 0 + @count += 1 + 'from CountingFileSystem' + end +end + class IncludeTagTest < Test::Unit::TestCase include Liquid @@ -136,4 +145,22 @@ class IncludeTagTest < Test::Unit::TestCase assert_equal "Product: Draft 151cm ", Template.parse("{% include template for product %}").render("template" => 'product', 'product' => { 'title' => 'Draft 151cm'}) end -end # IncludeTagTest \ No newline at end of file + + def test_include_tag_caches_second_read_of_same_partial + file_system = CountingFileSystem.new + assert_equal 'from CountingFileSystemfrom CountingFileSystem', + Template.parse("{% include 'pick_a_source' %}{% include 'pick_a_source' %}").render({}, :registers => {:file_system => file_system}) + assert_equal 1, file_system.count + end + + def test_include_tag_doesnt_cache_partials_across_renders + file_system = CountingFileSystem.new + assert_equal 'from CountingFileSystem', + Template.parse("{% include 'pick_a_source' %}").render({}, :registers => {:file_system => file_system}) + assert_equal 1, file_system.count + + assert_equal 'from CountingFileSystem', + Template.parse("{% include 'pick_a_source' %}").render({}, :registers => {:file_system => file_system}) + assert_equal 2, file_system.count + end +end # IncludeTagTest