diff --git a/Gemfile b/Gemfile index 865423c22..b95deb19b 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,7 @@ end gemspec gem "base64" +gem "lru_redux" group :benchmark, :test do gem 'benchmark-ips' @@ -28,5 +29,3 @@ group :test do gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'main' end end - -gem "lru_redux" diff --git a/lib/liquid/expression.rb b/lib/liquid/expression.rb index 8eb1f2c32..5b3f0cd18 100644 --- a/lib/liquid/expression.rb +++ b/lib/liquid/expression.rb @@ -89,7 +89,9 @@ def parse(markup, ss = StringScanner.new(""), cache = nil) # Cache only exists during parsing if cache - cache.fetch(markup) { inner_parse(markup, ss, cache).freeze } + return cache[markup] if cache.key?(markup) + + cache[markup] = inner_parse(markup, ss, cache).freeze else inner_parse(markup, ss, nil).freeze end diff --git a/lib/liquid/parse_context.rb b/lib/liquid/parse_context.rb index 5eebd0459..5fc422810 100644 --- a/lib/liquid/parse_context.rb +++ b/lib/liquid/parse_context.rb @@ -15,7 +15,7 @@ def initialize(options = Const::EMPTY_HASH) # constructing new StringScanner in Lexer, Tokenizer, etc is expensive # This StringScanner will be shared by all of them @string_scanner = StringScanner.new("") - @expression_cache = LruRedux::Cache.new(10_000) + @expression_cache = options[:expression_cache] || LruRedux::Cache.new(10_000) self.depth = 0 self.partial = false diff --git a/test/integration/expression_test.rb b/test/integration/expression_test.rb index 6d29fc792..d79cde029 100644 --- a/test/integration/expression_test.rb +++ b/test/integration/expression_test.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'test_helper' +require 'lru_redux' class ExpressionTest < Minitest::Test def test_keyword_literals @@ -54,6 +55,27 @@ def test_quirky_negative_sign_expression_markup ) end + def test_expression_cache + skip("Liquid-C does not support Expression caching") if defined?(Liquid::C) && Liquid::C.enabled + + cache = LruRedux::Cache.new(10) + template = <<~LIQUID + {% assign x = 1 %} + {{ x }} + {% assign x = 2 %} + {{ x }} + {% assign y = 1 %} + {{ y }} + LIQUID + + Liquid::Template.parse(template, expression_cache: cache).render + + assert_equal( + ["1", "2", "x", "y"], + cache.to_a.map { _1[0] }.sort, + ) + end + private def assert_expression_result(expect, markup, **assigns)