liquid 4.0.3 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/History.md +54 -0
 - data/README.md +6 -0
 - data/lib/liquid/block.rb +31 -14
 - data/lib/liquid/block_body.rb +166 -54
 - data/lib/liquid/condition.rb +41 -20
 - data/lib/liquid/context.rb +107 -52
 - data/lib/liquid/document.rb +47 -9
 - data/lib/liquid/drop.rb +4 -2
 - data/lib/liquid/errors.rb +20 -18
 - data/lib/liquid/expression.rb +29 -34
 - data/lib/liquid/extensions.rb +2 -0
 - data/lib/liquid/file_system.rb +6 -4
 - data/lib/liquid/forloop_drop.rb +11 -4
 - data/lib/liquid/i18n.rb +5 -3
 - data/lib/liquid/interrupts.rb +3 -1
 - data/lib/liquid/lexer.rb +30 -23
 - data/lib/liquid/locales/en.yml +3 -1
 - data/lib/liquid/parse_context.rb +20 -4
 - data/lib/liquid/parse_tree_visitor.rb +2 -2
 - data/lib/liquid/parser.rb +30 -18
 - data/lib/liquid/parser_switching.rb +17 -3
 - data/lib/liquid/partial_cache.rb +24 -0
 - data/lib/liquid/profiler/hooks.rb +26 -14
 - data/lib/liquid/profiler.rb +67 -86
 - data/lib/liquid/range_lookup.rb +13 -3
 - data/lib/liquid/register.rb +6 -0
 - data/lib/liquid/resource_limits.rb +47 -8
 - data/lib/liquid/standardfilters.rb +95 -46
 - data/lib/liquid/static_registers.rb +44 -0
 - data/lib/liquid/strainer_factory.rb +36 -0
 - data/lib/liquid/strainer_template.rb +53 -0
 - data/lib/liquid/tablerowloop_drop.rb +6 -4
 - data/lib/liquid/tag/disableable.rb +22 -0
 - data/lib/liquid/tag/disabler.rb +21 -0
 - data/lib/liquid/tag.rb +28 -6
 - data/lib/liquid/tags/assign.rb +24 -10
 - data/lib/liquid/tags/break.rb +8 -3
 - data/lib/liquid/tags/capture.rb +11 -8
 - data/lib/liquid/tags/case.rb +40 -27
 - data/lib/liquid/tags/comment.rb +5 -3
 - data/lib/liquid/tags/continue.rb +8 -3
 - data/lib/liquid/tags/cycle.rb +25 -14
 - data/lib/liquid/tags/decrement.rb +6 -3
 - data/lib/liquid/tags/echo.rb +34 -0
 - data/lib/liquid/tags/for.rb +68 -44
 - data/lib/liquid/tags/if.rb +39 -23
 - data/lib/liquid/tags/ifchanged.rb +11 -10
 - data/lib/liquid/tags/include.rb +34 -47
 - data/lib/liquid/tags/increment.rb +7 -3
 - data/lib/liquid/tags/raw.rb +14 -11
 - data/lib/liquid/tags/render.rb +84 -0
 - data/lib/liquid/tags/table_row.rb +23 -19
 - data/lib/liquid/tags/unless.rb +23 -15
 - data/lib/liquid/template.rb +53 -72
 - data/lib/liquid/template_factory.rb +9 -0
 - data/lib/liquid/tokenizer.rb +18 -10
 - data/lib/liquid/usage.rb +8 -0
 - data/lib/liquid/utils.rb +13 -3
 - data/lib/liquid/variable.rb +46 -41
 - data/lib/liquid/variable_lookup.rb +11 -6
 - data/lib/liquid/version.rb +2 -1
 - data/lib/liquid.rb +17 -5
 - data/test/integration/assign_test.rb +74 -5
 - data/test/integration/blank_test.rb +11 -8
 - data/test/integration/block_test.rb +47 -1
 - data/test/integration/capture_test.rb +18 -10
 - data/test/integration/context_test.rb +609 -5
 - data/test/integration/document_test.rb +4 -2
 - data/test/integration/drop_test.rb +67 -83
 - data/test/integration/error_handling_test.rb +73 -61
 - data/test/integration/expression_test.rb +46 -0
 - data/test/integration/filter_test.rb +53 -42
 - data/test/integration/hash_ordering_test.rb +5 -3
 - data/test/integration/output_test.rb +26 -24
 - data/test/integration/parsing_quirks_test.rb +19 -7
 - data/test/integration/{render_profiling_test.rb → profiler_test.rb} +84 -25
 - data/test/integration/security_test.rb +30 -21
 - data/test/integration/standard_filter_test.rb +385 -281
 - data/test/integration/tag/disableable_test.rb +59 -0
 - data/test/integration/tag_test.rb +45 -0
 - data/test/integration/tags/break_tag_test.rb +4 -2
 - data/test/integration/tags/continue_tag_test.rb +4 -2
 - data/test/integration/tags/echo_test.rb +13 -0
 - data/test/integration/tags/for_tag_test.rb +107 -51
 - data/test/integration/tags/if_else_tag_test.rb +5 -3
 - data/test/integration/tags/include_tag_test.rb +70 -54
 - data/test/integration/tags/increment_tag_test.rb +4 -2
 - data/test/integration/tags/liquid_tag_test.rb +116 -0
 - data/test/integration/tags/raw_tag_test.rb +14 -11
 - data/test/integration/tags/render_tag_test.rb +213 -0
 - data/test/integration/tags/standard_tag_test.rb +38 -31
 - data/test/integration/tags/statements_test.rb +23 -21
 - data/test/integration/tags/table_row_test.rb +2 -0
 - data/test/integration/tags/unless_else_tag_test.rb +4 -2
 - data/test/integration/template_test.rb +132 -124
 - data/test/integration/trim_mode_test.rb +78 -44
 - data/test/integration/variable_test.rb +74 -32
 - data/test/test_helper.rb +113 -22
 - data/test/unit/block_unit_test.rb +19 -24
 - data/test/unit/condition_unit_test.rb +79 -77
 - data/test/unit/file_system_unit_test.rb +6 -4
 - data/test/unit/i18n_unit_test.rb +7 -5
 - data/test/unit/lexer_unit_test.rb +11 -9
 - data/test/{integration → unit}/parse_tree_visitor_test.rb +16 -2
 - data/test/unit/parser_unit_test.rb +37 -35
 - data/test/unit/partial_cache_unit_test.rb +128 -0
 - data/test/unit/regexp_unit_test.rb +17 -15
 - data/test/unit/static_registers_unit_test.rb +156 -0
 - data/test/unit/strainer_factory_unit_test.rb +100 -0
 - data/test/unit/strainer_template_unit_test.rb +82 -0
 - data/test/unit/tag_unit_test.rb +5 -3
 - data/test/unit/tags/case_tag_unit_test.rb +3 -1
 - data/test/unit/tags/for_tag_unit_test.rb +4 -2
 - data/test/unit/tags/if_tag_unit_test.rb +3 -1
 - data/test/unit/template_factory_unit_test.rb +12 -0
 - data/test/unit/template_unit_test.rb +19 -10
 - data/test/unit/tokenizer_unit_test.rb +26 -19
 - data/test/unit/variable_unit_test.rb +51 -49
 - metadata +76 -50
 - data/lib/liquid/strainer.rb +0 -66
 - data/lib/liquid/truffle.rb +0 -5
 - data/test/truffle/truffle_test.rb +0 -9
 - data/test/unit/context_unit_test.rb +0 -489
 - data/test/unit/strainer_unit_test.rb +0 -164
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: d033978c659ebc86ff6ae3a36adaa1a90b8f69580e0cd033eb9c2c669ff6d66d
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 5c440b450df6ac2c673b0a23091d97e497ccb8207a443b0b6011f2b1a9b6529b
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 114697a0375800b4f67d7c216f9c56f823bb4468203632d45bda92bc597e0677acd0c4151c60f92fc121c2cd1b8d0d1c18d2482f1265acb62dbf190f12773d5c
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 3c013aef59a531829e202b73c4ce33d63ad5f58d763b7f2c610a3ff96fdab13fe2fdcf7e9401b63e09bb136fb1518ad69300b41114ebc1f076565c7d2953de78
         
     | 
    
        data/History.md
    CHANGED
    
    | 
         @@ -1,5 +1,59 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Liquid Change Log
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## 5.1.0 / 2021-09-09
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            ### Features
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Add `base64_encode`, `base64_decode`, `base64_url_safe_encode`, and `base64_url_safe_decode` filters (#1450) [Daniel Insley]
         
     | 
| 
      
 7 
     | 
    
         
            +
            * Introduce `to_liquid_value` in `Liquid::Drop` (#1441) [Michael Go]
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ### Fixes
         
     | 
| 
      
 10 
     | 
    
         
            +
            * Fix support for using a String subclass for the liquid source (#1421) [Dylan Thacker-Smith]
         
     | 
| 
      
 11 
     | 
    
         
            +
            * Add `ParseTreeVisitor` to `RangeLookup` (#1470) [CP Clermont]
         
     | 
| 
      
 12 
     | 
    
         
            +
            * Translate `RangeError` to `Liquid::Error` for `truncatewords` with large int (#1431) [Dylan Thacker-Smith]
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            ## 5.0.1 / 2021-03-24
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            ### Fixes
         
     | 
| 
      
 17 
     | 
    
         
            +
            * Add ParseTreeVisitor to Echo tag (#1414) [CP Clermont]
         
     | 
| 
      
 18 
     | 
    
         
            +
            * Test with ruby 3.0 as the latest ruby version (#1398) [Dylan Thacker-Smith]
         
     | 
| 
      
 19 
     | 
    
         
            +
            * Handle carriage return in newlines_to_br (#1391) [Unending]
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            ### Performance Improvements
         
     | 
| 
      
 22 
     | 
    
         
            +
            * Use split limit in truncatewords (#1361) [Dylan Thacker-Smith]
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            ## 5.0.0 / 2021-01-06
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            ### Features
         
     | 
| 
      
 27 
     | 
    
         
            +
            * Add new `{% render %}` tag (#1122) [Samuel Doiron]
         
     | 
| 
      
 28 
     | 
    
         
            +
            * Add support for `as` in `{% render %}` and `{% include %}` (#1181) [Mike Angell]
         
     | 
| 
      
 29 
     | 
    
         
            +
            * Add `{% liquid %}` and `{% echo %}` tags (#1086) [Justin Li]
         
     | 
| 
      
 30 
     | 
    
         
            +
            * Add [usage tracking](README.md#usage-tracking) [Mike Angell]
         
     | 
| 
      
 31 
     | 
    
         
            +
            * Add `Tag.disable_tags` for disabling tags that prepend `Tag::Disableable` at render time (#1162, #1274, #1275) [Mike Angell]
         
     | 
| 
      
 32 
     | 
    
         
            +
            * Support using a profiler for multiple renders (#1365, #1366) [Dylan Thacker-Smith]
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            ### Fixes
         
     | 
| 
      
 35 
     | 
    
         
            +
            * Fix catastrophic backtracking in `RANGES_REGEX` regular expression (#1357) [Dylan Thacker-Smith]
         
     | 
| 
      
 36 
     | 
    
         
            +
            * Make sure the for tag's limit and offset are integers (#1094) [David Cornu]
         
     | 
| 
      
 37 
     | 
    
         
            +
            * Invokable methods for enumerable reject include (#1151) [Thierry Joyal]
         
     | 
| 
      
 38 
     | 
    
         
            +
            * Allow `default` filter to handle `false` as value (#1144) [Mike Angell]
         
     | 
| 
      
 39 
     | 
    
         
            +
            * Fix render length resource limit so it doesn't multiply nested output (#1285) [Dylan Thacker-Smith]
         
     | 
| 
      
 40 
     | 
    
         
            +
            * Fix duplication of text in raw tags (#1304) [Peter Zhu]
         
     | 
| 
      
 41 
     | 
    
         
            +
            * Fix strict parsing of find variable with a name expression (#1317) [Dylan Thacker-Smith]
         
     | 
| 
      
 42 
     | 
    
         
            +
            * Use monotonic time to measure durations in Liquid::Profiler (#1362) [Dylan Thacker-Smith]
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            ### Breaking Changes
         
     | 
| 
      
 45 
     | 
    
         
            +
            * Require Ruby >= 2.5 (#1131, #1310) [Mike Angell, Dylan Thacker-Smith]
         
     | 
| 
      
 46 
     | 
    
         
            +
            * Remove support for taint checking (#1268) [Dylan Thacker-Smith]
         
     | 
| 
      
 47 
     | 
    
         
            +
            * Split Strainer class into StrainerFactory and StrainerTemplate (#1208) [Thierry Joyal]
         
     | 
| 
      
 48 
     | 
    
         
            +
            * Remove handling of a nil context in the Strainer class (#1218) [Thierry Joyal]
         
     | 
| 
      
 49 
     | 
    
         
            +
            * Handle `BlockBody#blank?` at parse time (#1287) [Dylan Thacker-Smith]
         
     | 
| 
      
 50 
     | 
    
         
            +
            * Pass the tag markup and tokenizer to `Document#unknown_tag` (#1290) [Dylan Thacker-Smith]
         
     | 
| 
      
 51 
     | 
    
         
            +
            * And several internal changes
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
            ### Performance Improvements
         
     | 
| 
      
 54 
     | 
    
         
            +
            * Reduce allocations (#1073, #1091, #1115, #1099, #1117, #1141, #1322, #1341) [Richard Monette, Florian Weingarten, Ashwin Maroli]
         
     | 
| 
      
 55 
     | 
    
         
            +
            * Improve resources limits performance (#1093, #1323) [Florian Weingarten, Dylan Thacker-Smith]
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
       3 
57 
     | 
    
         
             
            ## 4.0.3 / 2019-03-12
         
     | 
| 
       4 
58 
     | 
    
         | 
| 
       5 
59 
     | 
    
         
             
            ### Fixed
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -106,3 +106,9 @@ template = Liquid::Template.parse("{{x}} {{y}}") 
     | 
|
| 
       106 
106 
     | 
    
         
             
            template.render!({ 'x' => 1}, { strict_variables: true })
         
     | 
| 
       107 
107 
     | 
    
         
             
            #=> Liquid::UndefinedVariable: Liquid error: undefined variable y
         
     | 
| 
       108 
108 
     | 
    
         
             
            ```
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
            ### Usage tracking
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
            To help track usages of a feature or code path in production, we have released opt-in usage tracking. To enable this, we provide an empty `Liquid:: Usage.increment` method which you can customize to your needs. The feature is well suited to https://github.com/Shopify/statsd-instrument. However, the choice of implementation is up to you.
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
            Once you have enabled usage tracking, we recommend reporting any events through Github Issues that your system may be logging. It is highly likely this event has been added to consider deprecating or improving code specific to this event, so please raise any concerns.
         
     | 
    
        data/lib/liquid/block.rb
    CHANGED
    
    | 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Liquid
         
     | 
| 
       2 
4 
     | 
    
         
             
              class Block < Tag
         
     | 
| 
       3 
5 
     | 
    
         
             
                MAX_DEPTH = 100
         
     | 
| 
         @@ -8,11 +10,13 @@ module Liquid 
     | 
|
| 
       8 
10 
     | 
    
         
             
                end
         
     | 
| 
       9 
11 
     | 
    
         | 
| 
       10 
12 
     | 
    
         
             
                def parse(tokens)
         
     | 
| 
       11 
     | 
    
         
            -
                  @body =  
     | 
| 
      
 13 
     | 
    
         
            +
                  @body = new_body
         
     | 
| 
       12 
14 
     | 
    
         
             
                  while parse_body(@body, tokens)
         
     | 
| 
       13 
15 
     | 
    
         
             
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @body.freeze
         
     | 
| 
       14 
17 
     | 
    
         
             
                end
         
     | 
| 
       15 
18 
     | 
    
         | 
| 
      
 19 
     | 
    
         
            +
                # For backwards compatibility
         
     | 
| 
       16 
20 
     | 
    
         
             
                def render(context)
         
     | 
| 
       17 
21 
     | 
    
         
             
                  @body.render(context)
         
     | 
| 
       18 
22 
     | 
    
         
             
                end
         
     | 
| 
         @@ -25,20 +29,29 @@ module Liquid 
     | 
|
| 
       25 
29 
     | 
    
         
             
                  @body.nodelist
         
     | 
| 
       26 
30 
     | 
    
         
             
                end
         
     | 
| 
       27 
31 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                def unknown_tag( 
     | 
| 
       29 
     | 
    
         
            -
                   
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
      
 32 
     | 
    
         
            +
                def unknown_tag(tag_name, _markup, _tokenizer)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  Block.raise_unknown_tag(tag_name, block_name, block_delimiter, parse_context)
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 37 
     | 
    
         
            +
                def self.raise_unknown_tag(tag, block_name, block_delimiter, parse_context)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  if tag == 'else'
         
     | 
| 
      
 39 
     | 
    
         
            +
                    raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_else",
         
     | 
| 
      
 40 
     | 
    
         
            +
                      block_name: block_name)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  elsif tag.start_with?('end')
         
     | 
| 
      
 42 
     | 
    
         
            +
                    raise SyntaxError, parse_context.locale.t("errors.syntax.invalid_delimiter",
         
     | 
| 
       34 
43 
     | 
    
         
             
                      tag: tag,
         
     | 
| 
       35 
44 
     | 
    
         
             
                      block_name: block_name,
         
     | 
| 
       36 
     | 
    
         
            -
                      block_delimiter: block_delimiter) 
     | 
| 
      
 45 
     | 
    
         
            +
                      block_delimiter: block_delimiter)
         
     | 
| 
       37 
46 
     | 
    
         
             
                  else
         
     | 
| 
       38 
     | 
    
         
            -
                    raise SyntaxError 
     | 
| 
      
 47 
     | 
    
         
            +
                    raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
         
     | 
| 
       39 
48 
     | 
    
         
             
                  end
         
     | 
| 
       40 
49 
     | 
    
         
             
                end
         
     | 
| 
       41 
50 
     | 
    
         | 
| 
      
 51 
     | 
    
         
            +
                def raise_tag_never_closed(block_name)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  raise SyntaxError, parse_context.locale.t("errors.syntax.tag_never_closed", block_name: block_name)
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
       42 
55 
     | 
    
         
             
                def block_name
         
     | 
| 
       43 
56 
     | 
    
         
             
                  @tag_name
         
     | 
| 
       44 
57 
     | 
    
         
             
                end
         
     | 
| 
         @@ -47,11 +60,17 @@ module Liquid 
     | 
|
| 
       47 
60 
     | 
    
         
             
                  @block_delimiter ||= "end#{block_name}"
         
     | 
| 
       48 
61 
     | 
    
         
             
                end
         
     | 
| 
       49 
62 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
                 
     | 
| 
      
 63 
     | 
    
         
            +
                private
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                # @api public
         
     | 
| 
      
 66 
     | 
    
         
            +
                def new_body
         
     | 
| 
      
 67 
     | 
    
         
            +
                  parse_context.new_block_body
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
       51 
69 
     | 
    
         | 
| 
      
 70 
     | 
    
         
            +
                # @api public
         
     | 
| 
       52 
71 
     | 
    
         
             
                def parse_body(body, tokens)
         
     | 
| 
       53 
72 
     | 
    
         
             
                  if parse_context.depth >= MAX_DEPTH
         
     | 
| 
       54 
     | 
    
         
            -
                    raise StackLevelError, "Nesting too deep" 
     | 
| 
      
 73 
     | 
    
         
            +
                    raise StackLevelError, "Nesting too deep"
         
     | 
| 
       55 
74 
     | 
    
         
             
                  end
         
     | 
| 
       56 
75 
     | 
    
         
             
                  parse_context.depth += 1
         
     | 
| 
       57 
76 
     | 
    
         
             
                  begin
         
     | 
| 
         @@ -59,9 +78,7 @@ module Liquid 
     | 
|
| 
       59 
78 
     | 
    
         
             
                      @blank &&= body.blank?
         
     | 
| 
       60 
79 
     | 
    
         | 
| 
       61 
80 
     | 
    
         
             
                      return false if end_tag_name == block_delimiter
         
     | 
| 
       62 
     | 
    
         
            -
                      unless end_tag_name
         
     | 
| 
       63 
     | 
    
         
            -
                        raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
         
     | 
| 
       64 
     | 
    
         
            -
                      end
         
     | 
| 
      
 81 
     | 
    
         
            +
                      raise_tag_never_closed(block_name) unless end_tag_name
         
     | 
| 
       65 
82 
     | 
    
         | 
| 
       66 
83 
     | 
    
         
             
                      # this tag is not registered with the system
         
     | 
| 
       67 
84 
     | 
    
         
             
                      # pass it to the current block for special handling or error reporting
         
     | 
    
        data/lib/liquid/block_body.rb
    CHANGED
    
    | 
         @@ -1,32 +1,138 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'English'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       1 
5 
     | 
    
         
             
            module Liquid
         
     | 
| 
       2 
6 
     | 
    
         
             
              class BlockBody
         
     | 
| 
       3 
     | 
    
         
            -
                 
     | 
| 
       4 
     | 
    
         
            -
                 
     | 
| 
      
 7 
     | 
    
         
            +
                LiquidTagToken      = /\A\s*(\w+)\s*(.*?)\z/o
         
     | 
| 
      
 8 
     | 
    
         
            +
                FullToken           = /\A#{TagStart}#{WhitespaceControl}?(\s*)(\w+)(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
         
     | 
| 
      
 9 
     | 
    
         
            +
                ContentOfVariable   = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
         
     | 
| 
       5 
10 
     | 
    
         
             
                WhitespaceOrNothing = /\A\s*\z/
         
     | 
| 
       6 
     | 
    
         
            -
                TAGSTART 
     | 
| 
       7 
     | 
    
         
            -
                VARSTART 
     | 
| 
      
 11 
     | 
    
         
            +
                TAGSTART            = "{%"
         
     | 
| 
      
 12 
     | 
    
         
            +
                VARSTART            = "{{"
         
     | 
| 
       8 
13 
     | 
    
         | 
| 
       9 
14 
     | 
    
         
             
                attr_reader :nodelist
         
     | 
| 
       10 
15 
     | 
    
         | 
| 
       11 
16 
     | 
    
         
             
                def initialize
         
     | 
| 
       12 
17 
     | 
    
         
             
                  @nodelist = []
         
     | 
| 
       13 
     | 
    
         
            -
                  @blank 
     | 
| 
      
 18 
     | 
    
         
            +
                  @blank    = true
         
     | 
| 
       14 
19 
     | 
    
         
             
                end
         
     | 
| 
       15 
20 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
                def parse(tokenizer, parse_context)
         
     | 
| 
      
 21 
     | 
    
         
            +
                def parse(tokenizer, parse_context, &block)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  raise FrozenError, "can't modify frozen Liquid::BlockBody" if frozen?
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
       17 
24 
     | 
    
         
             
                  parse_context.line_number = tokenizer.line_number
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  if tokenizer.for_liquid_tag
         
     | 
| 
      
 27 
     | 
    
         
            +
                    parse_for_liquid_tag(tokenizer, parse_context, &block)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  else
         
     | 
| 
      
 29 
     | 
    
         
            +
                    parse_for_document(tokenizer, parse_context, &block)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def freeze
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @nodelist.freeze
         
     | 
| 
      
 35 
     | 
    
         
            +
                  super
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                private def parse_for_liquid_tag(tokenizer, parse_context)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  while (token = tokenizer.shift)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    unless token.empty? || token =~ WhitespaceOrNothing
         
     | 
| 
      
 41 
     | 
    
         
            +
                      unless token =~ LiquidTagToken
         
     | 
| 
      
 42 
     | 
    
         
            +
                        # line isn't empty but didn't match tag syntax, yield and let the
         
     | 
| 
      
 43 
     | 
    
         
            +
                        # caller raise a syntax error
         
     | 
| 
      
 44 
     | 
    
         
            +
                        return yield token, token
         
     | 
| 
      
 45 
     | 
    
         
            +
                      end
         
     | 
| 
      
 46 
     | 
    
         
            +
                      tag_name = Regexp.last_match(1)
         
     | 
| 
      
 47 
     | 
    
         
            +
                      markup   = Regexp.last_match(2)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      unless (tag = registered_tags[tag_name])
         
     | 
| 
      
 49 
     | 
    
         
            +
                        # end parsing if we reach an unknown tag and let the caller decide
         
     | 
| 
      
 50 
     | 
    
         
            +
                        # determine how to proceed
         
     | 
| 
      
 51 
     | 
    
         
            +
                        return yield tag_name, markup
         
     | 
| 
      
 52 
     | 
    
         
            +
                      end
         
     | 
| 
      
 53 
     | 
    
         
            +
                      new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
         
     | 
| 
      
 54 
     | 
    
         
            +
                      @blank &&= new_tag.blank?
         
     | 
| 
      
 55 
     | 
    
         
            +
                      @nodelist << new_tag
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
                    parse_context.line_number = tokenizer.line_number
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  yield nil, nil
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 64 
     | 
    
         
            +
                def self.unknown_tag_in_liquid_tag(tag, parse_context)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  Block.raise_unknown_tag(tag, 'liquid', '%}', parse_context)
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 69 
     | 
    
         
            +
                def self.raise_missing_tag_terminator(token, parse_context)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  raise SyntaxError, parse_context.locale.t("errors.syntax.tag_termination", token: token, tag_end: TagEnd.inspect)
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 74 
     | 
    
         
            +
                def self.raise_missing_variable_terminator(token, parse_context)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  raise SyntaxError, parse_context.locale.t("errors.syntax.variable_termination", token: token, tag_end: VariableEnd.inspect)
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 79 
     | 
    
         
            +
                def self.render_node(context, output, node)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  node.render_to_output_buffer(context, output)
         
     | 
| 
      
 81 
     | 
    
         
            +
                rescue => exc
         
     | 
| 
      
 82 
     | 
    
         
            +
                  blank_tag = !node.instance_of?(Variable) && node.blank?
         
     | 
| 
      
 83 
     | 
    
         
            +
                  rescue_render_node(context, output, node.line_number, exc, blank_tag)
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 87 
     | 
    
         
            +
                def self.rescue_render_node(context, output, line_number, exc, blank_tag)
         
     | 
| 
      
 88 
     | 
    
         
            +
                  case exc
         
     | 
| 
      
 89 
     | 
    
         
            +
                  when MemoryError
         
     | 
| 
      
 90 
     | 
    
         
            +
                    raise
         
     | 
| 
      
 91 
     | 
    
         
            +
                  when UndefinedVariable, UndefinedDropMethod, UndefinedFilter
         
     | 
| 
      
 92 
     | 
    
         
            +
                    context.handle_error(exc, line_number)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  else
         
     | 
| 
      
 94 
     | 
    
         
            +
                    error_message = context.handle_error(exc, line_number)
         
     | 
| 
      
 95 
     | 
    
         
            +
                    unless blank_tag # conditional for backwards compatibility
         
     | 
| 
      
 96 
     | 
    
         
            +
                      output << error_message
         
     | 
| 
      
 97 
     | 
    
         
            +
                    end
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                private def parse_liquid_tag(markup, parse_context)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  liquid_tag_tokenizer = parse_context.new_tokenizer(
         
     | 
| 
      
 103 
     | 
    
         
            +
                    markup, start_line_number: parse_context.line_number, for_liquid_tag: true
         
     | 
| 
      
 104 
     | 
    
         
            +
                  )
         
     | 
| 
      
 105 
     | 
    
         
            +
                  parse_for_liquid_tag(liquid_tag_tokenizer, parse_context) do |end_tag_name, _end_tag_markup|
         
     | 
| 
      
 106 
     | 
    
         
            +
                    if end_tag_name
         
     | 
| 
      
 107 
     | 
    
         
            +
                      BlockBody.unknown_tag_in_liquid_tag(end_tag_name, parse_context)
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                private def parse_for_document(tokenizer, parse_context)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  while (token = tokenizer.shift)
         
     | 
| 
       19 
114 
     | 
    
         
             
                    next if token.empty?
         
     | 
| 
       20 
115 
     | 
    
         
             
                    case
         
     | 
| 
       21 
116 
     | 
    
         
             
                    when token.start_with?(TAGSTART)
         
     | 
| 
       22 
117 
     | 
    
         
             
                      whitespace_handler(token, parse_context)
         
     | 
| 
       23 
118 
     | 
    
         
             
                      unless token =~ FullToken
         
     | 
| 
       24 
     | 
    
         
            -
                        raise_missing_tag_terminator(token, parse_context)
         
     | 
| 
      
 119 
     | 
    
         
            +
                        BlockBody.raise_missing_tag_terminator(token, parse_context)
         
     | 
| 
      
 120 
     | 
    
         
            +
                      end
         
     | 
| 
      
 121 
     | 
    
         
            +
                      tag_name = Regexp.last_match(2)
         
     | 
| 
      
 122 
     | 
    
         
            +
                      markup   = Regexp.last_match(4)
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                      if parse_context.line_number
         
     | 
| 
      
 125 
     | 
    
         
            +
                        # newlines inside the tag should increase the line number,
         
     | 
| 
      
 126 
     | 
    
         
            +
                        # particularly important for multiline {% liquid %} tags
         
     | 
| 
      
 127 
     | 
    
         
            +
                        parse_context.line_number += Regexp.last_match(1).count("\n") + Regexp.last_match(3).count("\n")
         
     | 
| 
      
 128 
     | 
    
         
            +
                      end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                      if tag_name == 'liquid'
         
     | 
| 
      
 131 
     | 
    
         
            +
                        parse_liquid_tag(markup, parse_context)
         
     | 
| 
      
 132 
     | 
    
         
            +
                        next
         
     | 
| 
       25 
133 
     | 
    
         
             
                      end
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
                       
     | 
| 
       28 
     | 
    
         
            -
                      # fetch the tag from registered blocks
         
     | 
| 
       29 
     | 
    
         
            -
                      unless tag = registered_tags[tag_name]
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                      unless (tag = registered_tags[tag_name])
         
     | 
| 
       30 
136 
     | 
    
         
             
                        # end parsing if we reach an unknown tag and let the caller decide
         
     | 
| 
       31 
137 
     | 
    
         
             
                        # determine how to proceed
         
     | 
| 
       32 
138 
     | 
    
         
             
                        return yield tag_name, markup
         
     | 
| 
         @@ -55,8 +161,12 @@ module Liquid 
     | 
|
| 
       55 
161 
     | 
    
         
             
                def whitespace_handler(token, parse_context)
         
     | 
| 
       56 
162 
     | 
    
         
             
                  if token[2] == WhitespaceControl
         
     | 
| 
       57 
163 
     | 
    
         
             
                    previous_token = @nodelist.last
         
     | 
| 
       58 
     | 
    
         
            -
                    if previous_token.is_a? 
     | 
| 
      
 164 
     | 
    
         
            +
                    if previous_token.is_a?(String)
         
     | 
| 
      
 165 
     | 
    
         
            +
                      first_byte = previous_token.getbyte(0)
         
     | 
| 
       59 
166 
     | 
    
         
             
                      previous_token.rstrip!
         
     | 
| 
      
 167 
     | 
    
         
            +
                      if previous_token.empty? && parse_context[:bug_compatible_whitespace_trimming] && first_byte
         
     | 
| 
      
 168 
     | 
    
         
            +
                        previous_token << first_byte
         
     | 
| 
      
 169 
     | 
    
         
            +
                      end
         
     | 
| 
       60 
170 
     | 
    
         
             
                    end
         
     | 
| 
       61 
171 
     | 
    
         
             
                  end
         
     | 
| 
       62 
172 
     | 
    
         
             
                  parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
         
     | 
| 
         @@ -66,58 +176,58 @@ module Liquid 
     | 
|
| 
       66 
176 
     | 
    
         
             
                  @blank
         
     | 
| 
       67 
177 
     | 
    
         
             
                end
         
     | 
| 
       68 
178 
     | 
    
         | 
| 
      
 179 
     | 
    
         
            +
                # Remove blank strings in the block body for a control flow tag (e.g. `if`, `for`, `case`, `unless`)
         
     | 
| 
      
 180 
     | 
    
         
            +
                # with a blank body.
         
     | 
| 
      
 181 
     | 
    
         
            +
                #
         
     | 
| 
      
 182 
     | 
    
         
            +
                # For example, in a conditional assignment like the following
         
     | 
| 
      
 183 
     | 
    
         
            +
                #
         
     | 
| 
      
 184 
     | 
    
         
            +
                # ```
         
     | 
| 
      
 185 
     | 
    
         
            +
                # {% if size > max_size %}
         
     | 
| 
      
 186 
     | 
    
         
            +
                #   {% assign size = max_size %}
         
     | 
| 
      
 187 
     | 
    
         
            +
                # {% endif %}
         
     | 
| 
      
 188 
     | 
    
         
            +
                # ```
         
     | 
| 
      
 189 
     | 
    
         
            +
                #
         
     | 
| 
      
 190 
     | 
    
         
            +
                # we assume the intention wasn't to output the blank spaces in the `if` tag's block body, so this method
         
     | 
| 
      
 191 
     | 
    
         
            +
                # will remove them to reduce the render output size.
         
     | 
| 
      
 192 
     | 
    
         
            +
                #
         
     | 
| 
      
 193 
     | 
    
         
            +
                # Note that it is now preferred to use the `liquid` tag for this use case.
         
     | 
| 
      
 194 
     | 
    
         
            +
                def remove_blank_strings
         
     | 
| 
      
 195 
     | 
    
         
            +
                  raise "remove_blank_strings only support being called on a blank block body" unless @blank
         
     | 
| 
      
 196 
     | 
    
         
            +
                  @nodelist.reject! { |node| node.instance_of?(String) }
         
     | 
| 
      
 197 
     | 
    
         
            +
                end
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
       69 
199 
     | 
    
         
             
                def render(context)
         
     | 
| 
       70 
     | 
    
         
            -
                   
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
      
 200 
     | 
    
         
            +
                  render_to_output_buffer(context, +'')
         
     | 
| 
      
 201 
     | 
    
         
            +
                end
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
                def render_to_output_buffer(context, output)
         
     | 
| 
      
 204 
     | 
    
         
            +
                  freeze unless frozen?
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                  context.resource_limits.increment_render_score(@nodelist.length)
         
     | 
| 
       72 
207 
     | 
    
         | 
| 
       73 
208 
     | 
    
         
             
                  idx = 0
         
     | 
| 
       74 
     | 
    
         
            -
                  while node = @nodelist[idx]
         
     | 
| 
       75 
     | 
    
         
            -
                     
     | 
| 
       76 
     | 
    
         
            -
                    when String
         
     | 
| 
       77 
     | 
    
         
            -
                      check_resources(context, node)
         
     | 
| 
      
 209 
     | 
    
         
            +
                  while (node = @nodelist[idx])
         
     | 
| 
      
 210 
     | 
    
         
            +
                    if node.instance_of?(String)
         
     | 
| 
       78 
211 
     | 
    
         
             
                      output << node
         
     | 
| 
       79 
     | 
    
         
            -
                     
     | 
| 
       80 
     | 
    
         
            -
                       
     | 
| 
       81 
     | 
    
         
            -
                    when Block
         
     | 
| 
       82 
     | 
    
         
            -
                      render_node_to_output(node, output, context, node.blank?)
         
     | 
| 
       83 
     | 
    
         
            -
                      break if context.interrupt? # might have happened in a for-block
         
     | 
| 
       84 
     | 
    
         
            -
                    when Continue, Break
         
     | 
| 
      
 212 
     | 
    
         
            +
                    else
         
     | 
| 
      
 213 
     | 
    
         
            +
                      render_node(context, output, node)
         
     | 
| 
       85 
214 
     | 
    
         
             
                      # If we get an Interrupt that means the block must stop processing. An
         
     | 
| 
       86 
215 
     | 
    
         
             
                      # Interrupt is any command that stops block execution such as {% break %}
         
     | 
| 
       87 
     | 
    
         
            -
                      # or {% continue %}
         
     | 
| 
       88 
     | 
    
         
            -
                      context. 
     | 
| 
       89 
     | 
    
         
            -
                      break
         
     | 
| 
       90 
     | 
    
         
            -
                    else # Other non-Block tags
         
     | 
| 
       91 
     | 
    
         
            -
                      render_node_to_output(node, output, context)
         
     | 
| 
       92 
     | 
    
         
            -
                      break if context.interrupt? # might have happened through an include
         
     | 
| 
      
 216 
     | 
    
         
            +
                      # or {% continue %}. These tags may also occur through Block or Include tags.
         
     | 
| 
      
 217 
     | 
    
         
            +
                      break if context.interrupt? # might have happened in a for-block
         
     | 
| 
       93 
218 
     | 
    
         
             
                    end
         
     | 
| 
       94 
219 
     | 
    
         
             
                    idx += 1
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
                    context.resource_limits.increment_write_score(output)
         
     | 
| 
       95 
222 
     | 
    
         
             
                  end
         
     | 
| 
       96 
223 
     | 
    
         | 
| 
       97 
     | 
    
         
            -
                  output 
     | 
| 
      
 224 
     | 
    
         
            +
                  output
         
     | 
| 
       98 
225 
     | 
    
         
             
                end
         
     | 
| 
       99 
226 
     | 
    
         | 
| 
       100 
227 
     | 
    
         
             
                private
         
     | 
| 
       101 
228 
     | 
    
         | 
| 
       102 
     | 
    
         
            -
                def  
     | 
| 
       103 
     | 
    
         
            -
                   
     | 
| 
       104 
     | 
    
         
            -
                  node_output = node_output.is_a?(Array) ? node_output.join : node_output.to_s
         
     | 
| 
       105 
     | 
    
         
            -
                  check_resources(context, node_output)
         
     | 
| 
       106 
     | 
    
         
            -
                  output << node_output unless skip_output
         
     | 
| 
       107 
     | 
    
         
            -
                rescue MemoryError => e
         
     | 
| 
       108 
     | 
    
         
            -
                  raise e
         
     | 
| 
       109 
     | 
    
         
            -
                rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
         
     | 
| 
       110 
     | 
    
         
            -
                  context.handle_error(e, node.line_number)
         
     | 
| 
       111 
     | 
    
         
            -
                  output << nil
         
     | 
| 
       112 
     | 
    
         
            -
                rescue ::StandardError => e
         
     | 
| 
       113 
     | 
    
         
            -
                  line_number = node.is_a?(String) ? nil : node.line_number
         
     | 
| 
       114 
     | 
    
         
            -
                  output << context.handle_error(e, line_number)
         
     | 
| 
       115 
     | 
    
         
            -
                end
         
     | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
       117 
     | 
    
         
            -
                def check_resources(context, node_output)
         
     | 
| 
       118 
     | 
    
         
            -
                  context.resource_limits.render_length += node_output.length
         
     | 
| 
       119 
     | 
    
         
            -
                  return unless context.resource_limits.reached?
         
     | 
| 
       120 
     | 
    
         
            -
                  raise MemoryError.new("Memory limits exceeded".freeze)
         
     | 
| 
      
 229 
     | 
    
         
            +
                def render_node(context, output, node)
         
     | 
| 
      
 230 
     | 
    
         
            +
                  BlockBody.render_node(context, output, node)
         
     | 
| 
       121 
231 
     | 
    
         
             
                end
         
     | 
| 
       122 
232 
     | 
    
         | 
| 
       123 
233 
     | 
    
         
             
                def create_variable(token, parse_context)
         
     | 
| 
         @@ -125,15 +235,17 @@ module Liquid 
     | 
|
| 
       125 
235 
     | 
    
         
             
                    markup = content.first
         
     | 
| 
       126 
236 
     | 
    
         
             
                    return Variable.new(markup, parse_context)
         
     | 
| 
       127 
237 
     | 
    
         
             
                  end
         
     | 
| 
       128 
     | 
    
         
            -
                  raise_missing_variable_terminator(token, parse_context)
         
     | 
| 
      
 238 
     | 
    
         
            +
                  BlockBody.raise_missing_variable_terminator(token, parse_context)
         
     | 
| 
       129 
239 
     | 
    
         
             
                end
         
     | 
| 
       130 
240 
     | 
    
         | 
| 
      
 241 
     | 
    
         
            +
                # @deprecated Use {.raise_missing_tag_terminator} instead
         
     | 
| 
       131 
242 
     | 
    
         
             
                def raise_missing_tag_terminator(token, parse_context)
         
     | 
| 
       132 
     | 
    
         
            -
                   
     | 
| 
      
 243 
     | 
    
         
            +
                  BlockBody.raise_missing_tag_terminator(token, parse_context)
         
     | 
| 
       133 
244 
     | 
    
         
             
                end
         
     | 
| 
       134 
245 
     | 
    
         | 
| 
      
 246 
     | 
    
         
            +
                # @deprecated Use {.raise_missing_variable_terminator} instead
         
     | 
| 
       135 
247 
     | 
    
         
             
                def raise_missing_variable_terminator(token, parse_context)
         
     | 
| 
       136 
     | 
    
         
            -
                   
     | 
| 
      
 248 
     | 
    
         
            +
                  BlockBody.raise_missing_variable_terminator(token, parse_context)
         
     | 
| 
       137 
249 
     | 
    
         
             
                end
         
     | 
| 
       138 
250 
     | 
    
         | 
| 
       139 
251 
     | 
    
         
             
                def registered_tags
         
     | 
    
        data/lib/liquid/condition.rb
    CHANGED
    
    | 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Liquid
         
     | 
| 
       2 
4 
     | 
    
         
             
              # Container for liquid nodes which conveniently wraps decision making logic
         
     | 
| 
       3 
5 
     | 
    
         
             
              #
         
     | 
| 
         @@ -6,36 +8,55 @@ module Liquid 
     | 
|
| 
       6 
8 
     | 
    
         
             
              #   c = Condition.new(1, '==', 1)
         
     | 
| 
       7 
9 
     | 
    
         
             
              #   c.evaluate #=> true
         
     | 
| 
       8 
10 
     | 
    
         
             
              #
         
     | 
| 
       9 
     | 
    
         
            -
              class Condition  
     | 
| 
      
 11 
     | 
    
         
            +
              class Condition # :nodoc:
         
     | 
| 
       10 
12 
     | 
    
         
             
                @@operators = {
         
     | 
| 
       11 
     | 
    
         
            -
                  '==' 
     | 
| 
       12 
     | 
    
         
            -
                  '!=' 
     | 
| 
       13 
     | 
    
         
            -
                  '<>' 
     | 
| 
       14 
     | 
    
         
            -
                  '<' 
     | 
| 
       15 
     | 
    
         
            -
                  '>' 
     | 
| 
       16 
     | 
    
         
            -
                  '>=' 
     | 
| 
       17 
     | 
    
         
            -
                  '<=' 
     | 
| 
       18 
     | 
    
         
            -
                  'contains' 
     | 
| 
      
 13 
     | 
    
         
            +
                  '==' => ->(cond, left, right) {  cond.send(:equal_variables, left, right) },
         
     | 
| 
      
 14 
     | 
    
         
            +
                  '!=' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
         
     | 
| 
      
 15 
     | 
    
         
            +
                  '<>' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
         
     | 
| 
      
 16 
     | 
    
         
            +
                  '<' => :<,
         
     | 
| 
      
 17 
     | 
    
         
            +
                  '>' => :>,
         
     | 
| 
      
 18 
     | 
    
         
            +
                  '>=' => :>=,
         
     | 
| 
      
 19 
     | 
    
         
            +
                  '<=' => :<=,
         
     | 
| 
      
 20 
     | 
    
         
            +
                  'contains' => lambda do |_cond, left, right|
         
     | 
| 
       19 
21 
     | 
    
         
             
                    if left && right && left.respond_to?(:include?)
         
     | 
| 
       20 
22 
     | 
    
         
             
                      right = right.to_s if left.is_a?(String)
         
     | 
| 
       21 
23 
     | 
    
         
             
                      left.include?(right)
         
     | 
| 
       22 
24 
     | 
    
         
             
                    else
         
     | 
| 
       23 
25 
     | 
    
         
             
                      false
         
     | 
| 
       24 
26 
     | 
    
         
             
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end,
         
     | 
| 
      
 28 
     | 
    
         
            +
                }
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                class MethodLiteral
         
     | 
| 
      
 31 
     | 
    
         
            +
                  attr_reader :method_name, :to_s
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def initialize(method_name, to_s)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    @method_name = method_name
         
     | 
| 
      
 35 
     | 
    
         
            +
                    @to_s = to_s
         
     | 
| 
       25 
36 
     | 
    
         
             
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                @@method_literals = {
         
     | 
| 
      
 40 
     | 
    
         
            +
                  'blank' => MethodLiteral.new(:blank?, '').freeze,
         
     | 
| 
      
 41 
     | 
    
         
            +
                  'empty' => MethodLiteral.new(:empty?, '').freeze,
         
     | 
| 
       26 
42 
     | 
    
         
             
                }
         
     | 
| 
       27 
43 
     | 
    
         | 
| 
       28 
44 
     | 
    
         
             
                def self.operators
         
     | 
| 
       29 
45 
     | 
    
         
             
                  @@operators
         
     | 
| 
       30 
46 
     | 
    
         
             
                end
         
     | 
| 
       31 
47 
     | 
    
         | 
| 
      
 48 
     | 
    
         
            +
                def self.parse_expression(parse_context, markup)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  @@method_literals[markup] || parse_context.parse_expression(markup)
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
       32 
52 
     | 
    
         
             
                attr_reader :attachment, :child_condition
         
     | 
| 
       33 
53 
     | 
    
         
             
                attr_accessor :left, :operator, :right
         
     | 
| 
       34 
54 
     | 
    
         | 
| 
       35 
55 
     | 
    
         
             
                def initialize(left = nil, operator = nil, right = nil)
         
     | 
| 
       36 
     | 
    
         
            -
                  @left 
     | 
| 
      
 56 
     | 
    
         
            +
                  @left     = left
         
     | 
| 
       37 
57 
     | 
    
         
             
                  @operator = operator
         
     | 
| 
       38 
     | 
    
         
            -
                  @right 
     | 
| 
      
 58 
     | 
    
         
            +
                  @right    = right
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
       39 
60 
     | 
    
         
             
                  @child_relation  = nil
         
     | 
| 
       40 
61 
     | 
    
         
             
                  @child_condition = nil
         
     | 
| 
       41 
62 
     | 
    
         
             
                end
         
     | 
| 
         @@ -60,12 +81,12 @@ module Liquid 
     | 
|
| 
       60 
81 
     | 
    
         
             
                end
         
     | 
| 
       61 
82 
     | 
    
         | 
| 
       62 
83 
     | 
    
         
             
                def or(condition)
         
     | 
| 
       63 
     | 
    
         
            -
                  @child_relation 
     | 
| 
      
 84 
     | 
    
         
            +
                  @child_relation  = :or
         
     | 
| 
       64 
85 
     | 
    
         
             
                  @child_condition = condition
         
     | 
| 
       65 
86 
     | 
    
         
             
                end
         
     | 
| 
       66 
87 
     | 
    
         | 
| 
       67 
88 
     | 
    
         
             
                def and(condition)
         
     | 
| 
       68 
     | 
    
         
            -
                  @child_relation 
     | 
| 
      
 89 
     | 
    
         
            +
                  @child_relation  = :and
         
     | 
| 
       69 
90 
     | 
    
         
             
                  @child_condition = condition
         
     | 
| 
       70 
91 
     | 
    
         
             
                end
         
     | 
| 
       71 
92 
     | 
    
         | 
| 
         @@ -78,7 +99,7 @@ module Liquid 
     | 
|
| 
       78 
99 
     | 
    
         
             
                end
         
     | 
| 
       79 
100 
     | 
    
         | 
| 
       80 
101 
     | 
    
         
             
                def inspect
         
     | 
| 
       81 
     | 
    
         
            -
                  "#<Condition #{[@left, @operator, @right].compact.join(' ' 
     | 
| 
      
 102 
     | 
    
         
            +
                  "#<Condition #{[@left, @operator, @right].compact.join(' ')}>"
         
     | 
| 
       82 
103 
     | 
    
         
             
                end
         
     | 
| 
       83 
104 
     | 
    
         | 
| 
       84 
105 
     | 
    
         
             
                protected
         
     | 
| 
         @@ -88,7 +109,7 @@ module Liquid 
     | 
|
| 
       88 
109 
     | 
    
         
             
                private
         
     | 
| 
       89 
110 
     | 
    
         | 
| 
       90 
111 
     | 
    
         
             
                def equal_variables(left, right)
         
     | 
| 
       91 
     | 
    
         
            -
                  if left.is_a?( 
     | 
| 
      
 112 
     | 
    
         
            +
                  if left.is_a?(MethodLiteral)
         
     | 
| 
       92 
113 
     | 
    
         
             
                    if right.respond_to?(left.method_name)
         
     | 
| 
       93 
114 
     | 
    
         
             
                      return right.send(left.method_name)
         
     | 
| 
       94 
115 
     | 
    
         
             
                    else
         
     | 
| 
         @@ -96,7 +117,7 @@ module Liquid 
     | 
|
| 
       96 
117 
     | 
    
         
             
                    end
         
     | 
| 
       97 
118 
     | 
    
         
             
                  end
         
     | 
| 
       98 
119 
     | 
    
         | 
| 
       99 
     | 
    
         
            -
                  if right.is_a?( 
     | 
| 
      
 120 
     | 
    
         
            +
                  if right.is_a?(MethodLiteral)
         
     | 
| 
       100 
121 
     | 
    
         
             
                    if left.respond_to?(right.method_name)
         
     | 
| 
       101 
122 
     | 
    
         
             
                      return left.send(right.method_name)
         
     | 
| 
       102 
123 
     | 
    
         
             
                    else
         
     | 
| 
         @@ -113,10 +134,10 @@ module Liquid 
     | 
|
| 
       113 
134 
     | 
    
         
             
                  # return this as the result.
         
     | 
| 
       114 
135 
     | 
    
         
             
                  return context.evaluate(left) if op.nil?
         
     | 
| 
       115 
136 
     | 
    
         | 
| 
       116 
     | 
    
         
            -
                  left 
     | 
| 
       117 
     | 
    
         
            -
                  right = context.evaluate(right)
         
     | 
| 
      
 137 
     | 
    
         
            +
                  left  = Liquid::Utils.to_liquid_value(context.evaluate(left))
         
     | 
| 
      
 138 
     | 
    
         
            +
                  right = Liquid::Utils.to_liquid_value(context.evaluate(right))
         
     | 
| 
       118 
139 
     | 
    
         | 
| 
       119 
     | 
    
         
            -
                  operation = self.class.operators[op] || raise(Liquid::ArgumentError 
     | 
| 
      
 140 
     | 
    
         
            +
                  operation = self.class.operators[op] || raise(Liquid::ArgumentError, "Unknown operator #{op}")
         
     | 
| 
       120 
141 
     | 
    
         | 
| 
       121 
142 
     | 
    
         
             
                  if operation.respond_to?(:call)
         
     | 
| 
       122 
143 
     | 
    
         
             
                    operation.call(self, left, right)
         
     | 
| 
         @@ -124,7 +145,7 @@ module Liquid 
     | 
|
| 
       124 
145 
     | 
    
         
             
                    begin
         
     | 
| 
       125 
146 
     | 
    
         
             
                      left.send(operation, right)
         
     | 
| 
       126 
147 
     | 
    
         
             
                    rescue ::ArgumentError => e
         
     | 
| 
       127 
     | 
    
         
            -
                      raise Liquid::ArgumentError 
     | 
| 
      
 148 
     | 
    
         
            +
                      raise Liquid::ArgumentError, e.message
         
     | 
| 
       128 
149 
     | 
    
         
             
                    end
         
     | 
| 
       129 
150 
     | 
    
         
             
                  end
         
     | 
| 
       130 
151 
     | 
    
         
             
                end
         
     |