From 832516fb31df07fb6de06298222f4da5a86b573f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Tue, 30 Mar 2021 12:23:45 +0200 Subject: [PATCH 01/23] Add replace_last and remove_last tests --- test/integration/standard_filter_test.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index 526ab91e2..f1bc354c9 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -506,6 +506,9 @@ def test_replace assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2)) assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2)) assert_template_result('2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}") + assert_equal('1 1 1 2', @filters.replace_last('1 1 1 1', '1', 2)) + assert_equal('1 1 1 2', @filters.replace_last('1 1 1 1', 1, 2)) + assert_template_result('1 1 1 2', "{{ '1 1 1 1' | replace_last: '1', 2 }}") end def test_remove @@ -514,6 +517,9 @@ def test_remove assert_equal('a a a', @filters.remove_first("a a a a", 'a ')) assert_equal(' 1 1 1', @filters.remove_first("1 1 1 1", 1)) assert_template_result('a a a', "{{ 'a a a a' | remove_first: 'a ' }}") + assert_equal('a a a', @filters.remove_last("a a a a", ' a')) + assert_equal('1 1 1 ', @filters.remove_last("1 1 1 1", 1)) + assert_template_result('a a a', "{{ 'a a a a' | remove_last: ' a' }}") end def test_pipes_in_string_arguments From e575c1f131bc8a50d91fd698559729c184ab736f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Tue, 30 Mar 2021 12:34:01 +0200 Subject: [PATCH 02/23] Add replace_last and remove_last filters --- lib/liquid/standardfilters.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index cf92e6871..c09c5a701 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -268,6 +268,11 @@ def replace_first(input, string, replacement = '') input.to_s.sub(string.to_s, replacement.to_s) end + # Replace the last occurrences of a string with another + def replace_last(input, string, replacement = '') + input.to_s.reserve.sub(string.to_s.reserve, replacement.to_s.reserve).reserve + end + # remove a substring def remove(input, string) input.to_s.gsub(string.to_s, '') @@ -278,6 +283,11 @@ def remove_first(input, string) input.to_s.sub(string.to_s, '') end + # remove the last occurences of a substring + def remove_last(input, string) + input.to_s.reverse.sub(string.to_s.reverse, '').reverse + end + # add one string to another def append(input, string) input.to_s + string.to_s From 7754a0fbee3e442b155a79bd0e84d30df09d3d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Tue, 30 Mar 2021 12:37:44 +0200 Subject: [PATCH 03/23] Use correct method --- lib/liquid/standardfilters.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index c09c5a701..059f74067 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -270,7 +270,7 @@ def replace_first(input, string, replacement = '') # Replace the last occurrences of a string with another def replace_last(input, string, replacement = '') - input.to_s.reserve.sub(string.to_s.reserve, replacement.to_s.reserve).reserve + input.to_s.reverse.sub(string.to_s.reverse, replacement.to_s.reverse).reverse end # remove a substring From 38b7364f96e8ed5d8fa6a52faafde1e0bf53c339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Fri, 2 Apr 2021 20:11:38 +0200 Subject: [PATCH 04/23] Improve tests --- test/integration/standard_filter_test.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index f1bc354c9..89c56a97a 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -503,10 +503,8 @@ def test_first_last def test_replace assert_equal('2 2 2 2', @filters.replace('1 1 1 1', '1', 2)) assert_equal('2 2 2 2', @filters.replace('1 1 1 1', 1, 2)) - assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2)) assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2)) assert_template_result('2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}") - assert_equal('1 1 1 2', @filters.replace_last('1 1 1 1', '1', 2)) assert_equal('1 1 1 2', @filters.replace_last('1 1 1 1', 1, 2)) assert_template_result('1 1 1 2', "{{ '1 1 1 1' | replace_last: '1', 2 }}") end @@ -516,8 +514,8 @@ def test_remove assert_equal(' ', @filters.remove("1 1 1 1", 1)) assert_equal('a a a', @filters.remove_first("a a a a", 'a ')) assert_equal(' 1 1 1', @filters.remove_first("1 1 1 1", 1)) - assert_template_result('a a a', "{{ 'a a a a' | remove_first: 'a ' }}") - assert_equal('a a a', @filters.remove_last("a a a a", ' a')) + assert_template_result('b a a', "{{ 'a b a a' | remove_first: 'a ' }}") + assert_equal('a a b', @filters.remove_last("a a b a", ' a')) assert_equal('1 1 1 ', @filters.remove_last("1 1 1 1", 1)) assert_template_result('a a a', "{{ 'a a a a' | remove_last: ' a' }}") end From effee1ed50017ce8698ea2d30addbcff0282fa6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Fri, 2 Apr 2021 20:15:13 +0200 Subject: [PATCH 05/23] Update tests --- test/integration/standard_filter_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index 89c56a97a..44700a192 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -512,12 +512,12 @@ def test_replace def test_remove assert_equal(' ', @filters.remove("a a a a", 'a')) assert_equal(' ', @filters.remove("1 1 1 1", 1)) - assert_equal('a a a', @filters.remove_first("a a a a", 'a ')) + assert_equal('b a a', @filters.remove_first("a b a a", 'a ')) assert_equal(' 1 1 1', @filters.remove_first("1 1 1 1", 1)) assert_template_result('b a a', "{{ 'a b a a' | remove_first: 'a ' }}") assert_equal('a a b', @filters.remove_last("a a b a", ' a')) assert_equal('1 1 1 ', @filters.remove_last("1 1 1 1", 1)) - assert_template_result('a a a', "{{ 'a a a a' | remove_last: ' a' }}") + assert_template_result('a a b', "{{ 'a a b a' | remove_last: ' a' }}") end def test_pipes_in_string_arguments From b3d14e5b493525730f05a515b9e366a35d70fcfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Tue, 6 Apr 2021 07:34:36 +0200 Subject: [PATCH 06/23] Update tests Co-Authored-By: ADTC --- test/integration/standard_filter_test.rb | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index 44700a192..0bd1db638 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -501,12 +501,18 @@ def test_first_last end def test_replace - assert_equal('2 2 2 2', @filters.replace('1 1 1 1', '1', 2)) + assert_equal('b b b b', @filters.replace('a a a a', 'a', 'b')) assert_equal('2 2 2 2', @filters.replace('1 1 1 1', 1, 2)) + assert_equal('b a a a', @filters.replace_first('a a a a', 'a', 'b')) assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2)) - assert_template_result('2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}") + assert_equal('a a a b', @filters.replace_last('a a a a', 'a', 'b')) assert_equal('1 1 1 2', @filters.replace_last('1 1 1 1', 1, 2)) - assert_template_result('1 1 1 2', "{{ '1 1 1 1' | replace_last: '1', 2 }}") + assert_template_result('b b b b', "{{ 'a a a a' | replace: 'a', 'b' }}") + assert_template_result('2 2 2 2', "{{ '1 1 1 1' | replace: 1, 2 }}") + assert_template_result('b a a a', "{{ 'a a a a' | replace_first: 'a', 'b' }}") + assert_template_result('2 1 1 1', "{{ '1 1 1 1' | replace_first: 1, 2 }}") + assert_template_result('a a a b', "{{ 'a a a a' | replace_last: 'a', 'b' }}") + assert_template_result('1 1 1 2', "{{ '1 1 1 1' | replace_last: 1, 2 }}") end def test_remove @@ -514,10 +520,14 @@ def test_remove assert_equal(' ', @filters.remove("1 1 1 1", 1)) assert_equal('b a a', @filters.remove_first("a b a a", 'a ')) assert_equal(' 1 1 1', @filters.remove_first("1 1 1 1", 1)) - assert_template_result('b a a', "{{ 'a b a a' | remove_first: 'a ' }}") assert_equal('a a b', @filters.remove_last("a a b a", ' a')) assert_equal('1 1 1 ', @filters.remove_last("1 1 1 1", 1)) + assert_template_result(' ', "{{ 'a a a a' | remove: 'a' }}") + assert_template_result(' ', "{{ '1 1 1 1' | remove: 1 }}") + assert_template_result('b a a', "{{ 'a b a a' | remove_first: 'a ' }}") + assert_template_result(' 1 1 1', "{{ '1 1 1 1' | remove_first: 1 }}") assert_template_result('a a b', "{{ 'a a b a' | remove_last: ' a' }}") + assert_template_result('1 1 1 ', "{{ '1 1 1 1' | remove_last: 1 }}") end def test_pipes_in_string_arguments From d5ecf00a8c804c3cd66704f1870352f51347807b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Thu, 9 Sep 2021 09:28:22 +0200 Subject: [PATCH 07/23] Update History.md --- History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/History.md b/History.md index 507a4a2f6..1353e74a0 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,7 @@ ## 5.0.2 (unreleased) ### Features +* Add `remove_last`, and `replace_last` filters (#1422) [Anders Hagbard] * Add `base64_encode`, `base64_decode`, `base64_url_safe_encode`, and `base64_url_safe_decode` filters (#1450) [Daniel Insley] ### Fixes From f72cfb13abff18b4ad0350da4140f4a008ca1892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Fri, 10 Sep 2021 08:24:50 +0200 Subject: [PATCH 08/23] Delegate functions to corresponding replace functions --- 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 4413a2032..e8b18bfe4 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -303,17 +303,17 @@ def replace_last(input, string, replacement = '') # remove a substring def remove(input, string) - input.to_s.gsub(string.to_s, '') + replace(input.to_s, string, '') end # remove the first occurrences of a substring def remove_first(input, string) - input.to_s.sub(string.to_s, '') + replace_first(input.to_s, string, '') end # remove the last occurences of a substring def remove_last(input, string) - input.to_s.reverse.sub(string.to_s.reverse, '').reverse + replace_last(input.to_s, string, '') end # add one string to another From dc7818f371346152ec6a3a2a4ac967ed1533447f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Fri, 10 Sep 2021 09:55:08 +0200 Subject: [PATCH 09/23] Update filter to use rindex --- lib/liquid/standardfilters.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index e8b18bfe4..b21df72d2 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -298,7 +298,16 @@ def replace_first(input, string, replacement = '') # Replace the last occurrences of a string with another def replace_last(input, string, replacement = '') - input.to_s.reverse.sub(string.to_s.reverse, replacement.to_s.reverse).reverse + start_index = input.to_s.rindex(string.to_s) + + if !start_index.nil? && start_index > -1 + end_index = start_index + replacement.to_s.length + + duplicate = input.to_s.dup + duplicate[start_index, end_index] = replacement.to_s + + duplicate + end end # remove a substring From f5e77b6d1d66186be33610af52198082ce357173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Fri, 10 Sep 2021 18:02:23 +0200 Subject: [PATCH 10/23] Update test to support third argument --- test/integration/standard_filter_test.rb | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index f0f402262..701672fbf 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -855,16 +855,20 @@ def test_all_filters_never_raise_non_liquid_exception ["foo", 123, nil, true, false, Drop, ["foo"], { foo: "bar" }], ] test_types.each do |first| - test_types.each do |other| - (@filters.methods - Object.methods).each do |method| - arg_count = @filters.method(method).arity - arg_count *= -1 if arg_count < 0 - inputs = [first] - inputs << ([other] * (arg_count - 1)) if arg_count > 1 - begin - @filters.send(method, *inputs) - rescue Liquid::ArgumentError, Liquid::ZeroDivisionError - nil + test_types.each do |second| + test_types.each do |third| + (@filters.methods - Object.methods).each do |method| + arg_count = @filters.method(method).arity + arg_count *= -1 if arg_count < 0 + inputs = [first] + inputs << ([second] * (arg_count - 1)) if arg_count > 1 + inputs << ([third] * (arg_count - 1)) if arg_count > 2 + + begin + @filters.send(method, *inputs) + rescue Liquid::ArgumentError, Liquid::ZeroDivisionError + nil + end end end end From b5abd145e319fadcd9dbfffb67e1ca3c59097b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Fri, 10 Sep 2021 18:02:35 +0200 Subject: [PATCH 11/23] Remove default value for replacement argument --- lib/liquid/standardfilters.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index b21df72d2..cce0f99f1 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -297,7 +297,7 @@ def replace_first(input, string, replacement = '') end # Replace the last occurrences of a string with another - def replace_last(input, string, replacement = '') + def replace_last(input, string, replacement) start_index = input.to_s.rindex(string.to_s) if !start_index.nil? && start_index > -1 From 986391cb7d44999f8fc96a25027266df3e1e4dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Mon, 13 Sep 2021 18:00:03 +0200 Subject: [PATCH 12/23] start_index will never be -1 --- lib/liquid/standardfilters.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index cce0f99f1..ebcb4f269 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -300,7 +300,7 @@ def replace_first(input, string, replacement = '') def replace_last(input, string, replacement) start_index = input.to_s.rindex(string.to_s) - if !start_index.nil? && start_index > -1 + if !start_index.nil? end_index = start_index + replacement.to_s.length duplicate = input.to_s.dup From e3c82a9e4ed43f02052f0b76bf6847842c30e618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Mon, 13 Sep 2021 18:07:29 +0200 Subject: [PATCH 13/23] Favor unless over if for negative conditions --- lib/liquid/standardfilters.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index ebcb4f269..e12ad8517 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -300,7 +300,7 @@ def replace_first(input, string, replacement = '') def replace_last(input, string, replacement) start_index = input.to_s.rindex(string.to_s) - if !start_index.nil? + unless start_index.nil? end_index = start_index + replacement.to_s.length duplicate = input.to_s.dup From d81f7f04ce031a8ca73cdc0905d1fe84ee696c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Thu, 16 Sep 2021 18:42:18 +0200 Subject: [PATCH 14/23] Add tests to make sure it returns original string on no replacement --- test/integration/standard_filter_test.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index 701672fbf..fc112b466 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -541,16 +541,22 @@ def test_first_last def test_replace assert_equal('b b b b', @filters.replace('a a a a', 'a', 'b')) assert_equal('2 2 2 2', @filters.replace('1 1 1 1', 1, 2)) + assert_equal('1 1 1 1', @filters.replace('1 1 1 1', 2, 3)) assert_equal('b a a a', @filters.replace_first('a a a a', 'a', 'b')) assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2)) + assert_equal('1 1 1 1', @filters.replace_first('1 1 1 1', 2, 3)) assert_equal('a a a b', @filters.replace_last('a a a a', 'a', 'b')) assert_equal('1 1 1 2', @filters.replace_last('1 1 1 1', 1, 2)) + assert_equal('1 1 1 1', @filters.replace_last('1 1 1 1', 2, 3)) assert_template_result('b b b b', "{{ 'a a a a' | replace: 'a', 'b' }}") assert_template_result('2 2 2 2', "{{ '1 1 1 1' | replace: 1, 2 }}") + assert_template_result('1 1 1 1', "{{ '1 1 1 1' | replace: 2, 3 }}") assert_template_result('b a a a', "{{ 'a a a a' | replace_first: 'a', 'b' }}") assert_template_result('2 1 1 1', "{{ '1 1 1 1' | replace_first: 1, 2 }}") + assert_template_result('a a a a', "{{ 'a a a a' | replace_first: 'b', 'c' }}") assert_template_result('a a a b', "{{ 'a a a a' | replace_last: 'a', 'b' }}") assert_template_result('1 1 1 2', "{{ '1 1 1 1' | replace_last: 1, 2 }}") + assert_template_result('a a a a', "{{ 'a a a a' | replace_last: 'b', 'c' }}") end def test_remove From aead4c5daa9db39e85e4dcdd8214a04ec318975a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Thu, 16 Sep 2021 19:10:50 +0200 Subject: [PATCH 15/23] Refactor filter --- lib/liquid/standardfilters.rb | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index e12ad8517..a2b1d13a5 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -298,16 +298,7 @@ def replace_first(input, string, replacement = '') # Replace the last occurrences of a string with another def replace_last(input, string, replacement) - start_index = input.to_s.rindex(string.to_s) - - unless start_index.nil? - end_index = start_index + replacement.to_s.length - - duplicate = input.to_s.dup - duplicate[start_index, end_index] = replacement.to_s - - duplicate - end + input.to_s.sub(/.*\K#{Regexp.escape(string.to_s)}/, replacement.to_s) end # remove a substring From 5187399fd4c51eb580a851dd3712e09c2ff50196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Fri, 17 Sep 2021 08:26:16 +0200 Subject: [PATCH 16/23] Update lib/liquid/standardfilters.rb Co-authored-by: Dylan Thacker-Smith --- lib/liquid/standardfilters.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index a2b1d13a5..aa0a38217 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -303,7 +303,7 @@ def replace_last(input, string, replacement) # remove a substring def remove(input, string) - replace(input.to_s, string, '') + replace(input, string, '') end # remove the first occurrences of a substring From 45f186b4dfeddf9d37d404af9a90889c08e881fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Fri, 17 Sep 2021 08:33:59 +0200 Subject: [PATCH 17/23] Remove string formatter --- lib/liquid/standardfilters.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index aa0a38217..bd2a128fa 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -308,12 +308,12 @@ def remove(input, string) # remove the first occurrences of a substring def remove_first(input, string) - replace_first(input.to_s, string, '') + replace_first(input, string, '') end # remove the last occurences of a substring def remove_last(input, string) - replace_last(input.to_s, string, '') + replace_last(input, string, '') end # add one string to another From 0f17ed01b4fe50e66df4017154397f049db5b0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Fri, 17 Sep 2021 08:38:56 +0200 Subject: [PATCH 18/23] Use rindex again --- lib/liquid/standardfilters.rb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index bd2a128fa..29cf602b8 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -298,7 +298,17 @@ def replace_first(input, string, replacement = '') # Replace the last occurrences of a string with another def replace_last(input, string, replacement) - input.to_s.sub(/.*\K#{Regexp.escape(string.to_s)}/, replacement.to_s) + input = input.to_s + string = string.to_s + replacement = replacement.to_s + + start_index = input.rindex(string) + + return input unless start_index + + output = input.dup + output[start_index, string.length] = replacement + output end # remove a substring From 50c88fe74d05126e66183b062de29f7fdacebba0 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Thu, 24 Feb 2022 09:24:35 -0500 Subject: [PATCH 19/23] History.md: Add missing PR number to previous changelog entry --- History.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/History.md b/History.md index cdcca1463..a6a0dd76d 100644 --- a/History.md +++ b/History.md @@ -3,8 +3,7 @@ ## 5.1.1 (unreleased) ### Fixes - -* Fix some internal errors in filters from invalid input [Dylan Thacker-Smith] +* Fix some internal errors in filters from invalid input (#1476) [Dylan Thacker-Smith] ## 5.1.0 / 2021-09-09 From 588d407e0bd4bfb98b4f5481ed54f2c41e59f669 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Thu, 24 Feb 2022 09:21:28 -0500 Subject: [PATCH 20/23] Fix new changelog entry so it is under unreleased --- History.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index a6a0dd76d..5d0c092ba 100644 --- a/History.md +++ b/History.md @@ -1,6 +1,9 @@ # Liquid Change Log -## 5.1.1 (unreleased) +## 5.2.0 (unreleased) + +### Features +* Add `remove_last`, and `replace_last` filters (#1422) [Anders Hagbard] ### Fixes * Fix some internal errors in filters from invalid input (#1476) [Dylan Thacker-Smith] @@ -8,7 +11,6 @@ ## 5.1.0 / 2021-09-09 ### Features -* Add `remove_last`, and `replace_last` filters (#1422) [Anders Hagbard] * Add `base64_encode`, `base64_decode`, `base64_url_safe_encode`, and `base64_url_safe_decode` filters (#1450) [Daniel Insley] * Introduce `to_liquid_value` in `Liquid::Drop` (#1441) [Michael Go] From 63ae4cc2cc09518f17c67ce3f96c3cc5b6b11942 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Thu, 24 Feb 2022 13:56:14 -0500 Subject: [PATCH 21/23] Remove redundant test assertions --- test/integration/standard_filter_test.rb | 26 ++++++++---------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index c307e65a4..694d24d77 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -542,35 +542,27 @@ def test_replace assert_equal('b b b b', @filters.replace('a a a a', 'a', 'b')) assert_equal('2 2 2 2', @filters.replace('1 1 1 1', 1, 2)) assert_equal('1 1 1 1', @filters.replace('1 1 1 1', 2, 3)) + assert_template_result('2 2 2 2', "{{ '1 1 1 1' | replace: '1', 2 }}") + assert_equal('b a a a', @filters.replace_first('a a a a', 'a', 'b')) assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2)) assert_equal('1 1 1 1', @filters.replace_first('1 1 1 1', 2, 3)) + assert_template_result('2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}") + assert_equal('a a a b', @filters.replace_last('a a a a', 'a', 'b')) assert_equal('1 1 1 2', @filters.replace_last('1 1 1 1', 1, 2)) assert_equal('1 1 1 1', @filters.replace_last('1 1 1 1', 2, 3)) - assert_template_result('b b b b', "{{ 'a a a a' | replace: 'a', 'b' }}") - assert_template_result('2 2 2 2', "{{ '1 1 1 1' | replace: 1, 2 }}") - assert_template_result('1 1 1 1', "{{ '1 1 1 1' | replace: 2, 3 }}") - assert_template_result('b a a a', "{{ 'a a a a' | replace_first: 'a', 'b' }}") - assert_template_result('2 1 1 1', "{{ '1 1 1 1' | replace_first: 1, 2 }}") - assert_template_result('a a a a', "{{ 'a a a a' | replace_first: 'b', 'c' }}") - assert_template_result('a a a b', "{{ 'a a a a' | replace_last: 'a', 'b' }}") - assert_template_result('1 1 1 2', "{{ '1 1 1 1' | replace_last: 1, 2 }}") - assert_template_result('a a a a', "{{ 'a a a a' | replace_last: 'b', 'c' }}") + assert_template_result('1 1 1 2', "{{ '1 1 1 1' | replace_last: '1', 2 }}") end def test_remove assert_equal(' ', @filters.remove("a a a a", 'a')) - assert_equal(' ', @filters.remove("1 1 1 1", 1)) - assert_equal('b a a', @filters.remove_first("a b a a", 'a ')) - assert_equal(' 1 1 1', @filters.remove_first("1 1 1 1", 1)) - assert_equal('a a b', @filters.remove_last("a a b a", ' a')) - assert_equal('1 1 1 ', @filters.remove_last("1 1 1 1", 1)) - assert_template_result(' ', "{{ 'a a a a' | remove: 'a' }}") assert_template_result(' ', "{{ '1 1 1 1' | remove: 1 }}") - assert_template_result('b a a', "{{ 'a b a a' | remove_first: 'a ' }}") + + assert_equal('b a a', @filters.remove_first("a b a a", 'a ')) assert_template_result(' 1 1 1', "{{ '1 1 1 1' | remove_first: 1 }}") - assert_template_result('a a b', "{{ 'a a b a' | remove_last: ' a' }}") + + assert_equal('a a b', @filters.remove_last("a a b a", ' a')) assert_template_result('1 1 1 ', "{{ '1 1 1 1' | remove_last: 1 }}") end From bfdcfcea518c5c7f83d3cc405f2409db102bf11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Thu, 12 May 2022 16:41:40 +0200 Subject: [PATCH 22/23] Add reject filter --- lib/liquid/standardfilters.rb | 23 ++++++++ test/integration/standard_filter_test.rb | 75 ++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index fade736c2..b8cfcdd86 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -234,6 +234,29 @@ def where(input, property, target_value = nil) end end + # Reject the elements of an array to those with a certain property value. + # By default the target is any falsy value. + def reject(input, properties, target_value = nil) + raise_property_error(properties) unless properties.is_a?(String) + + properties = properties.to_s.split('.') + ary = InputIterator.new(input, context) + + ary.reject do |item| + if item.is_a?(Hash) + value = item.dig(*properties) + + if target_value.nil? + !value + else + value == target_value + end + else + true + end + end + end + # Remove duplicate elements from an array # provide optional property with which to determine uniqueness def uniq(input, property = nil) diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index f413e6f99..b9cc1ead1 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -864,6 +864,81 @@ def test_where_array_of_only_unindexable_values assert_nil(@filters.where([nil], "ok")) end + def test_reject + input = [ + { "handle" => "alpha", "ok" => true }, + { "handle" => "beta", "ok" => false }, + { "handle" => "gamma", "ok" => false }, + { "handle" => "delta", "ok" => true }, + ] + + expectation = [ + { "handle" => "alpha", "ok" => true }, + { "handle" => "delta", "ok" => true }, + ] + + assert_equal(expectation, @filters.reject(input, "ok", false)) + assert_equal(expectation, @filters.reject(input, "ok")) + end + + def test_reject_no_key_set + input = [ + { "handle" => "alpha", "ok" => true }, + { "handle" => "beta" }, + { "handle" => "gamma" }, + { "handle" => "delta", "ok" => true }, + ] + + expectation = [ + { "handle" => "alpha", "ok" => true }, + { "handle" => "delta", "ok" => true }, + ] + + assert_equal(expectation, @filters.reject(input, "ok")) + end + + def test_reject_non_array_map_input + assert_equal([], @filters.reject({ "foo" => "bar" }, "foo", "bar")) + assert_equal([{ "foo" => "baz" }], @filters.reject({ "foo" => "baz" }, "foo", "bar")) + end + + def test_reject_indexable_but_non_map_value + assert_equal([], @filters.reject(1, "ok", true)) + assert_equal([], @filters.reject(1, "ok")) + end + + def test_reject_non_boolean_value + input = [ + { "message" => "Bonjour!", "language" => "French" }, + { "message" => "Hello!", "language" => "English" }, + ] + + assert_equal([{ "message" => "Hello!", "language" => "English" }], @filters.reject(input, "language", "French")) + assert_equal([{ "message" => "Bonjour!", "language" => "French" }], @filters.reject(input, "language", "English")) + end + + def test_reject_array_of_only_unindexable_values + assert_equal([], @filters.reject([nil, nil], "ok", true)) + assert_equal([], @filters.reject([nil, nil], "ok")) + end + + def test_reject_deep + input = [ + { "item" => { "handle" => "alpha", "ok" => true } }, + { "item" => { "handle" => "beta", "ok" => false } }, + { "item" => { "handle" => "gamma", "ok" => false } }, + { "item" => { "handle" => "delta", "ok" => true } }, + ] + + expectation = [ + { "item" => { "handle" => "alpha", "ok" => true } }, + { "item" => { "handle" => "delta", "ok" => true } }, + ] + + assert_equal(expectation, @filters.reject(input, "item.ok", false)) + assert_equal(expectation, @filters.reject(input, "item.ok")) + end + def test_all_filters_never_raise_non_liquid_exception test_drop = TestDrop.new(value: "test") test_drop.context = Context.new From 72540235d6e5e56b66a5101349cd0ae8d92f28d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20S=C3=B8gaard?= Date: Tue, 7 Jun 2022 12:21:54 +0200 Subject: [PATCH 23/23] Update History.md --- History.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index 855f06a77..ea78cd07c 100644 --- a/History.md +++ b/History.md @@ -363,13 +363,13 @@ Yanked from rubygems, as it contained too many changes that broke compatibility. * Removed count helper from standard lib. use size [Tobias Luetke] * Fixed bug with string filter parameters failing to tolerate commas in strings. [Paul Hammond] * Improved filter parameters. Filter parameters are now context sensitive; Types are resolved according to the rules of the context. Multiple parameters are now separated by the Liquid::ArgumentSeparator: , by default [Paul Hammond] - {{ 'Typo' | link_to: 'http://typo.leetsoft.com', 'Typo - a modern weblog engine' }} + {{ 'Typo' | link_to: 'http://typo.leetsoft.com', 'Typo - a modern weblog engine' }} * Added Liquid::Drop. A base class which you can use for exporting proxy objects to liquid which can acquire more data when used in liquid. [Tobias Luetke] class ProductDrop < Liquid::Drop - def top_sales - Shop.current.products.find(:all, :order => 'sales', :limit => 10 ) - end + def top_sales + Shop.current.products.find(:all, :order => 'sales', :limit => 10 ) + end end t = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {% endfor %} ' ) t.render('product' => ProductDrop.new )