diff --git a/.ruby-version b/.ruby-version index 9c25013db..47b322c97 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.3.6 +3.4.1 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..39933c553 100644 --- a/test/integration/hash_rendering_test.rb +++ b/test/integration/hash_rendering_test.rb @@ -80,4 +80,21 @@ 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_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