liquid 3.0.6 → 5.4.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 +5 -5
 - data/History.md +243 -58
 - data/README.md +43 -4
 - data/lib/liquid/block.rb +57 -123
 - data/lib/liquid/block_body.rb +217 -85
 - data/lib/liquid/condition.rb +92 -45
 - data/lib/liquid/context.rb +154 -89
 - data/lib/liquid/document.rb +57 -9
 - data/lib/liquid/drop.rb +20 -17
 - data/lib/liquid/errors.rb +27 -29
 - data/lib/liquid/expression.rb +32 -20
 - data/lib/liquid/extensions.rb +21 -7
 - data/lib/liquid/file_system.rb +17 -15
 - data/lib/liquid/forloop_drop.rb +92 -0
 - data/lib/liquid/i18n.rb +10 -8
 - data/lib/liquid/interrupts.rb +4 -3
 - data/lib/liquid/lexer.rb +37 -26
 - data/lib/liquid/locales/en.yml +13 -6
 - data/lib/liquid/parse_context.rb +54 -0
 - data/lib/liquid/parse_tree_visitor.rb +42 -0
 - data/lib/liquid/parser.rb +30 -18
 - data/lib/liquid/parser_switching.rb +20 -6
 - data/lib/liquid/partial_cache.rb +24 -0
 - data/lib/liquid/profiler/hooks.rb +26 -14
 - data/lib/liquid/profiler.rb +72 -92
 - data/lib/liquid/range_lookup.rb +28 -3
 - data/lib/liquid/registers.rb +51 -0
 - data/lib/liquid/resource_limits.rb +62 -0
 - data/lib/liquid/standardfilters.rb +715 -132
 - data/lib/liquid/strainer_factory.rb +41 -0
 - data/lib/liquid/strainer_template.rb +62 -0
 - data/lib/liquid/tablerowloop_drop.rb +121 -0
 - data/lib/liquid/tag/disableable.rb +22 -0
 - data/lib/liquid/tag/disabler.rb +21 -0
 - data/lib/liquid/tag.rb +35 -12
 - data/lib/liquid/tags/assign.rb +57 -18
 - data/lib/liquid/tags/break.rb +15 -5
 - data/lib/liquid/tags/capture.rb +24 -18
 - data/lib/liquid/tags/case.rb +79 -30
 - data/lib/liquid/tags/comment.rb +19 -4
 - data/lib/liquid/tags/continue.rb +16 -12
 - data/lib/liquid/tags/cycle.rb +47 -27
 - data/lib/liquid/tags/decrement.rb +23 -24
 - data/lib/liquid/tags/echo.rb +41 -0
 - data/lib/liquid/tags/for.rb +155 -124
 - data/lib/liquid/tags/if.rb +97 -63
 - data/lib/liquid/tags/ifchanged.rb +11 -12
 - data/lib/liquid/tags/include.rb +82 -73
 - data/lib/liquid/tags/increment.rb +23 -17
 - data/lib/liquid/tags/inline_comment.rb +43 -0
 - data/lib/liquid/tags/raw.rb +50 -8
 - data/lib/liquid/tags/render.rb +109 -0
 - data/lib/liquid/tags/table_row.rb +57 -41
 - data/lib/liquid/tags/unless.rb +38 -20
 - data/lib/liquid/template.rb +71 -103
 - data/lib/liquid/template_factory.rb +9 -0
 - data/lib/liquid/tokenizer.rb +39 -0
 - data/lib/liquid/usage.rb +8 -0
 - data/lib/liquid/utils.rb +63 -9
 - data/lib/liquid/variable.rb +74 -56
 - data/lib/liquid/variable_lookup.rb +31 -15
 - data/lib/liquid/version.rb +3 -1
 - data/lib/liquid.rb +27 -12
 - metadata +30 -106
 - data/lib/liquid/module_ex.rb +0 -62
 - data/lib/liquid/strainer.rb +0 -59
 - data/lib/liquid/token.rb +0 -18
 - data/test/fixtures/en_locale.yml +0 -9
 - data/test/integration/assign_test.rb +0 -48
 - data/test/integration/blank_test.rb +0 -106
 - data/test/integration/capture_test.rb +0 -50
 - data/test/integration/context_test.rb +0 -32
 - data/test/integration/drop_test.rb +0 -271
 - data/test/integration/error_handling_test.rb +0 -207
 - data/test/integration/filter_test.rb +0 -138
 - data/test/integration/hash_ordering_test.rb +0 -23
 - data/test/integration/output_test.rb +0 -124
 - data/test/integration/parsing_quirks_test.rb +0 -116
 - data/test/integration/render_profiling_test.rb +0 -154
 - data/test/integration/security_test.rb +0 -64
 - data/test/integration/standard_filter_test.rb +0 -396
 - data/test/integration/tags/break_tag_test.rb +0 -16
 - data/test/integration/tags/continue_tag_test.rb +0 -16
 - data/test/integration/tags/for_tag_test.rb +0 -375
 - data/test/integration/tags/if_else_tag_test.rb +0 -190
 - data/test/integration/tags/include_tag_test.rb +0 -234
 - data/test/integration/tags/increment_tag_test.rb +0 -24
 - data/test/integration/tags/raw_tag_test.rb +0 -25
 - data/test/integration/tags/standard_tag_test.rb +0 -297
 - data/test/integration/tags/statements_test.rb +0 -113
 - data/test/integration/tags/table_row_test.rb +0 -63
 - data/test/integration/tags/unless_else_tag_test.rb +0 -26
 - data/test/integration/template_test.rb +0 -182
 - data/test/integration/variable_test.rb +0 -82
 - data/test/test_helper.rb +0 -89
 - data/test/unit/block_unit_test.rb +0 -55
 - data/test/unit/condition_unit_test.rb +0 -149
 - data/test/unit/context_unit_test.rb +0 -492
 - data/test/unit/file_system_unit_test.rb +0 -35
 - data/test/unit/i18n_unit_test.rb +0 -37
 - data/test/unit/lexer_unit_test.rb +0 -48
 - data/test/unit/module_ex_unit_test.rb +0 -87
 - data/test/unit/parser_unit_test.rb +0 -82
 - data/test/unit/regexp_unit_test.rb +0 -44
 - data/test/unit/strainer_unit_test.rb +0 -69
 - data/test/unit/tag_unit_test.rb +0 -16
 - data/test/unit/tags/case_tag_unit_test.rb +0 -10
 - data/test/unit/tags/for_tag_unit_test.rb +0 -13
 - data/test/unit/tags/if_tag_unit_test.rb +0 -8
 - data/test/unit/template_unit_test.rb +0 -69
 - data/test/unit/tokenizer_unit_test.rb +0 -38
 - data/test/unit/variable_unit_test.rb +0 -145
 - /data/{MIT-LICENSE → LICENSE} +0 -0
 
    
        data/lib/liquid/utils.rb
    CHANGED
    
    | 
         @@ -1,27 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Liquid
         
     | 
