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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1f6acfa193f9ff74b8a5b9ec7f097541c51e0bc0
4
- data.tar.gz: cec8af085b61e195f3d1f963c44aa59b12a3fd11
3
+ metadata.gz: bd8c6f633da42e565f8c6e744f5de6385cb58ee5
4
+ data.tar.gz: 43fefa58e961df937b95fe110b11c7a6315ed2d9
5
5
  SHA512:
6
- metadata.gz: 4246db8db03ad8aa8d07dbfc7da6693dfd27ab4119ec1626703d3e778c4c3c3055d161ecd3d3d7a7f318853ee009f67b445d95b69e79c82b07575c1dfcb8e012
7
- data.tar.gz: a5d85f6d83e6b34dfd9d9abaec0ad310af13c5cee7e247b742a7e929beb48ebb130e92ea1f7e7a815d0d1052e82ee8c302a2261acf53d092a7b550734d1a4d73
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 "pry", require: false
9
+ gem "pry", require: false
10
+ gem "rubocop", require: false
6
11
  gem "rake", require: false
7
12
  end
8
13
 
@@ -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, to: :@args, \
31
- alias_of: :with_indifferent_access
32
-
33
- FLOAT = /^\d+\.\d+$/
34
- BOOLEAN = /^(?<!\\)(\!|@)/
35
- NEGATIVE_BOOLEAN = /^(?<!\\)\!/
36
- BOOLEAN_QUOTE_REPLACEMENT = "\\1\\\\\\2"
37
- SHELLSPLIT = /\G\s*(?>([^\s\\\'\"]+)|'([^\']*)'|"((?:[^\"\\]|\\.)*)"|(\\.?)|(\S))(\s|\z)?/m
38
- BOOLEAN_QUOTE = /('|")((?<!\\)@|(?<!\\)\!)/
39
- POSITIVE_BOOLEAN = /^(?<!\\)\@/
40
- ESCAPED_BOOLEAN = /\\(@|\!)/
41
- KEY = /\b(?<!\\):/
42
- INT = /^\d+$/
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 = /\\#{@rsep}/
48
- @sep_regexp = /\b(?<!\\)#{@rsep}/
56
+ @escaped_sep_regexp = %r!\\(#{@rsep})!
57
+ @sep_regexp = %r!\b(?<\!\\)(#{@rsep})!
49
58
  @escaped_sep = "\\#{@sep}"
50
- @defaults = defaults
51
- @args = {}
59
+ @args = defaults
52
60
  @raw = raw
53
61
 
54
62
  parse
55
63
  end
56
64
 
57
- def to_html(skip: [], hash: false)
58
- out = @args.each_with_object(hash ? {} : []) do |(k, v), o|
59
- next if k == :argv1
60
- next if v.is_a?(Array)
61
- next if skip.include?(k)
62
- next if v.is_a?(Hash)
63
- next if v == false
64
-
65
- o[k] = v if hash
66
- unless hash
67
- o << (v == true ? k.to_s : "#{k}=\"#{v}\"")
68
- end
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
- hash ? out : out.join(" ")
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 argv1(args)
76
- val = args[0]
77
- hash = {}
78
-
79
- # !"@true" && !"key1:key2=val" but "argv1 @true key1:key2=val"
80
- if val !~ BOOLEAN && val !~ KEY && val !~ @sep_regexp
81
- hash = {
82
- argv1: args.delete_at(0)
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
- return args, hash
154
+ out
87
155
  end
88
156
 
157
+ # --
89
158
  private
90
159
  def parse
91
- args, hash = argv1(from_shellwords)
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
- val = val.gsub(@escaped_sep_regexp, @sep) # Unescape \\=
96
- keys = val.gsub(BOOLEAN, "") if keys.empty? && val =~ BOOLEAN # @true
97
- keys, val = val, nil if keys.empty? # key val
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
- if keys.size > 1
101
- h = h[keys[0]] ||= {}
102
- keys[1...-1].each do |sk|
103
- h = h[sk] ||= {}
104
- end
105
- end
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
- key = keys.last.to_sym
119
- h[key] << val if h[key].is_a?(Array)
120
- h[key] = [h[key]].flatten << val if h[key] && !h[key].is_a?(Array)
121
- h[key] = val unless h[key]
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
- out
124
- end)
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(@raw.gsub(/('|")([^\1]+)\1/) do |v|
130
- v.gsub(BOOLEAN_QUOTE, BOOLEAN_QUOTE_REPLACEMENT).gsub(@sep_regexp, @escaped_sep)
131
- end)
203
+ shellsplit(
204
+ @raw.gsub(SPECIAL, "\\\\\\1")
205
+ .gsub(UNQUOTED_SPECIAL, "\\\\\\1")
206
+ .gsub(@sep_regexp, @escaped_sep))
132
207
  end
133
208
 
134
- # Because Shellwords.shellwords on < 2.4 has problems
135
- # with quotes and \\, we ported this back, this pretty
136
- # much the same thing except we replace some of the
137
- # questionable code like `String.new`
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
- field = field + (w || s || (d && d.gsub(/\\([$`"\\\n])/, '\\1')) \
145
- || e.gsub(/\\(.)/, '\\1'))
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
@@ -5,7 +5,7 @@
5
5
  module Liquid
6
6
  class Tag
7
7
  class Parser
8
- VERSION = "1.6.1"
8
+ VERSION = "1.7.0"
9
9
  end
10
10
  end
11
11
  end
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.6.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-01 00:00:00.000000000 Z
11
+ date: 2017-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec