From cc6cd64065aa169260deb4ac43c932533e9fbaf6 Mon Sep 17 00:00:00 2001 From: Sam Doiron Date: Tue, 1 Nov 2022 14:40:36 -0300 Subject: [PATCH] Special case Hash#last in variable lookup Ruby's defines Hash#first via Enumerable but not Hash#last. This is a confusing behaviour, and it bubbles up to Liquid. Solution: add a special case for command-lookups of Hash#last to return hash.values.last. --- lib/liquid/variable_lookup.rb | 6 +++++- test/integration/variable_test.rb | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/liquid/variable_lookup.rb b/lib/liquid/variable_lookup.rb index 1ade30ef..83c5e8db 100644 --- a/lib/liquid/variable_lookup.rb +++ b/lib/liquid/variable_lookup.rb @@ -61,11 +61,15 @@ module Liquid # as commands and call them on the current object elsif lookup_command?(i) && object.respond_to?(key) object = object.send(key).to_liquid + elsif lookup_command?(i) && object.is_a?(Hash) && key == 'last' + # Ruby defines Hash#first via Enumerable but not Hash#last. + # This is unexpected, so we explicitly handle that case here. + object = object.values.last.to_liquid + else # No key was present with the desired value and it wasn't one of the directly supported # keywords either. The only thing we got left is to return nil or # raise an exception if `strict_variables` option is set to true - else return nil unless context.strict_variables raise Liquid::UndefinedVariable, "undefined variable #{key}" end diff --git a/test/integration/variable_test.rb b/test/integration/variable_test.rb index c1fe16f7..263b3145 100644 --- a/test/integration/variable_test.rb +++ b/test/integration/variable_test.rb @@ -130,4 +130,8 @@ class VariableTest < Minitest::Test def test_raw_value_variable assert_template_result('bar', '{{ [key] }}', { 'key' => 'foo', 'foo' => 'bar' }) end + + def test_hash_last + assert_template_result('last', '{{ hash.last }}', { 'hash' => { 'first' => 'first', 'last' => 'last' } }) + end end