liquid-tag-parser 1.6.1 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +6 -1
- data/lib/liquid/tag/parser.rb +157 -78
- data/lib/liquid/tag/parser/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd8c6f633da42e565f8c6e744f5de6385cb58ee5
|
4
|
+
data.tar.gz: 43fefa58e961df937b95fe110b11c7a6315ed2d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6a50303cb1f2abf3ed5d807918b81682d264d91a87bf51d5c7ff3731131a7e559fb15530a49d884afd1cabb0f849ffa70560934972c80af01eb6840691fc5dc
|
7
|
+
data.tar.gz: c98e7886202657c54d46c9af31b4aac1cb60ab778f94af1920384cea4802d860c931e1a9fc012992edde64949b1f117691bb0bed9a588dc1820912f674644639
|
data/Gemfile
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
# Frozen-string-literal: true
|
2
|
+
# Copyright: 2017 Jordon Bedwell - MIT License
|
3
|
+
# Encoding: utf-8
|
4
|
+
|
1
5
|
source "https://rubygems.org"
|
2
6
|
gemspec
|
3
7
|
|
4
8
|
group :development do
|
5
|
-
gem
|
9
|
+
gem "pry", require: false
|
10
|
+
gem "rubocop", require: false
|
6
11
|
gem "rake", require: false
|
7
12
|
end
|
8
13
|
|
data/lib/liquid/tag/parser.rb
CHANGED
@@ -12,6 +12,7 @@ module Liquid
|
|
12
12
|
attr_reader :args, :raw_args
|
13
13
|
extend Forwardable::Extended
|
14
14
|
|
15
|
+
# --
|
15
16
|
rb_delegate :each, to: :@args
|
16
17
|
rb_delegate :key?, to: :@args
|
17
18
|
rb_delegate :to_h, to: :@args
|
@@ -27,122 +28,200 @@ module Liquid
|
|
27
28
|
rb_delegate :merge!, to: :@args
|
28
29
|
rb_delegate :deep_merge, to: :@args
|
29
30
|
rb_delegate :deep_merge!, to: :@args
|
30
|
-
rb_delegate :args_with_indifferent_access,
|
31
|
-
alias_of: :with_indifferent_access
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
31
|
+
rb_delegate :args_with_indifferent_access, {
|
32
|
+
to: :@args, alias_of: :with_indifferent_access
|
33
|
+
}
|
34
|
+
|
35
|
+
# --
|
36
|
+
FALSE = "!"
|
37
|
+
FLOAT = %r!\A\d+\.\d+\Z!
|
38
|
+
QUOTE = %r!("|')([^\1]*)(\1)!
|
39
|
+
SPECIAL = %r!(?<\!\\)(@|\!|:|=)!
|
40
|
+
BOOL = %r!\A(?<\!\\)(\!|@)([\w:]+)\Z!
|
41
|
+
UNQUOTED_SPECIAL = %r!(?<\!\\)(://)!
|
42
|
+
SPECIAL_ESCAPED = %r!\\(@|\!|:|=)!
|
43
|
+
KEY = %r!\b(?<\!\\):!
|
44
|
+
INT = %r!^\d+$!
|
45
|
+
TRUE = "@"
|
46
|
+
|
47
|
+
# This is taken from Ruby 2.4 standard library.
|
48
|
+
SHELLSPLIT = %r!\G\s*(?>([^\s\\\'\"]+)|'([^\']*)'|"((?:[^\"\\]|
|
49
|
+
\\.)*)"|(\\.?)|(\S))(\s|\z)?!mx
|
50
|
+
|
51
|
+
# --
|
44
52
|
def initialize(raw, defaults: {}, sep: "=")
|
45
53
|
@sep = sep
|
54
|
+
@unescaped_sep = sep
|
46
55
|
@rsep = Regexp.escape(sep)
|
47
|
-
@escaped_sep_regexp =
|
48
|
-
@sep_regexp =
|
56
|
+
@escaped_sep_regexp = %r!\\(#{@rsep})!
|
57
|
+
@sep_regexp = %r!\b(?<\!\\)(#{@rsep})!
|
49
58
|
@escaped_sep = "\\#{@sep}"
|
50
|
-
@
|
51
|
-
@args = {}
|
59
|
+
@args = defaults
|
52
60
|
@raw = raw
|
53
61
|
|
54
62
|
parse
|
55
63
|
end
|
56
64
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
next
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
# --
|
66
|
+
# Consumes a block and wraps around reusably on arguments.
|
67
|
+
# @return [Hash<Symbol,Object>,Array<String>]
|
68
|
+
# --
|
69
|
+
def skippable_loop(skip: [], hash: false)
|
70
|
+
@args.each_with_object(hash ? {} : []) do |(k, v), o|
|
71
|
+
skip_in_html?(k: k, v: v, skips: skip) ? next : yield([k, v], o)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# --
|
76
|
+
# @param [Array<Symbol>] skip keys to skip.
|
77
|
+
# Converts the arguments into an HTML attribute string.
|
78
|
+
# @return [String]
|
79
|
+
# --
|
80
|
+
def to_html(skip: [])
|
81
|
+
skippable_loop(skip: skip, hash: false) do |(k, v), o|
|
82
|
+
o << (v == true ? k.to_s : "#{k}=\"#{v}\"")
|
83
|
+
end.join(" ")
|
84
|
+
end
|
85
|
+
|
86
|
+
# --
|
87
|
+
# @param [Array<Symbol>] skip keys to skip.
|
88
|
+
# @param [true,false] for_html skip non-html values.
|
89
|
+
# Converts arguments into an HTML hash (or to arguments).
|
90
|
+
# @return [Hash]
|
91
|
+
# --
|
92
|
+
def to_h(skip: [], html: false)
|
93
|
+
return @args unless html
|
94
|
+
skippable_loop(skip: skip, hash: true) do |(k, v), o|
|
95
|
+
o[k] = v
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# --
|
100
|
+
# @param [String] k the key
|
101
|
+
# @param [Object] v the value
|
102
|
+
# @param [Array<Symbol>] skips personal skips.
|
103
|
+
# Determines if we should skip in HTML.
|
104
|
+
# @return [true,false]
|
105
|
+
# --
|
106
|
+
private
|
107
|
+
def skip_in_html?(k:, v:, skips: [])
|
108
|
+
k == :argv1 || v.is_a?(Array) || skips.include?(k) \
|
109
|
+
|| v.is_a?(Hash) || v == false
|
110
|
+
end
|
111
|
+
|
112
|
+
# --
|
113
|
+
# @return [true,nil] a truthy value.
|
114
|
+
# @param [Integer] i the current iteration.
|
115
|
+
# @param [String] keys the keys that will be split.
|
116
|
+
# @param [String] val the value.
|
117
|
+
# --
|
118
|
+
private
|
119
|
+
def argv1(i:, k:, v:)
|
120
|
+
if i.zero? && k.empty? && v !~ BOOL && v !~ @sep_regexp
|
121
|
+
@args[:argv1] = convert(v)
|
69
122
|
end
|
123
|
+
end
|
70
124
|
|
71
|
-
|
125
|
+
# --
|
126
|
+
# @return [Array<String,true|false>]
|
127
|
+
# Allows you to flip a value based on condition.
|
128
|
+
# @param [String] v the value.
|
129
|
+
# --
|
130
|
+
private
|
131
|
+
def flip_kv_bool(v)
|
132
|
+
[
|
133
|
+
v.gsub(BOOL, "\\2"),
|
134
|
+
v.start_with?(TRUE) ? true : false,
|
135
|
+
]
|
72
136
|
end
|
73
137
|
|
138
|
+
# --
|
139
|
+
# @param [Array<Symbol>] keys the keys.
|
140
|
+
# Builds a sub-hash or returns parent hash.
|
141
|
+
# @return [Hash]
|
142
|
+
# --
|
74
143
|
private
|
75
|
-
def
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
}
|
144
|
+
def build_hash(keys)
|
145
|
+
out = @args
|
146
|
+
|
147
|
+
if keys.size > 1
|
148
|
+
out = @args[keys[0]] ||= {}
|
149
|
+
keys[1...-1].each do |sk|
|
150
|
+
out = out[sk] ||= {}
|
151
|
+
end
|
84
152
|
end
|
85
153
|
|
86
|
-
|
154
|
+
out
|
87
155
|
end
|
88
156
|
|
157
|
+
# --
|
89
158
|
private
|
90
159
|
def parse
|
91
|
-
|
92
|
-
@args = @defaults.deep_merge(args.each_with_object(hash) do |k, h, out = h|
|
160
|
+
from_shellwords.each_with_index do |k, i|
|
93
161
|
keys, _, val = k.rpartition(@sep_regexp)
|
162
|
+
val.gsub!(@escaped_sep_regexp, @unescaped_sep)
|
163
|
+
val.gsub!(SPECIAL_ESCAPED, "\\1")
|
94
164
|
|
95
|
-
|
96
|
-
keys = val
|
97
|
-
keys,
|
165
|
+
next if argv1(i: i, k: keys, v: val)
|
166
|
+
keys, val = flip_kv_bool(val) if val =~ BOOL && keys.empty?
|
167
|
+
keys, val = val, nil if keys.empty?
|
98
168
|
keys = keys.split(KEY).map(&:to_sym)
|
99
169
|
|
100
|
-
|
101
|
-
|
102
|
-
keys
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
val = val.to_i if val =~ INT # key=1
|
108
|
-
val = val.to_f if val =~ FLOAT # key=0.1
|
109
|
-
val = false if val == "false" # key=false
|
110
|
-
val = false if val =~ NEGATIVE_BOOLEAN # !false
|
111
|
-
val = true if val =~ POSITIVE_BOOLEAN # @true
|
112
|
-
val = true if val == "true" # key=true
|
113
|
-
|
114
|
-
if val.is_a?(String)
|
115
|
-
then val = val.gsub(ESCAPED_BOOLEAN, "\\1")
|
116
|
-
end
|
170
|
+
set_val({
|
171
|
+
v: convert(val),
|
172
|
+
hash: build_hash(keys),
|
173
|
+
k: keys.last,
|
174
|
+
})
|
175
|
+
end
|
176
|
+
end
|
117
177
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
178
|
+
# --
|
179
|
+
private
|
180
|
+
def set_val(k:, v:, hash:)
|
181
|
+
hash[k] << v if hash[k].is_a?(Array)
|
182
|
+
hash[k] = [hash[k]].flatten << v if hash[k] && !hash[k].is_a?(Array)
|
183
|
+
hash[k] = v unless hash[k]
|
184
|
+
end
|
122
185
|
|
123
|
-
|
124
|
-
|
186
|
+
# --
|
187
|
+
# @return [true,false,Float,Integer]
|
188
|
+
# Convert a value to a native value.
|
189
|
+
# --
|
190
|
+
private
|
191
|
+
def convert(val)
|
192
|
+
return val.to_f if val =~ FLOAT
|
193
|
+
return val.to_i if val =~ INT
|
194
|
+
val
|
125
195
|
end
|
126
196
|
|
197
|
+
# --
|
198
|
+
# Wraps into `#shellsplit`, and first substitutes some values.
|
199
|
+
# @return [Array<String>]
|
200
|
+
# --
|
127
201
|
private
|
128
202
|
def from_shellwords
|
129
|
-
shellsplit(
|
130
|
-
|
131
|
-
|
203
|
+
shellsplit(
|
204
|
+
@raw.gsub(SPECIAL, "\\\\\\1")
|
205
|
+
.gsub(UNQUOTED_SPECIAL, "\\\\\\1")
|
206
|
+
.gsub(@sep_regexp, @escaped_sep))
|
132
207
|
end
|
133
208
|
|
134
|
-
#
|
135
|
-
#
|
136
|
-
#
|
137
|
-
#
|
209
|
+
# --
|
210
|
+
# @see Shellwords.shellsplit
|
211
|
+
# Because Shellwords.shellwords on < 2.4 has problems with
|
212
|
+
# quotes and \\, we ported this back, this pretty much the
|
213
|
+
# same thing except we replace some of the questionable
|
214
|
+
# code like `String.new`
|
215
|
+
# --
|
138
216
|
private
|
139
217
|
def shellsplit(line)
|
140
218
|
out, field = [], ""
|
141
219
|
|
220
|
+
# rubocop:disable Metrics/ParameterLists
|
142
221
|
line.scan(SHELLSPLIT) do |w, s, d, e, g, se|
|
143
222
|
raise ArgumentError, "Unmatched double quote: #{line.inspect}" if g
|
144
|
-
|
145
|
-
|| e.gsub(
|
223
|
+
field = field + (w || s || (d&.gsub(%r!\\([$`"\\\n])!,
|
224
|
+
'\\1')) || e.gsub(%r!\\(.)!, '\\1'))
|
146
225
|
|
147
226
|
if se
|
148
227
|
out << field
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: liquid-tag-parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordon Bedwell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|