liquid 5.6.0 → 5.8.7
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 +67 -2
- data/README.md +1 -1
- data/lib/liquid/context.rb +5 -1
- data/lib/liquid/expression.rb +97 -21
- data/lib/liquid/lexer.rb +69 -127
- data/lib/liquid/locales/en.yml +2 -0
- data/lib/liquid/parse_context.rb +25 -3
- data/lib/liquid/parser.rb +2 -2
- data/lib/liquid/range_lookup.rb +3 -3
- data/lib/liquid/standardfilters.rb +153 -61
- data/lib/liquid/tags/cycle.rb +7 -1
- data/lib/liquid/tags/decrement.rb +1 -1
- data/lib/liquid/tags/doc.rb +81 -0
- data/lib/liquid/tags/for.rb +1 -1
- data/lib/liquid/tags/if.rb +1 -1
- data/lib/liquid/tags/include.rb +1 -1
- data/lib/liquid/tags/increment.rb +1 -1
- data/lib/liquid/tags/render.rb +1 -1
- data/lib/liquid/tags.rb +2 -0
- data/lib/liquid/tokenizer.rb +129 -13
- data/lib/liquid/utils.rb +96 -0
- data/lib/liquid/variable.rb +3 -3
- data/lib/liquid/variable_lookup.rb +13 -5
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +4 -1
- metadata +6 -5
data/lib/liquid/tokenizer.rb
CHANGED
@@ -1,20 +1,43 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "strscan"
|
4
|
+
|
3
5
|
module Liquid
|
4
6
|
class Tokenizer
|
5
7
|
attr_reader :line_number, :for_liquid_tag
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
TAG_END = /%\}/
|
10
|
+
TAG_OR_VARIABLE_START = /\{[\{\%]/
|
11
|
+
NEWLINE = /\n/
|
12
|
+
|
13
|
+
OPEN_CURLEY = "{".ord
|
14
|
+
CLOSE_CURLEY = "}".ord
|
15
|
+
PERCENTAGE = "%".ord
|
16
|
+
|
17
|
+
def initialize(
|
18
|
+
source:,
|
19
|
+
string_scanner:,
|
20
|
+
line_numbers: false,
|
21
|
+
line_number: nil,
|
22
|
+
for_liquid_tag: false
|
23
|
+
)
|
24
|
+
@line_number = line_number || (line_numbers ? 1 : nil)
|
10
25
|
@for_liquid_tag = for_liquid_tag
|
11
|
-
@
|
12
|
-
@
|
26
|
+
@source = source.to_s.to_str
|
27
|
+
@offset = 0
|
28
|
+
@tokens = []
|
29
|
+
|
30
|
+
if @source
|
31
|
+
@ss = string_scanner
|
32
|
+
@ss.string = @source
|
33
|
+
tokenize
|
34
|
+
end
|
13
35
|
end
|
14
36
|
|
15
37
|
def shift
|
16
38
|
token = @tokens[@offset]
|
17
|
-
|
39
|
+
|
40
|
+
return unless token
|
18
41
|
|
19
42
|
@offset += 1
|
20
43
|
|
@@ -28,18 +51,111 @@ module Liquid
|
|
28
51
|
private
|
29
52
|
|
30
53
|
def tokenize
|
31
|
-
|
54
|
+
if @for_liquid_tag
|
55
|
+
@tokens = @source.split("\n")
|
56
|
+
else
|
57
|
+
@tokens << shift_normal until @ss.eos?
|
58
|
+
end
|
59
|
+
|
60
|
+
@source = nil
|
61
|
+
@ss = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def shift_normal
|
65
|
+
token = next_token
|
66
|
+
|
67
|
+
return unless token
|
68
|
+
|
69
|
+
token
|
70
|
+
end
|
71
|
+
|
72
|
+
def next_token
|
73
|
+
# possible states: :text, :tag, :variable
|
74
|
+
byte_a = @ss.peek_byte
|
75
|
+
|
76
|
+
if byte_a == OPEN_CURLEY
|
77
|
+
@ss.scan_byte
|
78
|
+
|
79
|
+
byte_b = @ss.peek_byte
|
80
|
+
|
81
|
+
if byte_b == PERCENTAGE
|
82
|
+
@ss.scan_byte
|
83
|
+
return next_tag_token
|
84
|
+
elsif byte_b == OPEN_CURLEY
|
85
|
+
@ss.scan_byte
|
86
|
+
return next_variable_token
|
87
|
+
end
|
88
|
+
|
89
|
+
@ss.pos -= 1
|
90
|
+
end
|
32
91
|
|
33
|
-
|
92
|
+
next_text_token
|
93
|
+
end
|
34
94
|
|
35
|
-
|
95
|
+
def next_text_token
|
96
|
+
start = @ss.pos
|
36
97
|
|
37
|
-
|
38
|
-
|
39
|
-
@
|
98
|
+
unless @ss.skip_until(TAG_OR_VARIABLE_START)
|
99
|
+
token = @ss.rest
|
100
|
+
@ss.terminate
|
101
|
+
return token
|
40
102
|
end
|
41
103
|
|
42
|
-
|
104
|
+
pos = @ss.pos -= 2
|
105
|
+
@source.byteslice(start, pos - start)
|
106
|
+
rescue ::ArgumentError => e
|
107
|
+
if e.message == "invalid byte sequence in #{@ss.string.encoding}"
|
108
|
+
raise SyntaxError, "Invalid byte sequence in #{@ss.string.encoding}"
|
109
|
+
else
|
110
|
+
raise
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def next_variable_token
|
115
|
+
start = @ss.pos - 2
|
116
|
+
|
117
|
+
byte_a = byte_b = @ss.scan_byte
|
118
|
+
|
119
|
+
while byte_b
|
120
|
+
byte_a = @ss.scan_byte while byte_a && (byte_a != CLOSE_CURLEY && byte_a != OPEN_CURLEY)
|
121
|
+
|
122
|
+
break unless byte_a
|
123
|
+
|
124
|
+
if @ss.eos?
|
125
|
+
return byte_a == CLOSE_CURLEY ? @source.byteslice(start, @ss.pos - start) : "{{"
|
126
|
+
end
|
127
|
+
|
128
|
+
byte_b = @ss.scan_byte
|
129
|
+
|
130
|
+
if byte_a == CLOSE_CURLEY
|
131
|
+
if byte_b == CLOSE_CURLEY
|
132
|
+
return @source.byteslice(start, @ss.pos - start)
|
133
|
+
elsif byte_b != CLOSE_CURLEY
|
134
|
+
@ss.pos -= 1
|
135
|
+
return @source.byteslice(start, @ss.pos - start)
|
136
|
+
end
|
137
|
+
elsif byte_a == OPEN_CURLEY && byte_b == PERCENTAGE
|
138
|
+
return next_tag_token_with_start(start)
|
139
|
+
end
|
140
|
+
|
141
|
+
byte_a = byte_b
|
142
|
+
end
|
143
|
+
|
144
|
+
"{{"
|
145
|
+
end
|
146
|
+
|
147
|
+
def next_tag_token
|
148
|
+
start = @ss.pos - 2
|
149
|
+
if (len = @ss.skip_until(TAG_END))
|
150
|
+
@source.byteslice(start, len + 2)
|
151
|
+
else
|
152
|
+
"{%"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def next_tag_token_with_start(start)
|
157
|
+
@ss.skip_until(TAG_END)
|
158
|
+
@source.byteslice(start, @ss.pos - start)
|
43
159
|
end
|
44
160
|
end
|
45
161
|
end
|
data/lib/liquid/utils.rb
CHANGED
@@ -89,5 +89,101 @@ module Liquid
|
|
89
89
|
# Otherwise return the object itself
|
90
90
|
obj
|
91
91
|
end
|
92
|
+
|
93
|
+
def self.to_s(obj, seen = {})
|
94
|
+
case obj
|
95
|
+
when Hash
|
96
|
+
# If the custom hash implementation overrides `#to_s`, use their
|
97
|
+
# custom implementation. Otherwise we use Liquid's default
|
98
|
+
# implementation.
|
99
|
+
if obj.class.instance_method(:to_s) == HASH_TO_S_METHOD
|
100
|
+
hash_inspect(obj, seen)
|
101
|
+
else
|
102
|
+
obj.to_s
|
103
|
+
end
|
104
|
+
when Array
|
105
|
+
array_inspect(obj, seen)
|
106
|
+
else
|
107
|
+
obj.to_s
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.inspect(obj, seen = {})
|
112
|
+
case obj
|
113
|
+
when Hash
|
114
|
+
# If the custom hash implementation overrides `#inspect`, use their
|
115
|
+
# custom implementation. Otherwise we use Liquid's default
|
116
|
+
# implementation.
|
117
|
+
if obj.class.instance_method(:inspect) == HASH_INSPECT_METHOD
|
118
|
+
hash_inspect(obj, seen)
|
119
|
+
else
|
120
|
+
obj.inspect
|
121
|
+
end
|
122
|
+
when Array
|
123
|
+
array_inspect(obj, seen)
|
124
|
+
else
|
125
|
+
obj.inspect
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.array_inspect(arr, seen = {})
|
130
|
+
if seen[arr.object_id]
|
131
|
+
return "[...]"
|
132
|
+
end
|
133
|
+
|
134
|
+
seen[arr.object_id] = true
|
135
|
+
str = +"["
|
136
|
+
cursor = 0
|
137
|
+
len = arr.length
|
138
|
+
|
139
|
+
while cursor < len
|
140
|
+
if cursor > 0
|
141
|
+
str << ", "
|
142
|
+
end
|
143
|
+
|
144
|
+
item_str = inspect(arr[cursor], seen)
|
145
|
+
str << item_str
|
146
|
+
cursor += 1
|
147
|
+
end
|
148
|
+
|
149
|
+
str << "]"
|
150
|
+
str
|
151
|
+
ensure
|
152
|
+
seen.delete(arr.object_id)
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.hash_inspect(hash, seen = {})
|
156
|
+
if seen[hash.object_id]
|
157
|
+
return "{...}"
|
158
|
+
end
|
159
|
+
seen[hash.object_id] = true
|
160
|
+
|
161
|
+
str = +"{"
|
162
|
+
first = true
|
163
|
+
hash.each do |key, value|
|
164
|
+
if first
|
165
|
+
first = false
|
166
|
+
else
|
167
|
+
str << ", "
|
168
|
+
end
|
169
|
+
|
170
|
+
key_str = inspect(key, seen)
|
171
|
+
str << key_str
|
172
|
+
str << "=>"
|
173
|
+
|
174
|
+
value_str = inspect(value, seen)
|
175
|
+
str << value_str
|
176
|
+
end
|
177
|
+
str << "}"
|
178
|
+
str
|
179
|
+
ensure
|
180
|
+
seen.delete(hash.object_id)
|
181
|
+
end
|
182
|
+
|
183
|
+
HASH_TO_S_METHOD = Hash.instance_method(:to_s)
|
184
|
+
private_constant :HASH_TO_S_METHOD
|
185
|
+
|
186
|
+
HASH_INSPECT_METHOD = Hash.instance_method(:inspect)
|
187
|
+
private_constant :HASH_INSPECT_METHOD
|
92
188
|
end
|
93
189
|
end
|
data/lib/liquid/variable.rb
CHANGED
@@ -61,7 +61,7 @@ module Liquid
|
|
61
61
|
|
62
62
|
def strict_parse(markup)
|
63
63
|
@filters = []
|
64
|
-
p =
|
64
|
+
p = @parse_context.new_parser(markup)
|
65
65
|
|
66
66
|
return if p.look(:end_of_string)
|
67
67
|
|
@@ -107,8 +107,8 @@ module Liquid
|
|
107
107
|
obj.each do |o|
|
108
108
|
render_obj_to_output(o, output)
|
109
109
|
end
|
110
|
-
|
111
|
-
output <<
|
110
|
+
else
|
111
|
+
output << Liquid::Utils.to_s(obj)
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
@@ -6,16 +6,20 @@ module Liquid
|
|
6
6
|
|
7
7
|
attr_reader :name, :lookups
|
8
8
|
|
9
|
-
def self.parse(markup)
|
10
|
-
new(markup)
|
9
|
+
def self.parse(markup, string_scanner = StringScanner.new(""), cache = nil)
|
10
|
+
new(markup, string_scanner, cache)
|
11
11
|
end
|
12
12
|
|
13
|
-
def initialize(markup)
|
13
|
+
def initialize(markup, string_scanner = StringScanner.new(""), cache = nil)
|
14
14
|
lookups = markup.scan(VariableParser)
|
15
15
|
|
16
16
|
name = lookups.shift
|
17
17
|
if name&.start_with?('[') && name&.end_with?(']')
|
18
|
-
name = Expression.parse(
|
18
|
+
name = Expression.parse(
|
19
|
+
name[1..-2],
|
20
|
+
string_scanner,
|
21
|
+
cache,
|
22
|
+
)
|
19
23
|
end
|
20
24
|
@name = name
|
21
25
|
|
@@ -25,7 +29,11 @@ module Liquid
|
|
25
29
|
@lookups.each_index do |i|
|
26
30
|
lookup = lookups[i]
|
27
31
|
if lookup&.start_with?('[') && lookup&.end_with?(']')
|
28
|
-
lookups[i] = Expression.parse(
|
32
|
+
lookups[i] = Expression.parse(
|
33
|
+
lookup[1..-2],
|
34
|
+
string_scanner,
|
35
|
+
cache,
|
36
|
+
)
|
29
37
|
elsif COMMAND_METHODS.include?(lookup)
|
30
38
|
@command_flags |= 1 << i
|
31
39
|
end
|
data/lib/liquid/version.rb
CHANGED
data/lib/liquid.rb
CHANGED
@@ -21,6 +21,8 @@
|
|
21
21
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
22
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
23
|
|
24
|
+
require "strscan"
|
25
|
+
|
24
26
|
module Liquid
|
25
27
|
FilterSeparator = /\|/
|
26
28
|
ArgumentSeparator = ','
|
@@ -44,6 +46,7 @@ module Liquid
|
|
44
46
|
VariableParser = /\[(?>[^\[\]]+|\g<0>)*\]|#{VariableSegment}+\??/o
|
45
47
|
|
46
48
|
RAISE_EXCEPTION_LAMBDA = ->(_e) { raise }
|
49
|
+
HAS_STRING_SCANNER_SCAN_BYTE = StringScanner.instance_methods.include?(:scan_byte)
|
47
50
|
end
|
48
51
|
|
49
52
|
require "liquid/version"
|
@@ -68,7 +71,6 @@ require 'liquid/extensions'
|
|
68
71
|
require 'liquid/errors'
|
69
72
|
require 'liquid/interrupts'
|
70
73
|
require 'liquid/strainer_template'
|
71
|
-
require 'liquid/expression'
|
72
74
|
require 'liquid/context'
|
73
75
|
require 'liquid/tag'
|
74
76
|
require 'liquid/block_body'
|
@@ -77,6 +79,7 @@ require 'liquid/variable'
|
|
77
79
|
require 'liquid/variable_lookup'
|
78
80
|
require 'liquid/range_lookup'
|
79
81
|
require 'liquid/resource_limits'
|
82
|
+
require 'liquid/expression'
|
80
83
|
require 'liquid/template'
|
81
84
|
require 'liquid/condition'
|
82
85
|
require 'liquid/utils'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: liquid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.8.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Lütke
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: strscan
|
@@ -15,14 +15,14 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - ">="
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version:
|
18
|
+
version: 3.1.1
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version:
|
25
|
+
version: 3.1.1
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: bigdecimal
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- lib/liquid/tags/continue.rb
|
121
121
|
- lib/liquid/tags/cycle.rb
|
122
122
|
- lib/liquid/tags/decrement.rb
|
123
|
+
- lib/liquid/tags/doc.rb
|
123
124
|
- lib/liquid/tags/echo.rb
|
124
125
|
- lib/liquid/tags/for.rb
|
125
126
|
- lib/liquid/tags/if.rb
|
@@ -158,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
158
159
|
- !ruby/object:Gem::Version
|
159
160
|
version: 1.3.7
|
160
161
|
requirements: []
|
161
|
-
rubygems_version: 3.6.
|
162
|
+
rubygems_version: 3.6.9
|
162
163
|
specification_version: 4
|
163
164
|
summary: A secure, non-evaling end user template engine with aesthetic markup.
|
164
165
|
test_files: []
|