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
 
| 
         @@ -0,0 +1,54 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Liquid
         
     | 
| 
      
 4 
     | 
    
         
            +
              class ParseContext
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :locale, :line_number, :trim_whitespace, :depth
         
     | 
| 
      
 6 
     | 
    
         
            +
                attr_reader :partial, :warnings, :error_mode
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(options = {})
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @template_options = options ? options.dup : {}
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  @locale   = @template_options[:locale] ||= I18n.new
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @warnings = []
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  self.depth   = 0
         
     | 
| 
      
 15 
     | 
    
         
            +
                  self.partial = false
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def [](option_key)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @options[option_key]
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def new_block_body
         
     | 
| 
      
 23 
     | 
    
         
            +
                  Liquid::BlockBody.new
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def new_tokenizer(markup, start_line_number: nil, for_liquid_tag: false)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  Tokenizer.new(markup, line_number: start_line_number, for_liquid_tag: for_liquid_tag)
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def parse_expression(markup)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  Expression.parse(markup)
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def partial=(value)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @partial = value
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @options = value ? partial_options : @template_options
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  @error_mode = @options[:error_mode] || Template.error_mode
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def partial_options
         
     | 
| 
      
 42 
     | 
    
         
            +
                  @partial_options ||= begin
         
     | 
| 
      
 43 
     | 
    
         
            +
                    dont_pass = @template_options[:include_options_blacklist]
         
     | 
| 
      
 44 
     | 
    
         
            +
                    if dont_pass == true
         
     | 
| 
      
 45 
     | 
    
         
            +
                      { locale: locale }
         
     | 
| 
      
 46 
     | 
    
         
            +
                    elsif dont_pass.is_a?(Array)
         
     | 
| 
      
 47 
     | 
    
         
            +
                      @template_options.reject { |k, _v| dont_pass.include?(k) }
         
     | 
| 
      
 48 
     | 
    
         
            +
                    else
         
     | 
| 
      
 49 
     | 
    
         
            +
                      @template_options
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Liquid
         
     | 
| 
      
 4 
     | 
    
         
            +
              class ParseTreeVisitor
         
     | 
| 
      
 5 
     | 
    
         
            +
                def self.for(node, callbacks = Hash.new(proc {}))
         
     | 
| 
      
 6 
     | 
    
         
            +
                  if defined?(node.class::ParseTreeVisitor)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    node.class::ParseTreeVisitor
         
     | 
| 
      
 8 
     | 
    
         
            +
                  else
         
     | 
| 
      
 9 
     | 
    
         
            +
                    self
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end.new(node, callbacks)
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def initialize(node, callbacks)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @node      = node
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @callbacks = callbacks
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def add_callback_for(*classes, &block)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  callback = block
         
     | 
| 
      
 20 
     | 
    
         
            +
                  callback = ->(node, _) { yield node } if block.arity.abs == 1
         
     | 
| 
      
 21 
     | 
    
         
            +
                  callback = ->(_, _) { yield } if block.arity.zero?
         
     | 
| 
      
 22 
     | 
    
         
            +
                  classes.each { |klass| @callbacks[klass] = callback }
         
     | 
| 
      
 23 
     | 
    
         
            +
                  self
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def visit(context = nil)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  children.map do |node|
         
     | 
