From f9027d54abdab21a25eb4003d3b11edbe6a98cd2 Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Wed, 24 Jul 2013 14:53:10 -0400 Subject: [PATCH 01/17] Use invoke_drop in map filter --- lib/liquid/standardfilters.rb | 6 ++---- test/liquid/drop_test.rb | 12 +++++++++--- test/liquid/standard_filter_test.rb | 5 +++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index 77bb945..080fc97 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -100,10 +100,8 @@ module Liquid # map/collect on a given property def map(input, property) ary = [input].flatten - if ary.first.respond_to?('[]') and !ary.first[property].nil? - ary.map {|e| e[property] } - elsif ary.first.respond_to?(property) - ary.map {|e| e.send(property) } + ary.map do |e| + e.respond_to?('[]') ? e[property] : nil end end diff --git a/test/liquid/drop_test.rb b/test/liquid/drop_test.rb index 7819f64..235a52f 100644 --- a/test/liquid/drop_test.rb +++ b/test/liquid/drop_test.rb @@ -71,23 +71,29 @@ class DropsTest < Test::Unit::TestCase include Liquid def test_product_drop - assert_nothing_raised do tpl = Liquid::Template.parse( ' ' ) tpl.render('product' => ProductDrop.new) end end + def test_drop_does_only_respond_to_whitelisted_methods + assert_equal "", Liquid::Template.parse("{{ product.inspect }}").render('product' => ProductDrop.new) + assert_equal "", Liquid::Template.parse("{{ product.pretty_inspect }}").render('product' => ProductDrop.new) + assert_equal "", Liquid::Template.parse("{{ product.whatever }}").render('product' => ProductDrop.new) + assert_equal "", Liquid::Template.parse('{{ product | map: "inspect" }}').render('product' => ProductDrop.new) + assert_equal "", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render('product' => ProductDrop.new) + assert_equal "", Liquid::Template.parse('{{ product | map: "whatever" }}').render('product' => ProductDrop.new) + end + def test_text_drop output = Liquid::Template.parse( ' {{ product.texts.text }} ' ).render('product' => ProductDrop.new) assert_equal ' text1 ', output - end def test_unknown_method output = Liquid::Template.parse( ' {{ product.catchall.unknown }} ' ).render('product' => ProductDrop.new) assert_equal ' method: unknown ', output - end def test_integer_argument_drop diff --git a/test/liquid/standard_filter_test.rb b/test/liquid/standard_filter_test.rb index f971e72..1542139 100644 --- a/test/liquid/standard_filter_test.rb +++ b/test/liquid/standard_filter_test.rb @@ -97,6 +97,11 @@ class StandardFiltersTest < Test::Unit::TestCase 'ary' => [{'foo' => {'bar' => 'a'}}, {'foo' => {'bar' => 'b'}}, {'foo' => {'bar' => 'c'}}] end + def test_map_doesnt_call_arbitrary_stuff + assert_equal "", Liquid::Template.parse('{{ "foo" | map: "__id__" }}').render + assert_equal "", Liquid::Template.parse('{{ "foo" | map: "inspect" }}').render + end + def test_date assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B") assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B") From c7336e0cc1ff344d94e253b8534f670fa51de139 Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Wed, 24 Jul 2013 19:10:12 -0400 Subject: [PATCH 02/17] Add license to gemspec, closes #231 --- liquid.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/liquid.gemspec b/liquid.gemspec index 0e909ca..e4b401e 100644 --- a/liquid.gemspec +++ b/liquid.gemspec @@ -12,6 +12,7 @@ Gem::Specification.new do |s| s.authors = ["Tobias Luetke"] s.email = ["tobi@leetsoft.com"] s.homepage = "http://www.liquidmarkup.org" + s.license = "MIT" #s.description = "A secure, non-evaling end user template engine with aesthetic markup." s.required_rubygems_version = ">= 1.3.7" From 182d3fefb6868b1d494883acfca00b0e960b1ab6 Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Thu, 25 Jul 2013 16:56:08 -0400 Subject: [PATCH 03/17] Always call 'to_liquid' on staff in map filter and allow to_liquid to be called on drops --- lib/liquid/drop.rb | 2 +- lib/liquid/standardfilters.rb | 8 +++++++- test/liquid/drop_test.rb | 5 +++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/liquid/drop.rb b/lib/liquid/drop.rb index 88adb6b..e8eb233 100644 --- a/lib/liquid/drop.rb +++ b/lib/liquid/drop.rb @@ -54,7 +54,7 @@ module Liquid # Check for method existence without invoking respond_to?, which creates symbols def self.invokable?(method_name) - @invokable_methods ||= Set.new((public_instance_methods - Liquid::Drop.public_instance_methods).map(&:to_s)) + @invokable_methods ||= Set.new(["to_liquid"] + (public_instance_methods - Liquid::Drop.public_instance_methods).map(&:to_s)) @invokable_methods.include?(method_name.to_s) end end diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index 080fc97..f03fc3b 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -101,7 +101,13 @@ module Liquid def map(input, property) ary = [input].flatten ary.map do |e| - e.respond_to?('[]') ? e[property] : nil + if e.respond_to?(:to_liquid) + e = e.to_liquid + end + + if e.respond_to?('[]') + e[property] + end end end diff --git a/test/liquid/drop_test.rb b/test/liquid/drop_test.rb index 235a52f..fc972aa 100644 --- a/test/liquid/drop_test.rb +++ b/test/liquid/drop_test.rb @@ -86,6 +86,11 @@ class DropsTest < Test::Unit::TestCase assert_equal "", Liquid::Template.parse('{{ product | map: "whatever" }}').render('product' => ProductDrop.new) end + def test_drops_respond_to_to_liquid + assert_equal "text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render('product' => ProductDrop.new) + assert_equal "text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render('product' => ProductDrop.new) + end + def test_text_drop output = Liquid::Template.parse( ' {{ product.texts.text }} ' ).render('product' => ProductDrop.new) assert_equal ' text1 ', output From 3e13ed4ba185a12152b76b1b02abd820be864e06 Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Thu, 25 Jul 2013 22:02:48 -0400 Subject: [PATCH 04/17] Fix mapping over procs --- lib/liquid/standardfilters.rb | 9 ++++---- test/liquid/standard_filter_test.rb | 33 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index f03fc3b..3f3730a 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -101,11 +101,12 @@ module Liquid def map(input, property) ary = [input].flatten ary.map do |e| - if e.respond_to?(:to_liquid) - e = e.to_liquid - end + e = e.call if e.is_a?(Proc) + e = e.to_liquid if e.respond_to?(:to_liquid) - if e.respond_to?('[]') + if property == "to_liquid" + e + elsif e.respond_to?(:[]) e[property] end end diff --git a/test/liquid/standard_filter_test.rb b/test/liquid/standard_filter_test.rb index 1542139..a034c50 100644 --- a/test/liquid/standard_filter_test.rb +++ b/test/liquid/standard_filter_test.rb @@ -6,6 +6,27 @@ class Filters include Liquid::StandardFilters end +class TestThing + def initialize + @foo = 0 + end + + def to_s + "woot: #{@foo}" + end + + def to_liquid + @foo += 1 + self + end +end + +class TestDrop < Liquid::Drop + def test + "testfoo" + end +end + class StandardFiltersTest < Test::Unit::TestCase include Liquid @@ -102,6 +123,18 @@ class StandardFiltersTest < Test::Unit::TestCase assert_equal "", Liquid::Template.parse('{{ "foo" | map: "inspect" }}').render end + def test_map_calls_to_liquid + t = TestThing.new + assert_equal "woot: 1", Liquid::Template.parse('{{ foo }}').render("foo" => t) + end + + def test_map_over_proc + drop = TestDrop.new + p = Proc.new{ drop } + templ = '{{ procs | map: "test" }}' + assert_equal "testfoo", Liquid::Template.parse(templ).render("procs" => [p]) + end + def test_date assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B") assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B") From 554675d1f8231673ae4ce5e5f5c7b07685034805 Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Fri, 26 Jul 2013 14:46:55 +0200 Subject: [PATCH 05/17] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 124b452..8071681 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ * [Version history](History.md) * [Liquid documentation from Shopify](http://docs.shopify.com/themes/liquid-basics) * [Liquid Wiki from Shopify](http://wiki.shopify.com/Liquid) +* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki) * [Website](http://liquidmarkup.org/) ## Introduction From 65dfd57bb5fc1ed1d82158b265490d867d77f1f0 Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Thu, 25 Jul 2013 17:21:50 -0400 Subject: [PATCH 06/17] Make 'map' filter work on Enumerable drops --- lib/liquid/standardfilters.rb | 9 ++++++++- test/liquid/standard_filter_test.rb | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index 3f3730a..fa6d2a9 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -99,7 +99,14 @@ module Liquid # map/collect on a given property def map(input, property) - ary = [input].flatten + ary = if input.is_a?(Array) + input.flatten + elsif !input.class.include?(Enumerable) + [input].flatten + else + input + end + ary.map do |e| e = e.call if e.is_a?(Proc) e = e.to_liquid if e.respond_to?(:to_liquid) diff --git a/test/liquid/standard_filter_test.rb b/test/liquid/standard_filter_test.rb index a034c50..18938c3 100644 --- a/test/liquid/standard_filter_test.rb +++ b/test/liquid/standard_filter_test.rb @@ -27,6 +27,14 @@ class TestDrop < Liquid::Drop end end +class TestEnumerable < Liquid::Drop + include Enumerable + + def each(&block) + [ { "foo" => 1 }, { "foo" => 2 }, { "foo" => 3 } ].each(&block) + end +end + class StandardFiltersTest < Test::Unit::TestCase include Liquid @@ -135,6 +143,10 @@ class StandardFiltersTest < Test::Unit::TestCase assert_equal "testfoo", Liquid::Template.parse(templ).render("procs" => [p]) end + def test_map_works_on_enumerables + assert_equal "123", Liquid::Template.parse('{{ foo | map: "foo" }}').render!("foo" => TestEnumerable.new) + end + def test_date assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B") assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B") From 07f7d63beaaa62e3e6dbde4401cba710c17507ad Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Fri, 26 Jul 2013 11:34:00 -0400 Subject: [PATCH 07/17] Use kind_of? instead of class.include? and rearrange stuff --- lib/liquid/standardfilters.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index fa6d2a9..ef559f7 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -101,10 +101,10 @@ module Liquid def map(input, property) ary = if input.is_a?(Array) input.flatten - elsif !input.class.include?(Enumerable) - [input].flatten - else + elsif input.kind_of?(Enumerable) input + else + [input].flatten end ary.map do |e| From ee2902761c6e22506d199bc2d21b30bcea61acd4 Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Thu, 1 Aug 2013 09:18:29 -0400 Subject: [PATCH 08/17] Update History.md --- History.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/History.md b/History.md index 3312c0a..a51ac8e 100644 --- a/History.md +++ b/History.md @@ -6,6 +6,9 @@ The following releases will only be tested against Ruby 1.9 and Ruby 2.0 and are ## 2.6.0 / Master branch (not yet released) * ... +* Make map filter work on enumerable drops, see #233 [Florian Weingarten, fw42] +* Fix security issue with map filter, see #230, #232, #234, #237 [Florian Weingarten, fw42] +* Improved whitespace stripping for blank blocks, related to #216 [Florian Weingarten, fw42] * Bugfix for #106: fix example servlet [gnowoel] * Bugfix for #97: strip_html filter supports multi-line tags [Jo Liss, joliss] * Bugfix for #114: strip_html filter supports style tags [James Allardice, jamesallardice] From ff570c3ddcca3377b6569cf33421d7a66b079ebb Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Wed, 31 Jul 2013 17:41:02 -0400 Subject: [PATCH 09/17] 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 10/17] 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 11/17] 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 12/17] 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) From 98c184f2fb2321b8cb69a8196cbc43e5b3a15de3 Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Tue, 6 Aug 2013 10:24:37 -0400 Subject: [PATCH 13/17] Update History.md --- History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/History.md b/History.md index a51ac8e..b540737 100644 --- a/History.md +++ b/History.md @@ -6,6 +6,7 @@ The following releases will only be tested against Ruby 1.9 and Ruby 2.0 and are ## 2.6.0 / Master branch (not yet released) * ... +* Fix clashing method names in enumerable drops, see #238 [Florian Weingarten, fw42] * Make map filter work on enumerable drops, see #233 [Florian Weingarten, fw42] * Fix security issue with map filter, see #230, #232, #234, #237 [Florian Weingarten, fw42] * Improved whitespace stripping for blank blocks, related to #216 [Florian Weingarten, fw42] From 8f978ecd1abe5e6da20d469d973d13b226e351a3 Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Thu, 8 Aug 2013 11:47:26 -0400 Subject: [PATCH 14/17] Make sort filter work on Enumerable drops --- lib/liquid/standardfilters.rb | 22 ++++++++++++---------- test/liquid/standard_filter_test.rb | 6 +++++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index ef559f7..3c543e5 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -81,7 +81,7 @@ module Liquid # Sort elements of the array # provide optional property with which to sort an array of hashes or drops def sort(input, property = nil) - ary = [input].flatten + ary = flatten_if_necessary(input) if property.nil? ary.sort elsif ary.first.respond_to?('[]') and !ary.first[property].nil? @@ -99,15 +99,7 @@ module Liquid # map/collect on a given property def map(input, property) - ary = if input.is_a?(Array) - input.flatten - elsif input.kind_of?(Enumerable) - input - else - [input].flatten - end - - ary.map do |e| + flatten_if_necessary(input).map do |e| e = e.call if e.is_a?(Proc) e = e.to_liquid if e.respond_to?(:to_liquid) @@ -256,6 +248,16 @@ module Liquid private + def flatten_if_necessary(input) + if input.is_a?(Array) + input.flatten + elsif input.kind_of?(Enumerable) + input + else + [input].flatten + end + end + def to_number(obj) case obj when Float diff --git a/test/liquid/standard_filter_test.rb b/test/liquid/standard_filter_test.rb index 18938c3..1716442 100644 --- a/test/liquid/standard_filter_test.rb +++ b/test/liquid/standard_filter_test.rb @@ -31,7 +31,7 @@ class TestEnumerable < Liquid::Drop include Enumerable def each(&block) - [ { "foo" => 1 }, { "foo" => 2 }, { "foo" => 3 } ].each(&block) + [ { "foo" => 1, "bar" => 2 }, { "foo" => 2, "bar" => 1 }, { "foo" => 3, "bar" => 3 } ].each(&block) end end @@ -147,6 +147,10 @@ class StandardFiltersTest < Test::Unit::TestCase assert_equal "123", Liquid::Template.parse('{{ foo | map: "foo" }}').render!("foo" => TestEnumerable.new) end + def test_sort_works_on_enumerables + assert_equal "213", Liquid::Template.parse('{{ foo | sort: "bar" | map: "foo" }}').render!("foo" => TestEnumerable.new) + end + def test_date assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B") assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B") From ed4b61bfd3d2372cc290240df68a2d292acf1d0f Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Thu, 8 Aug 2013 11:53:52 -0400 Subject: [PATCH 15/17] Fix broken map test and add sort test --- lib/liquid/standardfilters.rb | 4 ++-- test/liquid/standard_filter_test.rb | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index 3c543e5..b7f6418 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -101,7 +101,6 @@ module Liquid def map(input, property) flatten_if_necessary(input).map do |e| e = e.call if e.is_a?(Proc) - e = e.to_liquid if e.respond_to?(:to_liquid) if property == "to_liquid" e @@ -249,13 +248,14 @@ module Liquid private def flatten_if_necessary(input) - if input.is_a?(Array) + ary = if input.is_a?(Array) input.flatten elsif input.kind_of?(Enumerable) input else [input].flatten end + ary.map{ |e| e.respond_to?(:to_liquid) ? e.to_liquid : e } end def to_number(obj) diff --git a/test/liquid/standard_filter_test.rb b/test/liquid/standard_filter_test.rb index 1716442..b317b68 100644 --- a/test/liquid/standard_filter_test.rb +++ b/test/liquid/standard_filter_test.rb @@ -15,6 +15,10 @@ class TestThing "woot: #{@foo}" end + def [](whatever) + to_s + end + def to_liquid @foo += 1 self @@ -133,7 +137,12 @@ class StandardFiltersTest < Test::Unit::TestCase def test_map_calls_to_liquid t = TestThing.new - assert_equal "woot: 1", Liquid::Template.parse('{{ foo }}').render("foo" => t) + assert_equal "woot: 1", Liquid::Template.parse('{{ foo | map: "whatever" }}').render("foo" => [t]) + end + + def test_sort_calls_to_liquid + t = TestThing.new + assert_equal "woot: 1", Liquid::Template.parse('{{ foo | sort: "whatever" }}').render("foo" => [t]) end def test_map_over_proc From 355199dac4a28fb616683f1032bd0ef1ea10178d Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Wed, 14 Aug 2013 17:00:26 -0400 Subject: [PATCH 16/17] Update History.md --- History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/History.md b/History.md index b540737..1dc8ad0 100644 --- a/History.md +++ b/History.md @@ -6,6 +6,7 @@ The following releases will only be tested against Ruby 1.9 and Ruby 2.0 and are ## 2.6.0 / Master branch (not yet released) * ... +* Make sort filter work on enumerable drops, see #239 [Florian Weingarten, fw42] * Fix clashing method names in enumerable drops, see #238 [Florian Weingarten, fw42] * Make map filter work on enumerable drops, see #233 [Florian Weingarten, fw42] * Fix security issue with map filter, see #230, #232, #234, #237 [Florian Weingarten, fw42] From a57d576708027e854d5b86450927692a3215b422 Mon Sep 17 00:00:00 2001 From: Florian Weingarten Date: Mon, 19 Aug 2013 11:53:47 -0400 Subject: [PATCH 17/17] Overwrite drop inspect --- lib/liquid/drop.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/liquid/drop.rb b/lib/liquid/drop.rb index a1b66a4..f1bf48a 100644 --- a/lib/liquid/drop.rb +++ b/lib/liquid/drop.rb @@ -44,6 +44,10 @@ module Liquid true end + def inspect + self.class.to_s + end + def to_liquid self end