From cb5e2c640e927b3735ce3aa793c76ffd95b5fedb Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Tue, 26 May 2026 01:37:08 -0700 Subject: [PATCH] fix: allow Template/Environment/ParseContext to be serialized with Marshal Signed-off-by: Sai Asish Y --- lib/liquid/environment.rb | 13 +++++++++++++ lib/liquid/parse_context.rb | 9 +++++++++ test/unit/template_unit_test.rb | 22 ++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/lib/liquid/environment.rb b/lib/liquid/environment.rb index 100bd0bbd..21bbd25b5 100644 --- a/lib/liquid/environment.rb +++ b/lib/liquid/environment.rb @@ -155,5 +155,18 @@ def freeze # @strainer_template.freeze super end + + def marshal_dump + filter_modules = @strainer_template.ancestors[1...@strainer_template.ancestors.index(StrainerTemplate)] + [@tags, @error_mode, @file_system, @default_resource_limits, filter_modules] + end + + def marshal_load(data) + @tags, @error_mode, @file_system, @default_resource_limits, filter_modules = data + @exception_renderer = ->(exception) { exception } + @strainer_template = Class.new(StrainerTemplate) + filter_modules.reverse_each { |m| @strainer_template.add_filter(m) } + @strainer_template_class_cache = {} + end end end diff --git a/lib/liquid/parse_context.rb b/lib/liquid/parse_context.rb index d956d1c61..656ce6875 100644 --- a/lib/liquid/parse_context.rb +++ b/lib/liquid/parse_context.rb @@ -88,5 +88,14 @@ def partial_options end end end + + def marshal_dump + instance_variables.reject { |v| v == :@string_scanner }.map { |v| [v, instance_variable_get(v)] }.to_h + end + + def marshal_load(data) + data.each { |k, v| instance_variable_set(k, v) } + @string_scanner = StringScanner.new("") + end end end diff --git a/test/unit/template_unit_test.rb b/test/unit/template_unit_test.rb index 1f349657a..1173012db 100644 --- a/test/unit/template_unit_test.rb +++ b/test/unit/template_unit_test.rb @@ -46,4 +46,26 @@ def test_invalid_utf8 error.message, ) end + + module UpFilter + def up(input) = input.upcase + end + + def test_marshal_roundtrip + env = Liquid::Environment.build { |e| e.register_filter(UpFilter) } + t = Template.parse("Hello {{ name | up }}", environment: env) + t2 = Marshal.load(Marshal.dump(t)) + assert_equal("Hello WORLD", t2.render("name" => "world")) + end + + module ShoutFilter + def shout(input) = input.upcase + "!" + end + + def test_marshal_roundtrip_with_custom_filter + env = Liquid::Environment.build { |e| e.register_filter(ShoutFilter) } + t = Template.parse("{{ name | shout }}", environment: env) + t2 = Marshal.load(Marshal.dump(t)) + assert_equal("WORLD!", t2.render("name" => "world")) + end end