From 42d822bda9cb7b69273cc69aa18ad7a6bdd2f918 Mon Sep 17 00:00:00 2001 From: Michael Go Date: Mon, 25 Nov 2024 17:01:49 -0400 Subject: [PATCH] users can provide optional expression cache --- Gemfile | 3 +-- lib/liquid/expression.rb | 4 ++- lib/liquid/parse_context.rb | 5 +++- test/integration/expression_test.rb | 39 +++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index e74d19ec..6802722c 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,7 @@ end gemspec gem "base64" +gem "lru_redux" group :benchmark, :test do gem 'benchmark-ips' @@ -24,5 +25,3 @@ group :test do gem 'rubocop-shopify', '~> 2.12.0', require: false gem 'rubocop-performance', require: false end - -gem "lru_redux" diff --git a/lib/liquid/expression.rb b/lib/liquid/expression.rb index 8eb1f2c3..5b3f0cd1 100644 --- a/lib/liquid/expression.rb +++ b/lib/liquid/expression.rb @@ -89,7 +89,9 @@ module Liquid # 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 5eebd045..03052972 100644 --- a/lib/liquid/parse_context.rb +++ b/lib/liquid/parse_context.rb @@ -15,7 +15,10 @@ module Liquid # 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 = if options[:expression_cache] != false + options[:expression_cache] || LruRedux::Cache.new(10_000) + end self.depth = 0 self.partial = false diff --git a/test/integration/expression_test.rb b/test/integration/expression_test.rb index 6d29fc79..190a6bec 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,44 @@ class ExpressionTest < Minitest::Test ) 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 + + def test_disable_expression_cache + skip("Liquid-C does not support Expression caching") if defined?(Liquid::C) && Liquid::C.enabled + + template = <<~LIQUID + {% assign x = 1 %} + {{ x }} + {% assign x = 2 %} + {{ x }} + {% assign y = 1 %} + {{ y }} + LIQUID + + parse_context = Liquid::ParseContext.new(expression_cache: false) + Liquid::Template.parse(template, parse_context).render + assert(parse_context.instance_variable_get(:@expression_cache).nil?) + end + private def assert_expression_result(expect, markup, **assigns)