liquid 4.0.3 → 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 +4 -4
- data/History.md +89 -0
- data/README.md +10 -4
- data/lib/liquid/block.rb +31 -14
- data/lib/liquid/block_body.rb +169 -57
- data/lib/liquid/condition.rb +48 -21
- data/lib/liquid/context.rb +111 -52
- data/lib/liquid/document.rb +47 -9
- data/lib/liquid/drop.rb +4 -2
- data/lib/liquid/errors.rb +20 -18
- data/lib/liquid/expression.rb +28 -32
- data/lib/liquid/extensions.rb +2 -0
- data/lib/liquid/file_system.rb +6 -4
- data/lib/liquid/forloop_drop.rb +54 -4
- data/lib/liquid/i18n.rb +5 -3
- data/lib/liquid/interrupts.rb +3 -1
- data/lib/liquid/lexer.rb +30 -23
- data/lib/liquid/locales/en.yml +8 -5
- data/lib/liquid/parse_context.rb +20 -4
- data/lib/liquid/parse_tree_visitor.rb +2 -2
- data/lib/liquid/parser.rb +30 -18
- data/lib/liquid/parser_switching.rb +17 -3
- data/lib/liquid/partial_cache.rb +24 -0
- data/lib/liquid/profiler/hooks.rb +26 -14
- data/lib/liquid/profiler.rb +67 -86
- data/lib/liquid/range_lookup.rb +13 -3
- data/lib/liquid/registers.rb +51 -0
- data/lib/liquid/resource_limits.rb +47 -8
- data/lib/liquid/standardfilters.rb +551 -114
- data/lib/liquid/strainer_factory.rb +41 -0
- data/lib/liquid/strainer_template.rb +62 -0
- data/lib/liquid/tablerowloop_drop.rb +64 -5
- data/lib/liquid/tag/disableable.rb +22 -0
- data/lib/liquid/tag/disabler.rb +21 -0
- data/lib/liquid/tag.rb +28 -6
- data/lib/liquid/tags/assign.rb +36 -18
- data/lib/liquid/tags/break.rb +16 -3
- data/lib/liquid/tags/capture.rb +24 -18
- data/lib/liquid/tags/case.rb +61 -27
- data/lib/liquid/tags/comment.rb +18 -3
- data/lib/liquid/tags/continue.rb +16 -12
- data/lib/liquid/tags/cycle.rb +37 -25
- data/lib/liquid/tags/decrement.rb +22 -20
- data/lib/liquid/tags/echo.rb +41 -0
- data/lib/liquid/tags/for.rb +90 -87
- data/lib/liquid/tags/if.rb +50 -32
- data/lib/liquid/tags/ifchanged.rb +11 -10
- data/lib/liquid/tags/include.rb +49 -60
- data/lib/liquid/tags/increment.rb +23 -17
- data/lib/liquid/tags/inline_comment.rb +43 -0
- data/lib/liquid/tags/raw.rb +25 -11
- data/lib/liquid/tags/render.rb +109 -0
- data/lib/liquid/tags/table_row.rb +45 -19
- data/lib/liquid/tags/unless.rb +38 -19
- data/lib/liquid/template.rb +52 -72
- data/lib/liquid/template_factory.rb +9 -0
- data/lib/liquid/tokenizer.rb +18 -10
- data/lib/liquid/usage.rb +8 -0
- data/lib/liquid/utils.rb +13 -3
- data/lib/liquid/variable.rb +49 -44
- data/lib/liquid/variable_lookup.rb +18 -10
- data/lib/liquid/version.rb +2 -1
- data/lib/liquid.rb +18 -6
- metadata +20 -108
- data/lib/liquid/strainer.rb +0 -66
- data/lib/liquid/truffle.rb +0 -5
- 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/block_test.rb +0 -12
- data/test/integration/capture_test.rb +0 -50
- data/test/integration/context_test.rb +0 -32
- data/test/integration/document_test.rb +0 -19
- data/test/integration/drop_test.rb +0 -273
- data/test/integration/error_handling_test.rb +0 -260
- data/test/integration/filter_test.rb +0 -178
- data/test/integration/hash_ordering_test.rb +0 -23
- data/test/integration/output_test.rb +0 -123
- data/test/integration/parse_tree_visitor_test.rb +0 -247
- data/test/integration/parsing_quirks_test.rb +0 -122
- data/test/integration/render_profiling_test.rb +0 -154
- data/test/integration/security_test.rb +0 -80
- data/test/integration/standard_filter_test.rb +0 -776
- data/test/integration/tags/break_tag_test.rb +0 -15
- data/test/integration/tags/continue_tag_test.rb +0 -15
- data/test/integration/tags/for_tag_test.rb +0 -410
- data/test/integration/tags/if_else_tag_test.rb +0 -188
- data/test/integration/tags/include_tag_test.rb +0 -253
- data/test/integration/tags/increment_tag_test.rb +0 -23
- data/test/integration/tags/raw_tag_test.rb +0 -31
- data/test/integration/tags/standard_tag_test.rb +0 -296
- data/test/integration/tags/statements_test.rb +0 -111
- data/test/integration/tags/table_row_test.rb +0 -64
- data/test/integration/tags/unless_else_tag_test.rb +0 -26
- data/test/integration/template_test.rb +0 -332
- data/test/integration/trim_mode_test.rb +0 -529
- data/test/integration/variable_test.rb +0 -96
- data/test/test_helper.rb +0 -116
- data/test/truffle/truffle_test.rb +0 -9
- data/test/unit/block_unit_test.rb +0 -58
- data/test/unit/condition_unit_test.rb +0 -166
- data/test/unit/context_unit_test.rb +0 -489
- 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 -51
- 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 -164
- data/test/unit/tag_unit_test.rb +0 -21
- 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 -78
- data/test/unit/tokenizer_unit_test.rb +0 -55
- data/test/unit/variable_unit_test.rb +0 -162
data/lib/liquid/lexer.rb
CHANGED
@@ -1,24 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "strscan"
|
2
4
|
module Liquid
|
3
5
|
class Lexer
|
4
6
|
SPECIALS = {
|
5
|
-
'|'
|
6
|
-
'.'
|
7
|
-
':'
|
8
|
-
','
|
9
|
-
'['
|
10
|
-
']'
|
11
|
-
'('
|
12
|
-
')'
|
13
|
-
'?'
|
14
|
-
'-'
|
7
|
+
'|' => :pipe,
|
8
|
+
'.' => :dot,
|
9
|
+
':' => :colon,
|
10
|
+
',' => :comma,
|
11
|
+
'[' => :open_square,
|
12
|
+
']' => :close_square,
|
13
|
+
'(' => :open_round,
|
14
|
+
')' => :close_round,
|
15
|
+
'?' => :question,
|
16
|
+
'-' => :dash,
|
15
17
|
}.freeze
|
16
|
-
IDENTIFIER
|
18
|
+
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
|
17
19
|
SINGLE_STRING_LITERAL = /'[^\']*'/
|
18
20
|
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
19
|
-
NUMBER_LITERAL
|
20
|
-
DOTDOT
|
21
|
-
COMPARISON_OPERATOR
|
21
|
+
NUMBER_LITERAL = /-?\d+(\.\d+)?/
|
22
|
+
DOTDOT = /\.\./
|
23
|
+
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/
|
22
24
|
WHITESPACE_OR_NOTHING = /\s*/
|
23
25
|
|
24
26
|
def initialize(input)
|
@@ -31,16 +33,21 @@ module Liquid
|
|
31
33
|
until @ss.eos?
|
32
34
|
@ss.skip(WHITESPACE_OR_NOTHING)
|
33
35
|
break if @ss.eos?
|
34
|
-
tok =
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
tok = if (t = @ss.scan(COMPARISON_OPERATOR))
|
37
|
+
[:comparison, t]
|
38
|
+
elsif (t = @ss.scan(SINGLE_STRING_LITERAL))
|
39
|
+
[:string, t]
|
40
|
+
elsif (t = @ss.scan(DOUBLE_STRING_LITERAL))
|
41
|
+
[:string, t]
|
42
|
+
elsif (t = @ss.scan(NUMBER_LITERAL))
|
43
|
+
[:number, t]
|
44
|
+
elsif (t = @ss.scan(IDENTIFIER))
|
45
|
+
[:id, t]
|
46
|
+
elsif (t = @ss.scan(DOTDOT))
|
47
|
+
[:dotdot, t]
|
41
48
|
else
|
42
|
-
c
|
43
|
-
if s = SPECIALS[c]
|
49
|
+
c = @ss.getch
|
50
|
+
if (s = SPECIALS[c])
|
44
51
|
[s, c]
|
45
52
|
else
|
46
53
|
raise SyntaxError, "Unexpected character #{c}"
|
data/lib/liquid/locales/en.yml
CHANGED
@@ -13,14 +13,17 @@
|
|
13
13
|
for_invalid_attribute: "Invalid attribute in for loop. Valid attributes are limit and offset"
|
14
14
|
if: "Syntax Error in tag 'if' - Valid syntax: if [expression]"
|
15
15
|
include: "Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]"
|
16
|
-
|
16
|
+
inline_comment_invalid: "Syntax error in tag '#' - Each line of comments must be prefixed by the '#' character"
|
17
17
|
invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
|
18
|
+
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
19
|
+
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
20
|
+
tag_never_closed: "'%{block_name}' tag was never closed"
|
21
|
+
tag_termination: "Tag '%{token}' was not properly terminated with regexp: %{tag_end}"
|
18
22
|
unexpected_else: "%{block_name} tag does not expect 'else' tag"
|
19
23
|
unexpected_outer_tag: "Unexpected outer '%{tag}' tag"
|
20
|
-
|
24
|
+
unknown_tag: "Unknown tag '%{tag}'"
|
21
25
|
variable_termination: "Variable '%{token}' was not properly terminated with regexp: %{tag_end}"
|
22
|
-
tag_never_closed: "'%{block_name}' tag was never closed"
|
23
|
-
meta_syntax_error: "Liquid syntax error: #{e.message}"
|
24
|
-
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
25
26
|
argument:
|
26
27
|
include: "Argument error in tag 'include' - Illegal template name"
|
28
|
+
disabled:
|
29
|
+
tag: "usage is not allowed in this context"
|
data/lib/liquid/parse_context.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
class ParseContext
|
3
5
|
attr_accessor :locale, :line_number, :trim_whitespace, :depth
|
@@ -5,9 +7,11 @@ module Liquid
|
|
5
7
|
|
6
8
|
def initialize(options = {})
|
7
9
|
@template_options = options ? options.dup : {}
|
8
|
-
|
10
|
+
|
11
|
+
@locale = @template_options[:locale] ||= I18n.new
|
9
12
|
@warnings = []
|
10
|
-
|
13
|
+
|
14
|
+
self.depth = 0
|
11
15
|
self.partial = false
|
12
16
|
end
|
13
17
|
|
@@ -15,11 +19,23 @@ module Liquid
|
|
15
19
|
@options[option_key]
|
16
20
|
end
|
17
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
|
+
|
18
34
|
def partial=(value)
|
19
35
|
@partial = value
|
20
36
|
@options = value ? partial_options : @template_options
|
37
|
+
|
21
38
|
@error_mode = @options[:error_mode] || Template.error_mode
|
22
|
-
value
|
23
39
|
end
|
24
40
|
|
25
41
|
def partial_options
|
@@ -28,7 +44,7 @@ module Liquid
|
|
28
44
|
if dont_pass == true
|
29
45
|
{ locale: locale }
|
30
46
|
elsif dont_pass.is_a?(Array)
|
31
|
-
@template_options.reject { |k,
|
47
|
+
@template_options.reject { |k, _v| dont_pass.include?(k) }
|
32
48
|
else
|
33
49
|
@template_options
|
34
50
|
end
|
@@ -11,7 +11,7 @@ module Liquid
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def initialize(node, callbacks)
|
14
|
-
@node
|
14
|
+
@node = node
|
15
15
|
@callbacks = callbacks
|
16
16
|
end
|
17
17
|
|
@@ -28,7 +28,7 @@ module Liquid
|
|
28
28
|
item, new_context = @callbacks[node.class].call(node, context)
|
29
29
|
[
|
30
30
|
item,
|
31
|
-
ParseTreeVisitor.for(node, @callbacks).visit(new_context || context)
|
31
|
+
ParseTreeVisitor.for(node, @callbacks).visit(new_context || context),
|
32
32
|
]
|
33
33
|
end
|
34
34
|
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,15 +1,29 @@
|
|
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
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
25
|
parse_context.warnings << e
|
12
|
-
|
26
|
+
lax_parse(markup)
|
13
27
|
end
|
14
28
|
end
|
15
29
|
end
|
@@ -19,7 +33,7 @@ module Liquid
|
|
19
33
|
def strict_parse_with_error_context(markup)
|
20
34
|
strict_parse(markup)
|
21
35
|
rescue SyntaxError => e
|
22
|
-
e.line_number
|
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_node_without_profiling, :render_node_to_output
|
10
|
-
alias_method :render_node_to_output, :render_node_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,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'liquid/profiler/hooks'
|
2
4
|
|
3
5
|
module Liquid
|
@@ -23,7 +25,7 @@ module Liquid
|
|
23
25
|
# node.code
|
24
26
|
#
|
25
27
|
# # Which template and line number of this node.
|
26
|
-
# #
|
28
|
+
# # The top-level template name is `nil` by default, but can be set in the Liquid::Context before rendering.
|
27
29
|
# node.partial
|
28
30
|
# node.line_number
|
29
31
|
#
|
@@ -44,115 +46,94 @@ module Liquid
|
|
44
46
|
include Enumerable
|
45
47
|
|
46
48
|
class Timing
|
47
|
-
attr_reader :code, :
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
@
|
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 = []
|
54
59
|
end
|
55
60
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
def finish
|
65
|
-
@end_time = Time.now
|
66
|
-
end
|
67
|
-
|
68
|
-
def render_time
|
69
|
-
@end_time - @start_time
|
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
|
68
|
+
end
|
70
69
|
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
138
|
end
|
158
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,14 +12,16 @@ 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
23
|
start_int = to_integer(context.evaluate(@start_obj))
|
20
|
-
end_int
|
24
|
+
end_int = to_integer(context.evaluate(@end_obj))
|
21
25
|
start_int..end_int
|
22
26
|
end
|
23
27
|
|
@@ -33,5 +37,11 @@ module Liquid
|
|
33
37
|
Utils.to_integer(input)
|
34
38
|
end
|
35
39
|
end
|
40
|
+
|
41
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
42
|
+
def children
|
43
|
+
[@node.start_obj, @node.end_obj]
|
44
|
+
end
|
45
|
+
end
|
36
46
|
end
|
37
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
|