From ff570c3ddcca3377b6569cf33421d7a66b079ebb Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Wed, 31 Jul 2013 17:41:02 -0400 Subject: [PATCH 1/4] Fix clashing method names in enumerable drops --- lib/liquid/drop.rb | 11 ++++++++++- test/liquid/drop_test.rb | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/lib/liquid/drop.rb b/lib/liquid/drop.rb index e8eb233..2ad33c8 100644 --- a/lib/liquid/drop.rb +++ b/lib/liquid/drop.rb @@ -54,7 +54,16 @@ module Liquid # Check for method existence without invoking respond_to?, which creates symbols def self.invokable?(method_name) - @invokable_methods ||= Set.new(["to_liquid"] + (public_instance_methods - Liquid::Drop.public_instance_methods).map(&:to_s)) + unless @invokable_methods + blacklist = Liquid::Drop.public_instance_methods + [:each] + if include?(Enumerable) + blacklist += Enumerable.public_instance_methods + blacklist -= [:sort, :count, :first, :min, :max] + end + # Ruby 1.8 compatibility: call to_s on method names (which are strings in 1.8, but already symbols in 1.9) + whitelist = [:to_liquid] + (public_instance_methods.map(&:to_s) - blacklist.map(&:to_s)) + @invokable_methods = Set.new(whitelist.map(&:to_s)) + end @invokable_methods.include?(method_name.to_s) end end diff --git a/test/liquid/drop_test.rb b/test/liquid/drop_test.rb index fc972aa..04290cc 100644 --- a/test/liquid/drop_test.rb +++ b/test/liquid/drop_test.rb @@ -55,6 +55,9 @@ class ProductDrop < Liquid::Drop end class EnumerableDrop < Liquid::Drop + def before_method(method) + method + end def size 3 @@ -67,6 +70,20 @@ class EnumerableDrop < Liquid::Drop end end +class RealEnumerableDrop < Liquid::Drop + include Enumerable + + def before_method(method) + method + end + + def each + yield 1 + yield 2 + yield 3 + end +end + class DropsTest < Test::Unit::TestCase include Liquid @@ -170,6 +187,27 @@ class DropsTest < Test::Unit::TestCase assert_equal '3', Liquid::Template.parse( '{{collection.size}}').render('collection' => EnumerableDrop.new) end + def test_enumerable_drop_will_invoke_before_method_for_clashing_method_names + ["select", "each", "map", "cycle"].each do |method| + assert_equal method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render('collection' => EnumerableDrop.new) + assert_equal method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render('collection' => EnumerableDrop.new) + assert_equal method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render('collection' => RealEnumerableDrop.new) + assert_equal method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render('collection' => RealEnumerableDrop.new) + end + end + + def test_some_enumerable_methods_still_get_invoked + [ :count, :max ].each do |method| + assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render('collection' => RealEnumerableDrop.new) + assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render('collection' => RealEnumerableDrop.new) + end + + [ :min, :first ].each do |method| + assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render('collection' => RealEnumerableDrop.new) + assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render('collection' => RealEnumerableDrop.new) + end + end + def test_empty_string_value_access assert_equal '', Liquid::Template.parse('{{ product[value] }}').render('product' => ProductDrop.new, 'value' => '') end From c92efd3ab9951b6ba7be3f1f52a9db930fba876b Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Thu, 1 Aug 2013 13:46:55 -0400 Subject: [PATCH 2/4] Update some Drop tests --- test/liquid/drop_test.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/liquid/drop_test.rb b/test/liquid/drop_test.rb index 04290cc..4df54d5 100644 --- a/test/liquid/drop_test.rb +++ b/test/liquid/drop_test.rb @@ -63,6 +63,22 @@ class EnumerableDrop < Liquid::Drop 3 end + def first + 1 + end + + def count + 3 + end + + def min + 1 + end + + def max + 3 + end + def each yield 1 yield 2 @@ -200,11 +216,15 @@ class DropsTest < Test::Unit::TestCase [ :count, :max ].each do |method| assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render('collection' => RealEnumerableDrop.new) assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render('collection' => RealEnumerableDrop.new) + assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render('collection' => EnumerableDrop.new) + assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render('collection' => EnumerableDrop.new) end [ :min, :first ].each do |method| assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render('collection' => RealEnumerableDrop.new) assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render('collection' => RealEnumerableDrop.new) + assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render('collection' => EnumerableDrop.new) + assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render('collection' => EnumerableDrop.new) end end From 7fdb789eac6ff86fc999303f9b634c05151c6cef Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Thu, 1 Aug 2013 13:56:01 -0400 Subject: [PATCH 3/4] Ruby 1.8.x compatibility --- lib/liquid/drop.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/liquid/drop.rb b/lib/liquid/drop.rb index 2ad33c8..bac1b3a 100644 --- a/lib/liquid/drop.rb +++ b/lib/liquid/drop.rb @@ -55,12 +55,12 @@ module Liquid # Check for method existence without invoking respond_to?, which creates symbols def self.invokable?(method_name) unless @invokable_methods - blacklist = Liquid::Drop.public_instance_methods + [:each] - if include?(Enumerable) - blacklist += Enumerable.public_instance_methods - blacklist -= [:sort, :count, :first, :min, :max] - end # Ruby 1.8 compatibility: call to_s on method names (which are strings in 1.8, but already symbols in 1.9) + blacklist = (Liquid::Drop.public_instance_methods + [:each]).map(&:to_s) + if include?(Enumerable) + blacklist += Enumerable.public_instance_methods.map(&:to_s) + blacklist -= [:sort, :count, :first, :min, :max].map(&:to_s) + end whitelist = [:to_liquid] + (public_instance_methods.map(&:to_s) - blacklist.map(&:to_s)) @invokable_methods = Set.new(whitelist.map(&:to_s)) end From f98949117df9bea291a37794aabba20f23e22fa5 Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Fri, 2 Aug 2013 10:23:10 -0400 Subject: [PATCH 4/4] Fix .include? method on Enumerable drops, used by "contains" conditions --- lib/liquid/drop.rb | 2 +- test/liquid/drop_test.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/liquid/drop.rb b/lib/liquid/drop.rb index bac1b3a..a1b66a4 100644 --- a/lib/liquid/drop.rb +++ b/lib/liquid/drop.rb @@ -59,7 +59,7 @@ module Liquid blacklist = (Liquid::Drop.public_instance_methods + [:each]).map(&:to_s) if include?(Enumerable) blacklist += Enumerable.public_instance_methods.map(&:to_s) - blacklist -= [:sort, :count, :first, :min, :max].map(&:to_s) + blacklist -= [:sort, :count, :first, :min, :max, :include?].map(&:to_s) end whitelist = [:to_liquid] + (public_instance_methods.map(&:to_s) - blacklist.map(&:to_s)) @invokable_methods = Set.new(whitelist.map(&:to_s)) diff --git a/test/liquid/drop_test.rb b/test/liquid/drop_test.rb index 4df54d5..d8d02d9 100644 --- a/test/liquid/drop_test.rb +++ b/test/liquid/drop_test.rb @@ -220,6 +220,8 @@ class DropsTest < Test::Unit::TestCase assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render('collection' => EnumerableDrop.new) end + assert_equal "yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render('collection' => RealEnumerableDrop.new) + [ :min, :first ].each do |method| assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render('collection' => RealEnumerableDrop.new) assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render('collection' => RealEnumerableDrop.new)