diff --git a/lib/liquid/utils.rb b/lib/liquid/utils.rb index e7d2a4e32..a75b9fa37 100644 --- a/lib/liquid/utils.rb +++ b/lib/liquid/utils.rb @@ -90,34 +90,38 @@ def self.to_liquid_value(obj) obj end - if RUBY_VERSION >= '3.4' - def self.to_s(obj, seen = {}) - case obj - when Hash + def self.to_s(obj, seen = {}) + case obj + when Hash + # If the custom hash implementation overrides `#to_s`, use their + # custom implementation. Otherwise we use Liquid's default + # implementation. + if obj.class.instance_method(:to_s) == HASH_TO_S_METHOD hash_inspect(obj, seen) - when Array - array_inspect(obj, seen) else obj.to_s end + when Array + array_inspect(obj, seen) + else + obj.to_s end + end - def self.inspect(obj, seen = {}) - case obj - when Hash + def self.inspect(obj, seen = {}) + case obj + when Hash + # If the custom hash implementation overrides `#inspect`, use their + # custom implementation. Otherwise we use Liquid's default + # implementation. + if obj.class.instance_method(:inspect) == HASH_INSPECT_METHOD hash_inspect(obj, seen) - when Array - array_inspect(obj, seen) else obj.inspect end - end - else - def self.to_s(obj, seen = nil) - obj.to_s - end - - def self.inspect(obj, seen = nil) + when Array + array_inspect(obj, seen) + else obj.inspect end end @@ -175,5 +179,11 @@ def self.hash_inspect(hash, seen = {}) ensure seen.delete(hash.object_id) end + + HASH_TO_S_METHOD = Hash.instance_method(:to_s) + private_constant :HASH_TO_S_METHOD + + HASH_INSPECT_METHOD = Hash.instance_method(:inspect) + private_constant :HASH_INSPECT_METHOD end end diff --git a/test/integration/hash_rendering_test.rb b/test/integration/hash_rendering_test.rb index c465208fd..95df4f320 100644 --- a/test/integration/hash_rendering_test.rb +++ b/test/integration/hash_rendering_test.rb @@ -80,4 +80,27 @@ def test_render_hash_with_array_values_hash def test_render_hash_with_hash_key assert_template_result("{{\"foo\"=>\"bar\"}=>42}", "{{ my_hash }}", { "my_hash" => { Hash["foo" => "bar"] => 42 } }) end + + def test_join_filter_with_hash + array = [{ "key1" => "value1" }, { "key2" => "value2" }] + glue = { "lol" => "wut" } + assert_template_result("{\"key1\"=>\"value1\"}{\"lol\"=>\"wut\"}{\"key2\"=>\"value2\"}", "{{ my_array | join: glue }}", { "my_array" => array, "glue" => glue }) + end + + def test_rendering_hash_with_custom_to_s_method_uses_custom_to_s + my_hash = Class.new(Hash) do + def to_s + "kewl" + end + end.new + + assert_template_result("kewl", "{{ my_hash }}", { "my_hash" => my_hash }) + end + + def test_rendering_hash_without_custom_to_s_uses_default_inspect + my_hash = Class.new(Hash).new + my_hash[:foo] = :bar + + assert_template_result("{:foo=>:bar}", "{{ my_hash }}", { "my_hash" => my_hash }) + end end