| 
      
 28 
     | 
    
         
            +
                    item, new_context = @callbacks[node.class].call(node, context)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    [
         
     | 
| 
      
 30 
     | 
    
         
            +
                      item,
         
     | 
| 
      
 31 
     | 
    
         
            +
                      ParseTreeVisitor.for(node, @callbacks).visit(new_context || context),
         
     | 
| 
      
 32 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                protected
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def children
         
     | 
| 
      
 39 
     | 
    
         
            +
                  @node.respond_to?(:nodelist) ? Array(@node.nodelist) : []
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/liquid/parser.rb
    CHANGED
    
    | 
         @@ -1,9 +1,11 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Liquid
         
     | 
| 
       2 
4 
     | 
    
         
             
              class Parser
         
     | 
| 
       3 
5 
     | 
    
         
             
                def initialize(input)
         
     | 
| 
       4 
     | 
    
         
            -
                  l 
     | 
| 
      
 6 
     | 
    
         
            +
                  l       = Lexer.new(input)
         
     | 
| 
       5 
7 
     | 
    
         
             
                  @tokens = l.tokenize
         
     | 
| 
       6 
     | 
    
         
            -
                  @p 
     | 
| 
      
 8 
     | 
    
         
            +
                  @p      = 0 # pointer to current location
         
     | 
| 
       7 
9 
     | 
    
         
             
                end
         
     | 
| 
       8 
10 
     | 
    
         | 
| 
       9 
11 
     | 
    
         
             
                def jump(point)
         
     | 
| 
         @@ -46,11 +48,18 @@ module Liquid 
     | 
|
| 
       46 
48 
     | 
    
         | 
| 
       47 
49 
     | 
    
         
             
                def expression
         
     | 
| 
       48 
50 
     | 
    
         
             
                  token = @tokens[@p]
         
     | 
| 
       49 
     | 
    
         
            -
                   
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
      
 51 
     | 
    
         
            +
                  case token[0]
         
     | 
| 
      
 52 
     | 
    
         
            +
                  when :id
         
     | 
| 
      
 53 
     | 
    
         
            +
                    str = consume
         
     | 
| 
      
 54 
     | 
    
         
            +
                    str << variable_lookups
         
     | 
| 
      
 55 
     | 
    
         
            +
                  when :open_square
         
     | 
| 
      
 56 
     | 
    
         
            +
                    str = consume
         
     | 
| 
      
 57 
     | 
    
         
            +
                    str << expression
         
     | 
| 
      
 58 
     | 
    
         
            +
                    str << consume(:close_square)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    str << variable_lookups
         
     | 
| 
      
 60 
     | 
    
         
            +
                  when :string, :number
         
     | 
| 
       52 
61 
     | 
    
         
             
                    consume
         
     | 
| 
       53 
     | 
    
         
            -
                   
     | 
| 
      
 62 
     | 
    
         
            +
                  when :open_round
         
     | 
| 
       54 
63 
     | 
    
         
             
                    consume
         
     | 
| 
       55 
64 
     | 
    
         
             
                    first = expression
         
     | 
| 
       56 
65 
     | 
    
         
             
                    consume(:dotdot)
         
     | 
| 
         @@ -63,26 +72,29 @@ module Liquid 
     | 
|
| 
       63 
72 
     | 
    
         
             
                end
         
     | 
| 
       64 
73 
     | 
    
         | 
| 
       65 
74 
     | 
    
         
             
                def argument
         
     | 
| 
       66 
     | 
    
         
            -
                  str = ""
         
     | 
| 
      
 75 
     | 
    
         
            +
                  str = +""
         
     | 
| 
       67 
76 
     | 
    
         
             
                  # might be a keyword argument (identifier: expression)
         
     | 
| 
       68 
77 
     | 
    
         
             
                  if look(:id) && look(:colon, 1)
         
     | 
| 
       69 
     | 
    
         
            -
                    str << consume << consume << ' ' 
     | 
| 
      
 78 
     | 
    
         
            +
                    str << consume << consume << ' '
         
     | 
| 
       70 
79 
     | 
    
         
             
                  end
         
     | 
| 
       71 
80 
     | 
    
         | 
| 
       72 
81 
     | 
    
         
             
                  str << expression
         
     | 
| 
       73 
82 
     | 
    
         
             
                  str
         
     | 
| 
       74 
83 
     | 
    
         
             
                end
         
     | 
| 
       75 
84 
     | 
    
         | 
| 
       76 
     | 
    
         
            -
                def  
     | 
| 
       77 
     | 
    
         
            -
                  str =  
     | 
| 
       78 
     | 
    
         
            -
                   
     | 
| 
       79 
     | 
    
         
            -
                     
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
      
 85 
     | 
    
         
            +
                def variable_lookups
         
     | 
| 
      
 86 
     | 
    
         
            +
                  str = +""
         
     | 
| 
      
 87 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 88 
     | 
    
         
            +
                    if look(:open_square)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      str << consume
         
     | 
| 
      
 90 
     | 
    
         
            +
                      str << expression
         
     | 
| 
      
 91 
     | 
    
         
            +
                      str << consume(:close_square)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    elsif look(:dot)
         
     | 
| 
      
 93 
     | 
    
         
            +
                      str << consume
         
     | 
| 
      
 94 
     | 
    
         
            +
                      str << consume(:id)
         
     | 
| 
      
 95 
     | 
    
         
            +
                    else
         
     | 
| 
      
 96 
     | 
    
         
            +
                      break
         
     | 
| 
      
 97 
     | 
    
         
            +
                    end
         
     | 
| 
       86 
98 
     | 
    
         
             
                  end
         
     | 
| 
       87 
99 
     | 
    
         
             
                  str
         
     | 
| 
       88 
100 
     | 
    
         
             
                end
         
     | 
| 
         @@ -1,25 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Liquid
         
     | 
| 
       2 
4 
     | 
    
         
             
              module ParserSwitching
         
     | 
| 
      
 5 
     | 
    
         
            +
                def strict_parse_with_error_mode_fallback(markup)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  strict_parse_with_error_context(markup)
         
     | 
| 
      
 7 
     | 
    
         
            +
                rescue SyntaxError => e
         
     | 
| 
      
 8 
     | 
    
         
            +
                  case parse_context.error_mode
         
     | 
| 
      
 9 
     | 
    
         
            +
                  when :strict
         
     | 
| 
      
 10 
     | 
    
         
            +
                    raise
         
     | 
| 
      
 11 
     | 
    
         
            +
                  when :warn
         
     | 
| 
      
 12 
     | 
    
         
            +
                    parse_context.warnings << e
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                  lax_parse(markup)
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
       3 
17 
     | 
    
         
             
                def parse_with_selected_parser(markup)
         
     | 
| 
       4 
     | 
    
         
            -
                  case  
     | 
| 
      
 18 
     | 
    
         
            +
                  case parse_context.error_mode
         
     | 
| 
       5 
19 
     | 
    
         
             
                  when :strict then strict_parse_with_error_context(markup)
         
     | 
| 
       6 
20 
     | 
    
         
             
                  when :lax    then lax_parse(markup)
         
     | 
| 
       7 
21 
     | 
    
         
             
                  when :warn
         
     | 
| 
       8 
22 
     | 
    
         
             
                    begin
         
     | 
| 
       9 
     | 
    
         
            -
                       
     | 
| 
      
 23 
     | 
    
         
            +
                      strict_parse_with_error_context(markup)
         
     | 
| 
       10 
24 
     | 
    
         
             
                    rescue SyntaxError => e
         
     | 
| 
       11 
     | 
    
         
            -
                      e 
     | 
| 
       12 
     | 
    
         
            -
                       
     | 
| 
       13 
     | 
    
         
            -
                      @warnings << e
         
     | 
| 
       14 
     | 
    
         
            -
                      return lax_parse(markup)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      parse_context.warnings << e
         
     | 
| 
      
 26 
     | 
    
         
            +
                      lax_parse(markup)
         
     | 
| 
       15 
27 
     | 
    
         
             
                    end
         
     | 
| 
       16 
28 
     | 
    
         
             
                  end
         
     | 
| 
       17 
29 
     | 
    
         
             
                end
         
     | 
| 
       18 
30 
     | 
    
         | 
| 
       19 
31 
     | 
    
         
             
                private
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
       20 
33 
     | 
    
         
             
                def strict_parse_with_error_context(markup)
         
     | 
| 
       21 
34 
     | 
    
         
             
                  strict_parse(markup)
         
     | 
| 
       22 
35 
     | 
    
         
             
                rescue SyntaxError => e
         
     | 
| 
      
 36 
     | 
    
         
            +
                  e.line_number    = line_number
         
     | 
| 
       23 
37 
     | 
    
         
             
                  e.markup_context = markup_context(markup)
         
     | 
| 
       24 
38 
     | 
    
         
             
                  raise e
         
     | 
| 
       25 
39 
     | 
    
         
             
                end
         
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Liquid
         
     | 
| 
      
 4 
     | 
    
         
            +
              class PartialCache
         
     | 
| 
      
 5 
     | 
    
         
            +
                def self.load(template_name, context:, parse_context:)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  cached_partials = context.registers[:cached_partials]
         
     | 
| 
      
 7 
     | 
    
         
            +
                  cached = cached_partials[template_name]
         
     | 
| 
      
 8 
     | 
    
         
            +
                  return cached if cached
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  file_system = context.registers[:file_system]
         
     | 
| 
      
 11 
     | 
    
         
            +
                  source      = file_system.read_template_file(template_name)
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  parse_context.partial = true
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  template_factory = context.registers[:template_factory]
         
     | 
| 
      
 16 
     | 
    
         
            +
                  template = template_factory.for(template_name)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  partial = template.parse(source, parse_context)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  cached_partials[template_name] = partial
         
     | 
| 
      
 20 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 21 
     | 
    
         
            +
                  parse_context.partial = false
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,23 +1,35 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Liquid
         
     | 
| 
       2 
     | 
    
         
            -
               
     | 
| 
       3 
     | 
    
         
            -
                def  
     | 
| 
       4 
     | 
    
         
            -
                   
     | 
| 
       5 
     | 
    
         
            -
                     
     | 
| 
      
 4 
     | 
    
         
            +
              module BlockBodyProfilingHook
         
     | 
| 
      
 5 
     | 
    
         
            +
                def render_node(context, output, node)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  if (profiler = context.profiler)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    profiler.profile_node(context.template_name, code: node.raw, line_number: node.line_number) do
         
     | 
| 
      
 8 
     | 
    
         
            +
                      super
         
     | 
| 
      
 9 
     | 
    
         
            +
                    end
         
     | 
| 
      
 10 
     | 
    
         
            +
                  else
         
     | 
| 
      
 11 
     | 
    
         
            +
                    super
         
     | 
| 
       6 
12 
     | 
    
         
             
                  end
         
     | 
| 
       7 
13 
     | 
    
         
             
                end
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
                alias_method :render_token_without_profiling, :render_token
         
     | 
| 
       10 
     | 
    
         
            -
                alias_method :render_token, :render_token_with_profiling
         
     | 
| 
       11 
14 
     | 
    
         
             
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
              BlockBody.prepend(BlockBodyProfilingHook)
         
     | 
| 
       12 
16 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
               
     | 
| 
       14 
     | 
    
         
            -
                def  
     | 
| 
       15 
     | 
    
         
            -
                   
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
              module DocumentProfilingHook
         
     | 
| 
      
 18 
     | 
    
         
            +
                def render_to_output_buffer(context, output)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  return super unless context.profiler
         
     | 
| 
      
 20 
     | 
    
         
            +
                  context.profiler.profile(context.template_name) { super }
         
     | 
| 
       18 
21 
     | 
    
         
             
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
              Document.prepend(DocumentProfilingHook)
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              module ContextProfilingHook
         
     | 
| 
      
 26 
     | 
    
         
            +
                attr_accessor :profiler
         
     | 
| 
       19 
27 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                 
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 28 
     | 
    
         
            +
                def new_isolated_subcontext
         
     | 
| 
      
 29 
     | 
    
         
            +
                  new_context = super
         
     | 
| 
      
 30 
     | 
    
         
            +
                  new_context.profiler = profiler
         
     | 
| 
      
 31 
     | 
    
         
            +
                  new_context
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
       22 
33 
     | 
    
         
             
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
              Context.prepend(ContextProfilingHook)
         
     | 
| 
       23 
35 
     | 
    
         
             
            end
         
     | 
    
        data/lib/liquid/profiler.rb
    CHANGED
    
    | 
         @@ -1,9 +1,13 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'liquid/profiler/hooks'
         
     | 
| 
       2 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
            module Liquid
         
     | 
| 
       3 
6 
     | 
    
         
             
              # Profiler enables support for profiling template rendering to help track down performance issues.
         
     | 
| 
       4 
7 
     | 
    
         
             
              #
         
     | 
| 
       5 
     | 
    
         
            -
              # To enable profiling,  
     | 
| 
       6 
     | 
    
         
            -
              #  
     | 
| 
      
 8 
     | 
    
         
            +
              # To enable profiling, first require 'liquid/profiler'.
         
     | 
| 
      
 9 
     | 
    
         
            +
              # Then, to profile a parse/render cycle, pass the <tt>profile: true</tt> option to <tt>Liquid::Template.parse</tt>.
         
     | 
| 
      
 10 
     | 
    
         
            +
              # After <tt>Liquid::Template#render</tt> is called, the template object makes available an instance of this
         
     | 
| 
       7 
11 
     | 
    
         
             
              # class via the <tt>Liquid::Template#profiler</tt> method.
         
     | 
| 
       8 
12 
     | 
    
         
             
              #
         
     | 
| 
       9 
13 
     | 
    
         
             
              #   template = Liquid::Template.parse(template_content, profile: true)
         
     | 
| 
         @@ -17,11 +21,11 @@ module Liquid 
     | 
|
| 
       17 
21 
     | 
    
         
             
              # inside of <tt>{% include %}</tt> tags.
         
     | 
| 
       18 
22 
     | 
    
         
             
              #
         
     | 
| 
       19 
23 
     | 
    
         
             
              #   profile.each do |node|
         
     | 
| 
       20 
     | 
    
         
            -
              #     # Access to the  
     | 
| 
      
 24 
     | 
    
         
            +
              #     # Access to the node itself
         
     | 
| 
       21 
25 
     | 
    
         
             
              #     node.code
         
     | 
| 
       22 
26 
     | 
    
         
             
              #
         
     | 
| 
       23 
27 
     | 
    
         
             
              #     # Which template and line number of this node.
         
     | 
| 
       24 
     | 
    
         
            -
              #     #  
     | 
| 
      
 28 
     | 
    
         
            +
              #     # The top-level template name is `nil` by default, but can be set in the Liquid::Context before rendering.
         
     | 
| 
       25 
29 
     | 
    
         
             
              #     node.partial
         
     | 
| 
       26 
30 
     | 
    
         
             
              #     node.line_number
         
     | 
| 
       27 
31 
     | 
    
         
             
              #
         
     | 
| 
         @@ -42,118 +46,94 @@ module Liquid 
     | 
|
| 
       42 
46 
     | 
    
         
             
                include Enumerable
         
     | 
| 
       43 
47 
     | 
    
         | 
| 
       44 
48 
     | 
    
         
             
                class Timing
         
     | 
| 
       45 
     | 
    
         
            -
                  attr_reader :code, : 
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
                   
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                    @ 
     | 
| 
      
 49 
     | 
    
         
            +
                  attr_reader :code, :template_name, :line_number, :children
         
     | 
| 
      
 50 
     | 
    
         
            +
                  attr_accessor :total_time
         
     | 
| 
      
 51 
     | 
    
         
            +
                  alias_method :render_time, :total_time
         
     | 
| 
      
 52 
     | 
    
         
            +
                  alias_method :partial, :template_name
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  def initialize(code: nil, template_name: nil, line_number: nil)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @code = code
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @template_name = template_name
         
     | 
| 
      
 57 
     | 
    
         
            +
                    @line_number = line_number
         
     | 
| 
      
 58 
     | 
    
         
            +
                    @children = []
         
     | 
| 
       52 
59 
     | 
    
         
             
                  end
         
     | 
| 
       53 
60 
     | 
    
         | 
| 
       54 
     | 
    
         
            -
                  def  
     | 
| 
       55 
     | 
    
         
            -
                     
     | 
| 
       56 
     | 
    
         
            -
                       
     | 
| 
      
 61 
     | 
    
         
            +
                  def self_time
         
     | 
| 
      
 62 
     | 
    
         
            +
                    @self_time ||= begin
         
     | 
| 
      
 63 
     | 
    
         
            +
                      total_children_time = 0.0
         
     | 
| 
      
 64 
     | 
    
         
            +
                      @children.each do |child|
         
     | 
| 
      
 65 
     | 
    
         
            +
                        total_children_time += child.total_time
         
     | 
| 
      
 66 
     | 
    
         
            +
                      end
         
     | 
| 
      
 67 
     | 
    
         
            +
                      @total_time - total_children_time
         
     | 
| 
       57 
68 
     | 
    
         
             
                    end
         
     | 
| 
       58 
69 
     | 
    
         
             
                  end
         
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                  def start
         
     | 
| 
       61 
     | 
    
         
            -
                    @start_time = Time.now
         
     | 
| 
       62 
     | 
    
         
            -
                  end
         
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
                  def finish
         
     | 
| 
       65 
     | 
    
         
            -
                    @end_time = Time.now
         
     | 
| 
       66 
     | 
    
         
            -
                  end
         
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
                  def render_time
         
     | 
| 
       69 
     | 
    
         
            -
                    @end_time - @start_time
         
     | 
| 
       70 
     | 
    
         
            -
                  end
         
     | 
| 
       71 
70 
     | 
    
         
             
                end
         
     | 
| 
       72 
71 
     | 
    
         | 
| 
       73 
     | 
    
         
            -
                 
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                   
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
      
 72 
     | 
    
         
            +
                attr_reader :total_time
         
     | 
| 
      
 73 
     | 
    
         
            +
                alias_method :total_render_time, :total_time
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 76 
     | 
    
         
            +
                  @root_children = []
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @current_children = nil
         
     | 
| 
      
 78 
     | 
    
         
            +
                  @total_time = 0.0
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                def profile(template_name, &block)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  # nested renders are done from a tag that already has a timing node
         
     | 
| 
      
 83 
     | 
    
         
            +
                  return yield if @current_children
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  root_children = @root_children
         
     | 
| 
      
 86 
     | 
    
         
            +
                  render_idx = root_children.length
         
     | 
| 
      
 87 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 88 
     | 
    
         
            +
                    @current_children = root_children
         
     | 
| 
      
 89 
     | 
    
         
            +
                    profile_node(template_name, &block)
         
     | 
| 
      
 90 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 91 
     | 
    
         
            +
                    @current_children = nil
         
     | 
| 
      
 92 
     | 
    
         
            +
                    if (timing = root_children[render_idx])
         
     | 
| 
      
 93 
     | 
    
         
            +
                      @total_time += timing.total_time
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
       81 
95 
     | 
    
         
             
                  end
         
     | 
| 
       82 
96 
     | 
    
         
             
                end
         
     | 
| 
       83 
97 
     | 
    
         | 
| 
       84 
     | 
    
         
            -
                def  
     | 
| 
       85 
     | 
    
         
            -
                   
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
                     
     | 
| 
       88 
     | 
    
         
            -
                    Profiler.current_profile.pop_partial
         
     | 
| 
       89 
     | 
    
         
            -
                    output
         
     | 
| 
      
 98 
     | 
    
         
            +
                def children
         
     | 
| 
      
 99 
     | 
    
         
            +
                  children = @root_children
         
     | 
| 
      
 100 
     | 
    
         
            +
                  if children.length == 1
         
     | 
| 
      
 101 
     | 
    
         
            +
                    children.first.children
         
     | 
| 
       90 
102 
     | 
    
         
             
                  else
         
     | 
| 
       91 
     | 
    
         
            -
                     
     | 
| 
      
 103 
     | 
    
         
            +
                    children
         
     | 
| 
       92 
104 
     | 
    
         
             
                  end
         
     | 
| 
       93 
105 
     | 
    
         
             
                end
         
     | 
| 
       94 
106 
     | 
    
         | 
| 
       95 
     | 
    
         
            -
                def self.current_profile
         
     | 
| 
       96 
     | 
    
         
            -
                  Thread.current[:liquid_profiler]
         
     | 
| 
       97 
     | 
    
         
            -
                end
         
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
                def initialize
         
     | 
| 
       100 
     | 
    
         
            -
                  @partial_stack = ["<root>"]
         
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
                  @root_timing = Timing.new("", current_partial)
         
     | 
| 
       103 
     | 
    
         
            -
                  @timing_stack = [@root_timing]
         
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
     | 
    
         
            -
                  @render_start_at = Time.now
         
     | 
| 
       106 
     | 
    
         
            -
                  @render_end_at = @render_start_at
         
     | 
| 
       107 
     | 
    
         
            -
                end
         
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
                def start
         
     | 
| 
       110 
     | 
    
         
            -
                  Thread.current[:liquid_profiler] = self
         
     | 
| 
       111 
     | 
    
         
            -
                  @render_start_at = Time.now
         
     | 
| 
       112 
     | 
    
         
            -
                end
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
                def stop
         
     | 
| 
       115 
     | 
    
         
            -
                  Thread.current[:liquid_profiler] = nil
         
     | 
| 
       116 
     | 
    
         
            -
                  @render_end_at = Time.now
         
     | 
| 
       117 
     | 
    
         
            -
                end
         
     | 
| 
       118 
     | 
    
         
            -
             
     | 
| 
       119 
     | 
    
         
            -
                def total_render_time
         
     | 
| 
       120 
     | 
    
         
            -
                  @render_end_at - @render_start_at
         
     | 
| 
       121 
     | 
    
         
            -
                end
         
     | 
| 
       122 
     | 
    
         
            -
             
     | 
| 
       123 
107 
     | 
    
         
             
                def each(&block)
         
     | 
| 
       124 
     | 
    
         
            -
                   
     | 
| 
      
 108 
     | 
    
         
            +
                  children.each(&block)
         
     | 
| 
       125 
109 
     | 
    
         
             
                end
         
     | 
| 
       126 
110 
     | 
    
         | 
| 
       127 
111 
     | 
    
         
             
                def [](idx)
         
     | 
| 
       128 
     | 
    
         
            -
                   
     | 
| 
      
 112 
     | 
    
         
            +
                  children[idx]
         
     | 
| 
       129 
113 
     | 
    
         
             
                end
         
     | 
| 
       130 
114 
     | 
    
         | 
| 
       131 
115 
     | 
    
         
             
                def length
         
     | 
| 
       132 
     | 
    
         
            -
                   
     | 
| 
      
 116 
     | 
    
         
            +
                  children.length
         
     | 
| 
       133 
117 
     | 
    
         
             
                end
         
     | 
| 
       134 
118 
     | 
    
         | 
| 
       135 
     | 
    
         
            -
                def  
     | 
| 
       136 
     | 
    
         
            -
                   
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
             
     | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
                  @partial_stack.last
         
     | 
| 
      
 119 
     | 
    
         
            +
                def profile_node(template_name, code: nil, line_number: nil)
         
     | 
| 
      
 120 
     | 
    
         
            +
                  timing = Timing.new(code: code, template_name: template_name, line_number: line_number)
         
     | 
| 
      
 121 
     | 
    
         
            +
                  parent_children = @current_children
         
     | 
| 
      
 122 
     | 
    
         
            +
                  start_time = monotonic_time
         
     | 
| 
      
 123 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 124 
     | 
    
         
            +
                    @current_children = timing.children
         
     | 
| 
      
 125 
     | 
    
         
            +
                    yield
         
     | 
| 
      
 126 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 127 
     | 
    
         
            +
                    @current_children = parent_children
         
     | 
| 
      
 128 
     | 
    
         
            +
                    timing.total_time = monotonic_time - start_time
         
     | 
| 
      
 129 
     | 
    
         
            +
                    parent_children << timing
         
     | 
| 
      
 130 
     | 
    
         
            +
                  end
         
     | 
| 
       148 
131 
     | 
    
         
             
                end
         
     | 
| 
       149 
132 
     | 
    
         | 
| 
       150 
     | 
    
         
            -
                 
     | 
| 
       151 
     | 
    
         
            -
                  @partial_stack.push(partial_name)
         
     | 
| 
       152 
     | 
    
         
            -
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
                private
         
     | 
| 
       153 
134 
     | 
    
         | 
| 
       154 
     | 
    
         
            -
                def  
     | 
| 
       155 
     | 
    
         
            -
                   
     | 
| 
      
 135 
     | 
    
         
            +
                def monotonic_time
         
     | 
| 
      
 136 
     | 
    
         
            +
                  Process.clock_gettime(Process::CLOCK_MONOTONIC)
         
     | 
| 
       156 
137 
     | 
    
         
             
                end
         
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
       158 
138 
     | 
    
         
             
              end
         
     | 
| 
       159 
139 
     | 
    
         
             
            end
         
     | 
    
        data/lib/liquid/range_lookup.rb
    CHANGED
    
    | 
         @@ -1,8 +1,10 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Liquid
         
     | 
| 
       2 
4 
     | 
    
         
             
              class RangeLookup
         
     | 
| 
       3 
5 
     | 
    
         
             
                def self.parse(start_markup, end_markup)
         
     | 
| 
       4 
6 
     | 
    
         
             
                  start_obj = Expression.parse(start_markup)
         
     | 
| 
       5 
     | 
    
         
            -
                  end_obj 
     | 
| 
      
 7 
     | 
    
         
            +
                  end_obj   = Expression.parse(end_markup)
         
     | 
| 
       6 
8 
     | 
    
         
             
                  if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
         
     | 
| 
       7 
9 
     | 
    
         
             
                    new(start_obj, end_obj)
         
     | 
| 
       8 
10 
     | 
    
         
             
                  else
         
     | 
| 
         @@ -10,13 +12,36 @@ module Liquid 
     | 
|
| 
       10 
12 
     | 
    
         
             
                  end
         
     | 
| 
       11 
13 
     | 
    
         
             
                end
         
     | 
| 
       12 
14 
     | 
    
         | 
| 
      
 15 
     | 
    
         
            +
                attr_reader :start_obj, :end_obj
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
       13 
17 
     | 
    
         
             
                def initialize(start_obj, end_obj)
         
     | 
| 
       14 
18 
     | 
    
         
             
                  @start_obj = start_obj
         
     | 
| 
       15 
     | 
    
         
            -
                  @end_obj 
     | 
| 
      
 19 
     | 
    
         
            +
                  @end_obj   = end_obj
         
     | 
| 
       16 
20 
     | 
    
         
             
                end
         
     | 
| 
       17 
21 
     | 
    
         | 
| 
       18 
22 
     | 
    
         
             
                def evaluate(context)
         
     | 
| 
       19 
     | 
    
         
            -
                  context.evaluate(@start_obj) 
     | 
| 
      
 23 
     | 
    
         
            +
                  start_int = to_integer(context.evaluate(@start_obj))
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end_int   = to_integer(context.evaluate(@end_obj))
         
     | 
| 
      
 25 
     | 
    
         
            +
                  start_int..end_int
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                private
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def to_integer(input)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  case input
         
     | 
| 
      
 32 
     | 
    
         
            +
                  when Integer
         
     | 
| 
      
 33 
     | 
    
         
            +
                    input
         
     | 
| 
      
 34 
     | 
    
         
            +
                  when NilClass, String
         
     | 
| 
      
 35 
     | 
    
         
            +
                    input.to_i
         
     | 
| 
      
 36 
     | 
    
         
            +
                  else
         
     | 
| 
      
 37 
     | 
    
         
            +
                    Utils.to_integer(input)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                class ParseTreeVisitor < Liquid::ParseTreeVisitor
         
     | 
| 
      
 42 
     | 
    
         
            +
                  def children
         
     | 
| 
      
 43 
     | 
    
         
            +
                    [@node.start_obj, @node.end_obj]
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
       20 
45 
     | 
    
         
             
                end
         
     | 
| 
       21 
46 
     | 
    
         
             
              end
         
     | 
| 
       22 
47 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,51 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Liquid
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Registers
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :static
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(registers = {})
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @static = registers.is_a?(Registers) ? registers.static : registers
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @changes = {}
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def []=(key, value)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @changes[key] = value
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def [](key)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  if @changes.key?(key)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @changes[key]
         
     | 
| 
      
 19 
     | 
    
         
            +
                  else
         
     | 
| 
      
 20 
     | 
    
         
            +
                    @static[key]
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def delete(key)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @changes.delete(key)
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                UNDEFINED = Object.new
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def fetch(key, default = UNDEFINED, &block)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  if @changes.key?(key)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    @changes.fetch(key)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  elsif default != UNDEFINED
         
     | 
| 
      
 34 
     | 
    
         
            +
                    if block_given?
         
     | 
| 
      
 35 
     | 
    
         
            +
                      @static.fetch(key, &block)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    else
         
     | 
| 
      
 37 
     | 
    
         
            +
                      @static.fetch(key, default)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
                  else
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @static.fetch(key, &block)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def key?(key)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @changes.key?(key) || @static.key?(key)
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
              # Alias for backwards compatibility
         
     | 
| 
      
 50 
     | 
    
         
            +
              StaticRegisters = Registers
         
     | 
| 
      
 51 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Liquid
         
     | 
| 
      
 4 
     | 
    
         
            +
              class ResourceLimits
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :render_length_limit, :render_score_limit, :assign_score_limit
         
     | 
| 
      
 6 
     | 
    
         
            +
                attr_reader :render_score, :assign_score
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(limits)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @render_length_limit = limits[:render_length_limit]
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @render_score_limit  = limits[:render_score_limit]
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @assign_score_limit  = limits[:assign_score_limit]
         
     | 
| 
      
 12 
     | 
    
         
            +
                  reset
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def increment_render_score(amount)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @render_score += amount
         
     | 
| 
      
 17 
     | 
    
         
            +
                  raise_limits_reached if @render_score_limit && @render_score > @render_score_limit
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def increment_assign_score(amount)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @assign_score += amount
         
     | 
| 
      
 22 
     | 
    
         
            +
                  raise_limits_reached if @assign_score_limit && @assign_score > @assign_score_limit
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                # update either render_length or assign_score based on whether or not the writes are captured
         
     | 
| 
      
 26 
     | 
    
         
            +
                def increment_write_score(output)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  if (last_captured = @last_capture_length)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    captured = output.bytesize
         
     | 
| 
      
 29 
     | 
    
         
            +
                    increment = captured - last_captured
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @last_capture_length = captured
         
     | 
| 
      
 31 
     | 
    
         
            +
                    increment_assign_score(increment)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  elsif @render_length_limit && output.bytesize > @render_length_limit
         
     | 
| 
      
 33 
     | 
    
         
            +
                    raise_limits_reached
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def raise_limits_reached
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @reached_limit = true
         
     | 
| 
      
 39 
     | 
    
         
            +
                  raise MemoryError, "Memory limits exceeded"
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def reached?
         
     | 
| 
      
 43 
     | 
    
         
            +
                  @reached_limit
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def reset
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @reached_limit = false
         
     | 
| 
      
 48 
     | 
    
         
            +
                  @last_capture_length = nil
         
     | 
| 
      
 49 
     | 
    
         
            +
                  @render_score = @assign_score = 0
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def with_capture
         
     | 
| 
      
 53 
     | 
    
         
            +
                  old_capture_length = @last_capture_length
         
     | 
| 
      
 54 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @last_capture_length = 0
         
     | 
| 
      
 56 
     | 
    
         
            +
                    yield
         
     | 
| 
      
 57 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 58 
     | 
    
         
            +
                    @last_capture_length = old_capture_length
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
            end
         
     |