| 
       2 
4 
     | 
    
         
             
              module Utils
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
5 
     | 
    
         
             
                def self.slice_collection(collection, from, to)
         
     | 
| 
       5 
     | 
    
         
            -
                  if (from != 0 || to 
     | 
| 
      
 6 
     | 
    
         
            +
                  if (from != 0 || !to.nil?) && collection.respond_to?(:load_slice)
         
     | 
| 
       6 
7 
     | 
    
         
             
                    collection.load_slice(from, to)
         
     | 
| 
       7 
8 
     | 
    
         
             
                  else
         
     | 
| 
       8 
9 
     | 
    
         
             
                    slice_collection_using_each(collection, from, to)
         
     | 
| 
       9 
10 
     | 
    
         
             
                  end
         
     | 
| 
       10 
11 
     | 
    
         
             
                end
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
                def self.non_blank_string?(collection)
         
     | 
| 
       13 
     | 
    
         
            -
                  collection.is_a?(String) && collection != ''.freeze
         
     | 
| 
       14 
     | 
    
         
            -
                end
         
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
13 
     | 
    
         
             
                def self.slice_collection_using_each(collection, from, to)
         
     | 
| 
       17 
14 
     | 
    
         
             
                  segments = []
         
     | 
| 
       18 
     | 
    
         
            -
                  index 
     | 
| 
      
 15 
     | 
    
         
            +
                  index    = 0
         
     | 
| 
       19 
16 
     | 
    
         | 
| 
       20 
17 
     | 
    
         
             
                  # Maintains Ruby 1.8.7 String#each behaviour on 1.9
         
     | 
| 
       21 
     | 
    
         
            -
                   
     | 
| 
      
 18 
     | 
    
         
            +
                  if collection.is_a?(String)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    return collection.empty? ? [] : [collection]
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  return [] unless collection.respond_to?(:each)
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         
             
                  collection.each do |item|
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
24 
     | 
    
         
             
                    if to && to <= index
         
     | 
| 
       26 
25 
     | 
    
         
             
                      break
         
     | 
| 
       27 
26 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -35,5 +34,60 @@ module Liquid 
     | 
|
| 
       35 
34 
     | 
    
         | 
| 
       36 
35 
     | 
    
         
             
                  segments
         
     | 
| 
       37 
36 
     | 
    
         
             
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def self.to_integer(num)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  return num if num.is_a?(Integer)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  num = num.to_s
         
     | 
| 
      
 41 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 42 
     | 
    
         
            +
                    Integer(num)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  rescue ::ArgumentError
         
     | 
| 
      
 44 
     | 
    
         
            +
                    raise Liquid::ArgumentError, "invalid integer"
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def self.to_number(obj)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  case obj
         
     | 
| 
      
 50 
     | 
    
         
            +
                  when Float
         
     | 
| 
      
 51 
     | 
    
         
            +
                    BigDecimal(obj.to_s)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  when Numeric
         
     | 
| 
      
 53 
     | 
    
         
            +
                    obj
         
     | 
| 
      
 54 
     | 
    
         
            +
                  when String
         
     | 
| 
      
 55 
     | 
    
         
            +
                    /\A-?\d+\.\d+\z/.match?(obj.strip) ? BigDecimal(obj) : obj.to_i
         
     | 
| 
      
 56 
     | 
    
         
            +
                  else
         
     | 
| 
      
 57 
     | 
    
         
            +
                    if obj.respond_to?(:to_number)
         
     | 
| 
      
 58 
     | 
    
         
            +
                      obj.to_number
         
     | 
| 
      
 59 
     | 
    
         
            +
                    else
         
     | 
| 
      
 60 
     | 
    
         
            +
                      0
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                def self.to_date(obj)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  return obj if obj.respond_to?(:strftime)
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  if obj.is_a?(String)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    return nil if obj.empty?
         
     | 
| 
      
 70 
     | 
    
         
            +
                    obj = obj.downcase
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  case obj
         
     | 
| 
      
 74 
     | 
    
         
            +
                  when 'now', 'today'
         
     | 
| 
      
 75 
     | 
    
         
            +
                    Time.now
         
     | 
| 
      
 76 
     | 
    
         
            +
                  when /\A\d+\z/, Integer
         
     | 
| 
      
 77 
     | 
    
         
            +
                    Time.at(obj.to_i)
         
     | 
| 
      
 78 
     | 
    
         
            +
                  when String
         
     | 
| 
      
 79 
     | 
    
         
            +
                    Time.parse(obj)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                rescue ::ArgumentError
         
     | 
| 
      
 82 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                def self.to_liquid_value(obj)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  # Enable "obj" to represent itself as a primitive value like integer, string, or boolean
         
     | 
| 
      
 87 
     | 
    
         
            +
                  return obj.to_liquid_value if obj.respond_to?(:to_liquid_value)
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                  # Otherwise return the object itself
         
     | 
| 
      
 90 
     | 
    
         
            +
                  obj
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
       38 
92 
     | 
    
         
             
              end
         
     | 
| 
       39 
93 
     | 
    
         
             
            end
         
     | 
    
        data/lib/liquid/variable.rb
    CHANGED
    
    | 
         @@ -1,5 +1,6 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            module Liquid
         
     | 
| 
       3 
4 
     | 
    
         
             
              # Holds variables. Variables are only loaded "just in time"
         
     | 
| 
       4 
5 
     | 
    
         
             
              # and are not evaluated as part of the render stage
         
     | 
| 
       5 
6 
     | 
    
         
             
              #
         
     | 
| 
         @@ -11,18 +12,25 @@ module Liquid 
     | 
|
| 
       11 
12 
     | 
    
         
             
              #   {{ user | link }}
         
     | 
| 
       12 
13 
     | 
    
         
             
              #
         
     | 
| 
       13 
14 
     | 
    
         
             
              class Variable
         
     | 
| 
       14 
     | 
    
         
            -
                 
     | 
| 
       15 
     | 
    
         
            -
                 
     | 
| 
       16 
     | 
    
         
            -
                 
     | 
| 
       17 
     | 
    
         
            -
                 
     | 
| 
      
 15 
     | 
    
         
            +
                FilterMarkupRegex        = /#{FilterSeparator}\s*(.*)/om
         
     | 
| 
      
 16 
     | 
    
         
            +
                FilterParser             = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
         
     | 
| 
      
 17 
     | 
    
         
            +
                FilterArgsRegex          = /(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o
         
     | 
| 
      
 18 
     | 
    
         
            +
                JustTagAttributes        = /\A#{TagAttributes}\z/o
         
     | 
| 
      
 19 
     | 
    
         
            +
                MarkupWithQuotedFragment = /(#{QuotedFragment})(.*)/om
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                attr_accessor :filters, :name, :line_number
         
     | 
| 
      
 22 
     | 
    
         
            +
                attr_reader :parse_context
         
     | 
| 
      
 23 
     | 
    
         
            +
                alias_method :options, :parse_context
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
       18 
25 
     | 
    
         
             
                include ParserSwitching
         
     | 
| 
       19 
26 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                def initialize(markup,  
     | 
| 
       21 
     | 
    
         
            -
                  @markup 
     | 
| 
       22 
     | 
    
         
            -
                  @name 
     | 
| 
       23 
     | 
    
         
            -
                  @ 
     | 
| 
      
 27 
     | 
    
         
            +
                def initialize(markup, parse_context)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @markup        = markup
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @name          = nil
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @parse_context = parse_context
         
     | 
| 
      
 31 
     | 
    
         
            +
                  @line_number   = parse_context.line_number
         
     | 
| 
       24 
32 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
                   
     | 
| 
      
 33 
     | 
    
         
            +
                  strict_parse_with_error_mode_fallback(markup)
         
     | 
| 
       26 
34 
     | 
    
         
             
                end
         
     | 
| 
       27 
35 
     | 
    
         | 
| 
       28 
36 
     | 
    
         
             
                def raw
         
     | 
| 
         @@ -35,35 +43,29 @@ module Liquid 
     | 
|
| 
       35 
43 
     | 
    
         | 
| 
       36 
44 
     | 
    
         
             
                def lax_parse(markup)
         
     | 
| 
       37 
45 
     | 
    
         
             
                  @filters = []
         
     | 
| 
       38 
     | 
    
         
            -
                   
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                      end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  return unless markup =~ MarkupWithQuotedFragment
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  name_markup   = Regexp.last_match(1)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  filter_markup = Regexp.last_match(2)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @name         = parse_context.parse_expression(name_markup)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  if filter_markup =~ FilterMarkupRegex
         
     | 
| 
      
 52 
     | 
    
         
            +
                    filters = Regexp.last_match(1).scan(FilterParser)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    filters.each do |f|
         
     | 
| 
      
 54 
     | 
    
         
            +
                      next unless f =~ /\w+/
         
     | 
| 
      
 55 
     | 
    
         
            +
                      filtername = Regexp.last_match(0)
         
     | 
| 
      
 56 
     | 
    
         
            +
                      filterargs = f.scan(FilterArgsRegex).flatten
         
     | 
| 
      
 57 
     | 
    
         
            +
                      @filters << parse_filter_expressions(filtername, filterargs)
         
     | 
| 
       51 
58 
     | 
    
         
             
                    end
         
     | 
| 
       52 
59 
     | 
    
         
             
                  end
         
     | 
| 
       53 
60 
     | 
    
         
             
                end
         
     | 
| 
       54 
61 
     | 
    
         | 
| 
       55 
62 
     | 
    
         
             
                def strict_parse(markup)
         
     | 
| 
       56 
     | 
    
         
            -
                  # Very simple valid cases
         
     | 
| 
       57 
     | 
    
         
            -
                  if markup =~ EasyParse
         
     | 
| 
       58 
     | 
    
         
            -
                    @name = Expression.parse($1)
         
     | 
| 
       59 
     | 
    
         
            -
                    @filters = []
         
     | 
| 
       60 
     | 
    
         
            -
                    return
         
     | 
| 
       61 
     | 
    
         
            -
                  end
         
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
63 
     | 
    
         
             
                  @filters = []
         
     | 
| 
       64 
64 
     | 
    
         
             
                  p = Parser.new(markup)
         
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
                   
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  return if p.look(:end_of_string)
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  @name = parse_context.parse_expression(p.expression)
         
     | 
| 
       67 
69 
     | 
    
         
             
                  while p.consume?(:pipe)
         
     | 
| 
       68 
70 
     | 
    
         
             
                    filtername = p.consume(:id)
         
     | 
| 
       69 
71 
     | 
    
         
             
                    filterargs = p.consume?(:colon) ? parse_filterargs(p) : []
         
     | 
| 
         @@ -76,38 +78,62 @@ module Liquid 
     | 
|
| 
       76 
78 
     | 
    
         
             
                  # first argument
         
     | 
| 
       77 
79 
     | 
    
         
             
                  filterargs = [p.argument]
         
     | 
| 
       78 
80 
     | 
    
         
             
                  # followed by comma separated others
         
     | 
| 
       79 
     | 
    
         
            -
                  while p.consume?(:comma)
         
     | 
| 
       80 
     | 
    
         
            -
                    filterargs << p.argument
         
     | 
| 
       81 
     | 
    
         
            -
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                  filterargs << p.argument while p.consume?(:comma)
         
     | 
| 
       82 
82 
     | 
    
         
             
                  filterargs
         
     | 
| 
       83 
83 
     | 
    
         
             
                end
         
     | 
| 
       84 
84 
     | 
    
         | 
| 
       85 
85 
     | 
    
         
             
                def render(context)
         
     | 
| 
       86 
     | 
    
         
            -
                   
     | 
| 
      
 86 
     | 
    
         
            +
                  obj = context.evaluate(@name)
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  @filters.each do |filter_name, filter_args, filter_kwargs|
         
     | 
| 
       87 
89 
     | 
    
         
             
                    filter_args = evaluate_filter_expressions(context, filter_args, filter_kwargs)
         
     | 
| 
       88 
     | 
    
         
            -
                     
     | 
| 
       89 
     | 
    
         
            -
                  end 
     | 
| 
      
 90 
     | 
    
         
            +
                    obj = context.invoke(filter_name, obj, *filter_args)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  context.apply_global_filter(obj)
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                def render_to_output_buffer(context, output)
         
     | 
| 
      
 97 
     | 
    
         
            +
                  obj = render(context)
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  if obj.is_a?(Array)
         
     | 
| 
      
 100 
     | 
    
         
            +
                    output << obj.join
         
     | 
| 
      
 101 
     | 
    
         
            +
                  elsif obj.nil?
         
     | 
| 
      
 102 
     | 
    
         
            +
                  else
         
     | 
| 
      
 103 
     | 
    
         
            +
                    output << obj.to_s
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                  output
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                def disabled?(_context)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  false
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                def disabled_tags
         
     | 
| 
      
 114 
     | 
    
         
            +
                  []
         
     | 
| 
       90 
115 
     | 
    
         
             
                end
         
     | 
| 
       91 
116 
     | 
    
         | 
| 
       92 
117 
     | 
    
         
             
                private
         
     | 
| 
       93 
118 
     | 
    
         | 
| 
       94 
119 
     | 
    
         
             
                def parse_filter_expressions(filter_name, unparsed_args)
         
     | 
| 
       95 
     | 
    
         
            -
                  filter_args 
     | 
| 
       96 
     | 
    
         
            -
                  keyword_args =  
     | 
| 
      
 120 
     | 
    
         
            +
                  filter_args  = []
         
     | 
| 
      
 121 
     | 
    
         
            +
                  keyword_args = nil
         
     | 
| 
       97 
122 
     | 
    
         
             
                  unparsed_args.each do |a|
         
     | 
| 
       98 
     | 
    
         
            -
                    if matches = a.match( 
     | 
| 
       99 
     | 
    
         
            -
                      keyword_args 
     | 
| 
      
 123 
     | 
    
         
            +
                    if (matches = a.match(JustTagAttributes))
         
     | 
| 
      
 124 
     | 
    
         
            +
                      keyword_args           ||= {}
         
     | 
| 
      
 125 
     | 
    
         
            +
                      keyword_args[matches[1]] = parse_context.parse_expression(matches[2])
         
     | 
| 
       100 
126 
     | 
    
         
             
                    else
         
     | 
| 
       101 
     | 
    
         
            -
                      filter_args <<  
     | 
| 
      
 127 
     | 
    
         
            +
                      filter_args << parse_context.parse_expression(a)
         
     | 
| 
       102 
128 
     | 
    
         
             
                    end
         
     | 
| 
       103 
129 
     | 
    
         
             
                  end
         
     | 
| 
       104 
130 
     | 
    
         
             
                  result = [filter_name, filter_args]
         
     | 
| 
       105 
     | 
    
         
            -
                  result << keyword_args  
     | 
| 
      
 131 
     | 
    
         
            +
                  result << keyword_args if keyword_args
         
     | 
| 
       106 
132 
     | 
    
         
             
                  result
         
     | 
| 
       107 
133 
     | 
    
         
             
                end
         
     | 
| 
       108 
134 
     | 
    
         | 
| 
       109 
135 
     | 
    
         
             
                def evaluate_filter_expressions(context, filter_args, filter_kwargs)
         
     | 
| 
       110 
     | 
    
         
            -
                  parsed_args = filter_args.map{ |expr| context.evaluate(expr) }
         
     | 
| 
      
 136 
     | 
    
         
            +
                  parsed_args = filter_args.map { |expr| context.evaluate(expr) }
         
     | 
| 
       111 
137 
     | 
    
         
             
                  if filter_kwargs
         
     | 
| 
       112 
138 
     | 
    
         
             
                    parsed_kwargs = {}
         
     | 
| 
       113 
139 
     | 
    
         
             
                    filter_kwargs.each do |key, expr|
         
     | 
| 
         @@ -118,17 +144,9 @@ module Liquid 
     | 
|
| 
       118 
144 
     | 
    
         
             
                  parsed_args
         
     | 
| 
       119 
145 
     | 
    
         
             
                end
         
     | 
| 
       120 
146 
     | 
    
         | 
| 
       121 
     | 
    
         
            -
                 
     | 
| 
       122 
     | 
    
         
            -
                   
     | 
| 
       123 
     | 
    
         
            -
                    @ 
     | 
| 
       124 
     | 
    
         
            -
                    name = Regexp.last_match(0)
         
     | 
| 
       125 
     | 
    
         
            -
                    case Template.taint_mode
         
     | 
| 
       126 
     | 
    
         
            -
                    when :warn
         
     | 
| 
       127 
     | 
    
         
            -
                      @warnings ||= []
         
     | 
| 
       128 
     | 
    
         
            -
                      @warnings << "variable '#{name}' is tainted and was not escaped"
         
     | 
| 
       129 
     | 
    
         
            -
                    when :error
         
     | 
| 
       130 
     | 
    
         
            -
                      raise TaintedError, "Error - variable '#{name}' is tainted and was not escaped"
         
     | 
| 
       131 
     | 
    
         
            -
                    end
         
     | 
| 
      
 147 
     | 
    
         
            +
                class ParseTreeVisitor < Liquid::ParseTreeVisitor
         
     | 
| 
      
 148 
     | 
    
         
            +
                  def children
         
     | 
| 
      
 149 
     | 
    
         
            +
                    [@node.name] + @node.filters.flatten
         
     | 
| 
       132 
150 
     | 
    
         
             
                  end
         
     | 
| 
       133 
151 
     | 
    
         
             
                end
         
     | 
| 
       134 
152 
     | 
    
         
             
              end
         
     | 
| 
         @@ -1,7 +1,8 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Liquid
         
     | 
| 
       2 
4 
     | 
    
         
             
              class VariableLookup
         
     | 
| 
       3 
     | 
    
         
            -
                 
     | 
| 
       4 
     | 
    
         
            -
                COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze]
         
     | 
| 
      
 5 
     | 
    
         
            +
                COMMAND_METHODS = ['size', 'first', 'last'].freeze
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
       6 
7 
     | 
    
         
             
                attr_reader :name, :lookups
         
     | 
| 
       7 
8 
     | 
    
         | 
| 
         @@ -13,51 +14,60 @@ module Liquid 
     | 
|
| 
       13 
14 
     | 
    
         
             
                  lookups = markup.scan(VariableParser)
         
     | 
| 
       14 
15 
     | 
    
         | 
| 
       15 
16 
     | 
    
         
             
                  name = lookups.shift
         
     | 
| 
       16 
     | 
    
         
            -
                  if name  
     | 
| 
       17 
     | 
    
         
            -
                    name = Expression.parse( 
     | 
| 
      
 17 
     | 
    
         
            +
                  if name&.start_with?('[') && name&.end_with?(']')
         
     | 
| 
      
 18 
     | 
    
         
            +
                    name = Expression.parse(name[1..-2])
         
     | 
| 
       18 
19 
     | 
    
         
             
                  end
         
     | 
| 
       19 
20 
     | 
    
         
             
                  @name = name
         
     | 
| 
       20 
21 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                  @lookups 
     | 
| 
      
 22 
     | 
    
         
            +
                  @lookups       = lookups
         
     | 
| 
       22 
23 
     | 
    
         
             
                  @command_flags = 0
         
     | 
| 
       23 
24 
     | 
    
         | 
| 
       24 
25 
     | 
    
         
             
                  @lookups.each_index do |i|
         
     | 
| 
       25 
26 
     | 
    
         
             
                    lookup = lookups[i]
         
     | 
| 
       26 
     | 
    
         
            -
                    if lookup  
     | 
| 
       27 
     | 
    
         
            -
                      lookups[i] = Expression.parse( 
     | 
| 
      
 27 
     | 
    
         
            +
                    if lookup&.start_with?('[') && lookup&.end_with?(']')
         
     | 
| 
      
 28 
     | 
    
         
            +
                      lookups[i] = Expression.parse(lookup[1..-2])
         
     | 
| 
       28 
29 
     | 
    
         
             
                    elsif COMMAND_METHODS.include?(lookup)
         
     | 
| 
       29 
30 
     | 
    
         
             
                      @command_flags |= 1 << i
         
     | 
| 
       30 
31 
     | 
    
         
             
                    end
         
     | 
| 
       31 
32 
     | 
    
         
             
                  end
         
     | 
| 
       32 
33 
     | 
    
         
             
                end
         
     | 
| 
       33 
34 
     | 
    
         | 
| 
      
 35 
     | 
    
         
            +
                def lookup_command?(lookup_index)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @command_flags & (1 << lookup_index) != 0
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
       34 
39 
     | 
    
         
             
                def evaluate(context)
         
     | 
| 
       35 
     | 
    
         
            -
                  name 
     | 
| 
      
 40 
     | 
    
         
            +
                  name   = context.evaluate(@name)
         
     | 
| 
       36 
41 
     | 
    
         
             
                  object = context.find_variable(name)
         
     | 
| 
       37 
42 
     | 
    
         | 
| 
       38 
43 
     | 
    
         
             
                  @lookups.each_index do |i|
         
     | 
| 
       39 
44 
     | 
    
         
             
                    key = context.evaluate(@lookups[i])
         
     | 
| 
       40 
45 
     | 
    
         | 
| 
      
 46 
     | 
    
         
            +
                    # Cast "key" to its liquid value to enable it to act as a primitive value
         
     | 
| 
      
 47 
     | 
    
         
            +
                    key = Liquid::Utils.to_liquid_value(key)
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
       41 
49 
     | 
    
         
             
                    # If object is a hash- or array-like object we look for the
         
     | 
| 
       42 
50 
     | 
    
         
             
                    # presence of the key and if its available we return it
         
     | 
| 
       43 
51 
     | 
    
         
             
                    if object.respond_to?(:[]) &&
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
      
 52 
     | 
    
         
            +
                        ((object.respond_to?(:key?) && object.key?(key)) ||
         
     | 
| 
      
 53 
     | 
    
         
            +
                         (object.respond_to?(:fetch) && key.is_a?(Integer)))
         
     | 
| 
       46 
54 
     | 
    
         | 
| 
       47 
55 
     | 
    
         
             
                      # if its a proc we will replace the entry with the proc
         
     | 
| 
       48 
     | 
    
         
            -
                      res 
     | 
| 
      
 56 
     | 
    
         
            +
                      res    = context.lookup_and_evaluate(object, key)
         
     | 
| 
       49 
57 
     | 
    
         
             
                      object = res.to_liquid
         
     | 
| 
       50 
58 
     | 
    
         | 
| 
       51 
59 
     | 
    
         
             
                      # Some special cases. If the part wasn't in square brackets and
         
     | 
| 
       52 
60 
     | 
    
         
             
                      # no key with the same name was found we interpret following calls
         
     | 
| 
       53 
61 
     | 
    
         
             
                      # as commands and call them on the current object
         
     | 
| 
       54 
     | 
    
         
            -
                    elsif  
     | 
| 
      
 62 
     | 
    
         
            +
                    elsif lookup_command?(i) && object.respond_to?(key)
         
     | 
| 
       55 
63 
     | 
    
         
             
                      object = object.send(key).to_liquid
         
     | 
| 
       56 
64 
     | 
    
         | 
| 
       57 
65 
     | 
    
         
             
                      # No key was present with the desired value and it wasn't one of the directly supported
         
     | 
| 
       58 
     | 
    
         
            -
                      # keywords either. The only thing we got left is to return nil
         
     | 
| 
      
 66 
     | 
    
         
            +
                      # keywords either. The only thing we got left is to return nil or
         
     | 
| 
      
 67 
     | 
    
         
            +
                      # raise an exception if `strict_variables` option is set to true
         
     | 
| 
       59 
68 
     | 
    
         
             
                    else
         
     | 
| 
       60 
     | 
    
         
            -
                      return nil
         
     | 
| 
      
 69 
     | 
    
         
            +
                      return nil unless context.strict_variables
         
     | 
| 
      
 70 
     | 
    
         
            +
                      raise Liquid::UndefinedVariable, "undefined variable #{key}"
         
     | 
| 
       61 
71 
     | 
    
         
             
                    end
         
     | 
| 
       62 
72 
     | 
    
         | 
| 
       63 
73 
     | 
    
         
             
                    # If we are dealing with a drop here we have to
         
     | 
| 
         @@ -68,7 +78,7 @@ module Liquid 
     | 
|
| 
       68 
78 
     | 
    
         
             
                end
         
     | 
| 
       69 
79 
     | 
    
         | 
| 
       70 
80 
     | 
    
         
             
                def ==(other)
         
     | 
| 
       71 
     | 
    
         
            -
                  self.class == other.class &&  
     | 
| 
      
 81 
     | 
    
         
            +
                  self.class == other.class && state == other.state
         
     | 
| 
       72 
82 
     | 
    
         
             
                end
         
     | 
| 
       73 
83 
     | 
    
         | 
| 
       74 
84 
     | 
    
         
             
                protected
         
     | 
| 
         @@ -76,5 +86,11 @@ module Liquid 
     | 
|
| 
       76 
86 
     | 
    
         
             
                def state
         
     | 
| 
       77 
87 
     | 
    
         
             
                  [@name, @lookups, @command_flags]
         
     | 
| 
       78 
88 
     | 
    
         
             
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                class ParseTreeVisitor < Liquid::ParseTreeVisitor
         
     | 
| 
      
 91 
     | 
    
         
            +
                  def children
         
     | 
| 
      
 92 
     | 
    
         
            +
                    @node.lookups
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
       79 
95 
     | 
    
         
             
              end
         
     | 
| 
       80 
96 
     | 
    
         
             
            end
         
     | 
    
        data/lib/liquid/version.rb
    CHANGED
    
    
    
        data/lib/liquid.rb
    CHANGED
    
    | 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            # Copyright (c) 2005 Tobias Luetke
         
     | 
| 
       2 
4 
     | 
    
         
             
            #
         
     | 
| 
       3 
5 
     | 
    
         
             
            # Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
         @@ -21,11 +23,13 @@ 
     | 
|
| 
       21 
23 
     | 
    
         | 
| 
       22 
24 
     | 
    
         
             
            module Liquid
         
     | 
| 
       23 
25 
     | 
    
         
             
              FilterSeparator             = /\|/
         
     | 
| 
       24 
     | 
    
         
            -
              ArgumentSeparator           = ',' 
     | 
| 
       25 
     | 
    
         
            -
              FilterArgumentSeparator     = ':' 
     | 
| 
       26 
     | 
    
         
            -
              VariableAttributeSeparator  = '.' 
     | 
| 
      
 26 
     | 
    
         
            +
              ArgumentSeparator           = ','
         
     | 
| 
      
 27 
     | 
    
         
            +
              FilterArgumentSeparator     = ':'
         
     | 
| 
      
 28 
     | 
    
         
            +
              VariableAttributeSeparator  = '.'
         
     | 
| 
      
 29 
     | 
    
         
            +
              WhitespaceControl           = '-'
         
     | 
| 
       27 
30 
     | 
    
         
             
              TagStart                    = /\{\%/
         
     | 
| 
       28 
31 
     | 
    
         
             
              TagEnd                      = /\%\}/
         
     | 
| 
      
 32 
     | 
    
         
            +
              TagName                     = /#|\w+/
         
     | 
| 
       29 
33 
     | 
    
         
             
              VariableSignature           = /\(?[\w\-\.\[\]]\)?/
         
     | 
| 
       30 
34 
     | 
    
         
             
              VariableSegment             = /[\w\-]/
         
     | 
| 
       31 
35 
     | 
    
         
             
              VariableStart               = /\{\{/
         
     | 
| 
         @@ -33,45 +37,56 @@ module Liquid 
     | 
|
| 
       33 
37 
     | 
    
         
             
              VariableIncompleteEnd       = /\}\}?/
         
     | 
| 
       34 
38 
     | 
    
         
             
              QuotedString                = /"[^"]*"|'[^']*'/
         
     | 
| 
       35 
39 
     | 
    
         
             
              QuotedFragment              = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o
         
     | 
| 
       36 
     | 
    
         
            -
              TagAttributes               = /(\w 
     | 
| 
       37 
     | 
    
         
            -
              AnyStartingTag              =  
     | 
| 
      
 40 
     | 
    
         
            +
              TagAttributes               = /(\w[\w-]*)\s*\:\s*(#{QuotedFragment})/o
         
     | 
| 
      
 41 
     | 
    
         
            +
              AnyStartingTag              = /#{TagStart}|#{VariableStart}/o
         
     | 
| 
       38 
42 
     | 
    
         
             
              PartialTemplateParser       = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
         
     | 
| 
       39 
43 
     | 
    
         
             
              TemplateParser              = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
         
     | 
| 
       40 
44 
     | 
    
         
             
              VariableParser              = /\[[^\]]+\]|#{VariableSegment}+\??/o
         
     | 
| 
       41 
45 
     | 
    
         | 
| 
      
 46 
     | 
    
         
            +
              RAISE_EXCEPTION_LAMBDA = ->(_e) { raise }
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
       42 
48 
     | 
    
         
             
              singleton_class.send(:attr_accessor, :cache_classes)
         
     | 
| 
       43 
49 
     | 
    
         
             
              self.cache_classes = true
         
     | 
| 
       44 
50 
     | 
    
         
             
            end
         
     | 
| 
       45 
51 
     | 
    
         | 
| 
       46 
52 
     | 
    
         
             
            require "liquid/version"
         
     | 
| 
      
 53 
     | 
    
         
            +
            require 'liquid/parse_tree_visitor'
         
     | 
| 
       47 
54 
     | 
    
         
             
            require 'liquid/lexer'
         
     | 
| 
       48 
55 
     | 
    
         
             
            require 'liquid/parser'
         
     | 
| 
       49 
56 
     | 
    
         
             
            require 'liquid/i18n'
         
     | 
| 
       50 
57 
     | 
    
         
             
            require 'liquid/drop'
         
     | 
| 
      
 58 
     | 
    
         
            +
            require 'liquid/tablerowloop_drop'
         
     | 
| 
      
 59 
     | 
    
         
            +
            require 'liquid/forloop_drop'
         
     | 
| 
       51 
60 
     | 
    
         
             
            require 'liquid/extensions'
         
     | 
| 
       52 
61 
     | 
    
         
             
            require 'liquid/errors'
         
     | 
| 
       53 
62 
     | 
    
         
             
            require 'liquid/interrupts'
         
     | 
| 
       54 
     | 
    
         
            -
            require 'liquid/ 
     | 
| 
      
 63 
     | 
    
         
            +
            require 'liquid/strainer_template'
         
     | 
| 
      
 64 
     | 
    
         
            +
            require 'liquid/strainer_factory'
         
     | 
| 
       55 
65 
     | 
    
         
             
            require 'liquid/expression'
         
     | 
| 
       56 
66 
     | 
    
         
             
            require 'liquid/context'
         
     | 
| 
       57 
67 
     | 
    
         
             
            require 'liquid/parser_switching'
         
     | 
| 
       58 
68 
     | 
    
         
             
            require 'liquid/tag'
         
     | 
| 
      
 69 
     | 
    
         
            +
            require 'liquid/tag/disabler'
         
     | 
| 
      
 70 
     | 
    
         
            +
            require 'liquid/tag/disableable'
         
     | 
| 
       59 
71 
     | 
    
         
             
            require 'liquid/block'
         
     | 
| 
      
 72 
     | 
    
         
            +
            require 'liquid/block_body'
         
     | 
| 
       60 
73 
     | 
    
         
             
            require 'liquid/document'
         
     | 
| 
       61 
74 
     | 
    
         
             
            require 'liquid/variable'
         
     | 
| 
       62 
75 
     | 
    
         
             
            require 'liquid/variable_lookup'
         
     | 
| 
       63 
76 
     | 
    
         
             
            require 'liquid/range_lookup'
         
     | 
| 
       64 
77 
     | 
    
         
             
            require 'liquid/file_system'
         
     | 
| 
      
 78 
     | 
    
         
            +
            require 'liquid/resource_limits'
         
     | 
| 
       65 
79 
     | 
    
         
             
            require 'liquid/template'
         
     | 
| 
       66 
80 
     | 
    
         
             
            require 'liquid/standardfilters'
         
     | 
| 
       67 
81 
     | 
    
         
             
            require 'liquid/condition'
         
     | 
| 
       68 
     | 
    
         
            -
            require 'liquid/module_ex'
         
     | 
| 
       69 
82 
     | 
    
         
             
            require 'liquid/utils'
         
     | 
| 
       70 
     | 
    
         
            -
            require 'liquid/ 
     | 
| 
      
 83 
     | 
    
         
            +
            require 'liquid/tokenizer'
         
     | 
| 
      
 84 
     | 
    
         
            +
            require 'liquid/parse_context'
         
     | 
| 
      
 85 
     | 
    
         
            +
            require 'liquid/partial_cache'
         
     | 
| 
      
 86 
     | 
    
         
            +
            require 'liquid/usage'
         
     | 
| 
      
 87 
     | 
    
         
            +
            require 'liquid/registers'
         
     | 
| 
      
 88 
     | 
    
         
            +
            require 'liquid/template_factory'
         
     | 
| 
       71 
89 
     | 
    
         | 
| 
       72 
90 
     | 
    
         
             
            # Load all the tags of the standard library
         
     | 
| 
       73 
91 
     | 
    
         
             
            #
         
     | 
| 
       74 
     | 
    
         
            -
            Dir[ 
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
            require 'liquid/profiler'
         
     | 
| 
       77 
     | 
    
         
            -
            require 'liquid/profiler/hooks'
         
     | 
| 
      
 92 
     | 
    
         
            +
            Dir["#{__dir__}/liquid/tags/*.rb"].each { |f| require f }
         
     |