liquid-tag-parser 1.6.1 → 1.7.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/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
|