From 484fd186126313568aacdca285c38bd21fabc957 Mon Sep 17 00:00:00 2001 From: Jon Daniel Date: Mon, 20 Aug 2012 23:59:01 -0400 Subject: [PATCH] added break and continue tags --- lib/liquid/errors.rb | 7 ++++++- lib/liquid/tags/break.rb | 24 ++++++++++++++++++++++++ lib/liquid/tags/continue.rb | 24 ++++++++++++++++++++++++ lib/liquid/tags/for.rb | 20 ++++++++++++++++---- test/liquid/tags/for_tag_test.rb | 13 +++++++++++++ 5 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 lib/liquid/tags/break.rb create mode 100644 lib/liquid/tags/continue.rb diff --git a/lib/liquid/errors.rb b/lib/liquid/errors.rb index ce4ca7e6..33842496 100644 --- a/lib/liquid/errors.rb +++ b/lib/liquid/errors.rb @@ -8,4 +8,9 @@ module Liquid class StandardError < Error; end class SyntaxError < Error; end class StackLevelError < Error; end -end \ No newline at end of file + + + class Interrupt < Error; end + class BreakInterrupt < Interrupt; end + class ContinueInterrupt < Interrupt; end +end diff --git a/lib/liquid/tags/break.rb b/lib/liquid/tags/break.rb new file mode 100644 index 00000000..64758ddf --- /dev/null +++ b/lib/liquid/tags/break.rb @@ -0,0 +1,24 @@ +module Liquid + + # Break tag to be used to break out of a for loop. + # + # == Basic Usage: + # {% for item in collection %} + # {% if item.condition %} + # {% break %} + # {% endif %} + # {% endfor %} + # + class Break < Tag + + ## + # Add an interrupt to context errors so a for loop can check + # for interrupts. + def render(context) + context.handle_error(BreakInterrupt.new) + end + + end + + Template.register_tag('break', Break) +end diff --git a/lib/liquid/tags/continue.rb b/lib/liquid/tags/continue.rb new file mode 100644 index 00000000..e3cfe11b --- /dev/null +++ b/lib/liquid/tags/continue.rb @@ -0,0 +1,24 @@ +module Liquid + + # Continue tag to be used to break out of a for loop. + # + # == Basic Usage: + # {% for item in collection %} + # {% if item.condition %} + # {% continue %} + # {% endif %} + # {% endfor %} + # + class Continue < Tag + + ## + # Add an interrupt to context errors so a for loop can check + # for interrupts. + def render(context) + context.handle_error(ContinueInterrupt.new) + end + + end + + Template.register_tag('continue', Continue) +end diff --git a/lib/liquid/tags/for.rb b/lib/liquid/tags/for.rb index a76b8758..53888c2d 100644 --- a/lib/liquid/tags/for.rb +++ b/lib/liquid/tags/for.rb @@ -69,7 +69,7 @@ module Liquid @nodelist = @else_block = [] end - def render(context) + def render(context) context.registers[:for] ||= Hash.new(0) collection = context[@collection_name] @@ -101,8 +101,8 @@ module Liquid # Store our progress through the collection for the continue flag context.registers[:for][@name] = from + segment.length - context.stack do - segment.each_with_index do |item, index| + context.stack do + segment.each_with_index do |item, index| context[@variable_name] = item context['forloop'] = { 'name' => @name, @@ -114,7 +114,19 @@ module Liquid 'first' => (index == 0), 'last' => (index == length - 1) } - result << render_all(@for_block, context) + rendered = render_all(@for_block, context) + + if context.errors.last.is_a? BreakInterrupt + context.errors.pop + break + end + + if context.errors.last.is_a? ContinueInterrupt + context.errors.pop + next + end + + result << rendered end end result diff --git a/test/liquid/tags/for_tag_test.rb b/test/liquid/tags/for_tag_test.rb index 485701d2..a60c25f7 100644 --- a/test/liquid/tags/for_tag_test.rb +++ b/test/liquid/tags/for_tag_test.rb @@ -168,6 +168,19 @@ HERE assert_template_result(expected,markup,assigns) end + def test_break + assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,10]}} + markup = '{% for i in array.items %}{{ i }}{% if i > 3 %}{% break %}{% endif %}{% endfor %}' + expected = "123" + assert_template_result(expected,markup,assigns) + end + + def test_continue + assigns = {'array' => {'items' => [1,2,3,4,5]}} + markup = '{% for i in array.items %}{% if i == 3 %}{% continue %}{% else %}{{ i }}{% endif %}{% endfor %}' + expected = "1245" + assert_template_result(expected,markup,assigns) + end def test_for_tag_string # ruby 1.8.7 "String".each => Enumerator with single "String" element.