kronk 1.8.5 → 1.8.6
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.
- data/History.rdoc +12 -0
- data/Manifest.txt +0 -10
- data/README.rdoc +7 -2
- data/Rakefile +1 -0
- data/lib/kronk.rb +2 -5
- data/lib/kronk/cmd.rb +3 -3
- data/lib/kronk/data_string.rb +1 -1
- data/lib/kronk/diff/output.rb +2 -2
- data/lib/kronk/player.rb +1 -0
- data/lib/kronk/player/benchmark.rb +1 -0
- data/lib/kronk/player/suite.rb +20 -16
- data/lib/kronk/request.rb +9 -2
- data/lib/kronk/response.rb +2 -2
- data/lib/kronk/test/assertions.rb +4 -4
- data/test/test_helper.rb +1 -1
- metadata +49 -27
- data/lib/kronk/core_ext.rb +0 -110
- data/lib/kronk/path.rb +0 -334
- data/lib/kronk/path/match.rb +0 -130
- data/lib/kronk/path/matcher.rb +0 -193
- data/lib/kronk/path/transaction.rb +0 -341
- data/test/test_core_ext.rb +0 -74
- data/test/test_path.rb +0 -317
- data/test/test_path_match.rb +0 -105
- data/test/test_path_matcher.rb +0 -371
- data/test/test_transaction.rb +0 -520
data/lib/kronk/core_ext.rb
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
class Kronk
|
2
|
-
|
3
|
-
##
|
4
|
-
# Data manipulation and retrieval methods for Array and Hash classes.
|
5
|
-
|
6
|
-
module DataExt
|
7
|
-
|
8
|
-
##
|
9
|
-
# Checks if the given path exists and returns the first matching path
|
10
|
-
# as an array of keys. Returns false if no path is found.
|
11
|
-
|
12
|
-
def has_path? path
|
13
|
-
Kronk::Path.find path, self do |d,k,p|
|
14
|
-
return true
|
15
|
-
end
|
16
|
-
|
17
|
-
false
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
##
|
22
|
-
# Looks for data at paths matching path. Returns a hash of
|
23
|
-
# path array => data value pairs.
|
24
|
-
#
|
25
|
-
# If given a block will pass the parent data structure, the key
|
26
|
-
# or index of the item at given path, and the full path
|
27
|
-
# as an array of keys for each found path.
|
28
|
-
#
|
29
|
-
# data = {:foo => "bar", :foobar => [:a, :b, {:foo => "other bar"}, :c]}
|
30
|
-
# data.find_data "**/foo" do |parent, key, path|
|
31
|
-
# p path
|
32
|
-
# p parent[key]
|
33
|
-
# puts "---"
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# # outputs:
|
37
|
-
# # [:foo]
|
38
|
-
# # "bar"
|
39
|
-
# # ---
|
40
|
-
# # [:foobar, 2, :foo]
|
41
|
-
# # "other bar"
|
42
|
-
# # ---
|
43
|
-
#
|
44
|
-
# # returns:
|
45
|
-
# # {[:foo] => "bar", [:foobar, 2, :foo] => "other bar"}
|
46
|
-
|
47
|
-
def find_data path, &block
|
48
|
-
Kronk::Path.find path, self, &block
|
49
|
-
end
|
50
|
-
|
51
|
-
|
52
|
-
##
|
53
|
-
# Finds and replaces the value of any match with the given new value.
|
54
|
-
# Returns true if matches were replaced, otherwise false.
|
55
|
-
#
|
56
|
-
# data = {:foo => "bar", :foobar => [:a, :b, {:foo => "other bar"}, :c]}
|
57
|
-
# data.replace_at_path "**=*bar", "BAR"
|
58
|
-
# #=> true
|
59
|
-
#
|
60
|
-
# data
|
61
|
-
# #=> {:foo => "BAR", :foobar => [:a, :b, {:foo => "BAR"}, :c]}
|
62
|
-
#
|
63
|
-
# Note: Specifying a limit will allow only "limit" number of items to be
|
64
|
-
# set but may yield unpredictible results for non-ordered Hashes.
|
65
|
-
# It's also important to realize that arrays are modified starting with
|
66
|
-
# the last index, going down.
|
67
|
-
|
68
|
-
def replace_at_path path, value, limit=nil
|
69
|
-
count = 0
|
70
|
-
|
71
|
-
Kronk::Path.find path, self do |data, key, path_arr|
|
72
|
-
count = count.next
|
73
|
-
data[key] = value
|
74
|
-
|
75
|
-
return true if limit && count >= limit
|
76
|
-
end
|
77
|
-
|
78
|
-
return count > 0
|
79
|
-
end
|
80
|
-
|
81
|
-
|
82
|
-
##
|
83
|
-
# Similar to DataExt#replace_at_path but deletes found items.
|
84
|
-
# Returns a hash of path/value pairs of deleted items.
|
85
|
-
#
|
86
|
-
# data = {:foo => "bar", :foobar => [:a, :b, {:foo => "other bar"}, :c]}
|
87
|
-
# data.replace_at_path "**=*bar", "BAR"
|
88
|
-
# #=> {[:foo] => "bar", [:foobar, 2, :foo] => "other bar"}
|
89
|
-
|
90
|
-
def delete_at_path path, limit=nil
|
91
|
-
count = 0
|
92
|
-
out = {}
|
93
|
-
|
94
|
-
Kronk::Path.find path, self do |data, key, path_arr|
|
95
|
-
count = count.next
|
96
|
-
out[path_arr] = data[key]
|
97
|
-
|
98
|
-
data.respond_to(:delete_at) ? data.delete_at(key) : data.delete(key)
|
99
|
-
|
100
|
-
return true if limit && count >= limit
|
101
|
-
end
|
102
|
-
|
103
|
-
return count > 0
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
|
109
|
-
Array.send :include, Kronk::DataExt
|
110
|
-
Hash.send :include, Kronk::DataExt
|
data/lib/kronk/path.rb
DELETED
@@ -1,334 +0,0 @@
|
|
1
|
-
class Kronk
|
2
|
-
|
3
|
-
##
|
4
|
-
# Finds specific data points from a nested Hash or Array data structure
|
5
|
-
# through the use of a file-glob-like path selector.
|
6
|
-
#
|
7
|
-
# Special characters are: "/ * ? = | \ . , \ ( )"
|
8
|
-
# and are interpreted as follows:
|
9
|
-
#
|
10
|
-
# :foo/ - walk down tree by one level from key "foo"
|
11
|
-
# :*/foo - walk down tree from any parent with key "foo" as a child
|
12
|
-
# :foo1|foo2 - return elements with key value of "foo1" or "foo2"
|
13
|
-
# :foo(1|2) - same behavior as above
|
14
|
-
# :foo=val - return elements where key has a value of val
|
15
|
-
# :foo\* - return root-level element with key "foo*" ('*' char is escaped)
|
16
|
-
# :**/foo - recursively search for key "foo"
|
17
|
-
# :foo? - return keys that match /\Afoo.?\Z/
|
18
|
-
# :2..10 - match any integer from 2 to 10
|
19
|
-
# :2...10 - match any integer from 2 to 9
|
20
|
-
# :2,5 - match any integer from 2 to 7
|
21
|
-
#
|
22
|
-
# Examples:
|
23
|
-
#
|
24
|
-
# # Recursively look for elements with value "val" under top element "root"
|
25
|
-
# Path.find "root/**=val", data
|
26
|
-
#
|
27
|
-
# # Find child elements of "root" that have a key of "foo" or "bar"
|
28
|
-
# Path.find "root/foo|bar", data
|
29
|
-
#
|
30
|
-
# # Recursively find child elements of root whose value is 1, 2, or 3.
|
31
|
-
# Path.find "root/**=1..3", data
|
32
|
-
#
|
33
|
-
# # Recursively find child elements of root of literal value "1..3"
|
34
|
-
# Path.find "root/**=\\1..3", data
|
35
|
-
|
36
|
-
class Path
|
37
|
-
|
38
|
-
# Used as path instruction to go up one path level.
|
39
|
-
module PARENT; end
|
40
|
-
|
41
|
-
# Mapping of letters to Regexp options.
|
42
|
-
REGEX_OPTS = {
|
43
|
-
"i" => Regexp::IGNORECASE,
|
44
|
-
"m" => Regexp::MULTILINE,
|
45
|
-
"u" => (Regexp::FIXEDENCODING if defined?(Regexp::FIXEDENCODING)),
|
46
|
-
"x" => Regexp::EXTENDED
|
47
|
-
}
|
48
|
-
|
49
|
-
# The path item delimiter character "/"
|
50
|
-
DCH = "/"
|
51
|
-
|
52
|
-
# The replacement character "%" for path mapping
|
53
|
-
RCH = "%"
|
54
|
-
|
55
|
-
# The path character to assign value "="
|
56
|
-
VCH = "="
|
57
|
-
|
58
|
-
# The escape character to use any PATH_CHARS as its literal.
|
59
|
-
ECH = "\\"
|
60
|
-
|
61
|
-
# The Regexp escaped version of ECH.
|
62
|
-
RECH = Regexp.escape ECH
|
63
|
-
|
64
|
-
# The EndOfPath delimiter after which regex opt chars may be specified.
|
65
|
-
EOP = DCH + DCH
|
66
|
-
|
67
|
-
# The key string that represents PARENT.
|
68
|
-
PARENT_KEY = ".."
|
69
|
-
|
70
|
-
# The key string that indicates recursive lookup.
|
71
|
-
RECUR_KEY = "**"
|
72
|
-
|
73
|
-
|
74
|
-
##
|
75
|
-
# Instantiate a Path object with a String data path.
|
76
|
-
# Path.new "/path/**/to/*=bar/../../**/last"
|
77
|
-
|
78
|
-
def initialize path_str, regex_opts=nil, &block
|
79
|
-
path_str = path_str.dup
|
80
|
-
@path = self.class.parse_path_str path_str, regex_opts, &block
|
81
|
-
end
|
82
|
-
|
83
|
-
|
84
|
-
##
|
85
|
-
# Finds the current path in the given data structure.
|
86
|
-
# Returns a Hash of path_ary => data pairs for each match.
|
87
|
-
#
|
88
|
-
# If a block is given, yields the parent data object matched,
|
89
|
-
# the key, and the path array.
|
90
|
-
#
|
91
|
-
# data = {:path => {:foo => :bar, :sub => {:foo => :bar2}}, :other => nil}
|
92
|
-
# path = Path.new "path/**/foo"
|
93
|
-
#
|
94
|
-
# all_args = []
|
95
|
-
#
|
96
|
-
# path.find_in data |*args|
|
97
|
-
# all_args << args
|
98
|
-
# end
|
99
|
-
# #=> {[:path, :foo] => :bar, [:path, :sub, :foo] => :bar2}
|
100
|
-
#
|
101
|
-
# all_args
|
102
|
-
# #=> [
|
103
|
-
# #=> [{:foo => :bar, :sub => {:foo => :bar2}}, :foo, [:path, :foo]],
|
104
|
-
# #=> [{:foo => :bar2}, :foo, [:path, :sub, :foo]]
|
105
|
-
# #=> ]
|
106
|
-
|
107
|
-
def find_in data
|
108
|
-
matches = {[] => data}
|
109
|
-
|
110
|
-
@path.each_with_index do |matcher, i|
|
111
|
-
last_item = i == @path.length - 1
|
112
|
-
|
113
|
-
self.class.assign_find(matches, data, matcher) do |sdata, key, spath|
|
114
|
-
yield sdata, key, spath if last_item && block_given?
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
matches
|
119
|
-
end
|
120
|
-
|
121
|
-
|
122
|
-
##
|
123
|
-
# Returns a path-keyed data hash. Be careful of mixed key types in hashes
|
124
|
-
# as Symbols and Strings both use #to_s.
|
125
|
-
#
|
126
|
-
# Path.pathed {'foo' => %w{thing bar}, 'fizz' => {'buzz' => 123}}
|
127
|
-
# #=> {
|
128
|
-
# # '/foo/0' => 'thing',
|
129
|
-
# # '/foo/1' => 'bar',
|
130
|
-
# # '/fizz/buzz' => 123
|
131
|
-
# # }
|
132
|
-
|
133
|
-
def self.pathed data, escape=true
|
134
|
-
new_data = {}
|
135
|
-
|
136
|
-
find "**", data do |subdata, key, path|
|
137
|
-
next if Array === subdata[key] || Hash === subdata[key]
|
138
|
-
path_str = "#{DCH}#{join(path, escape)}"
|
139
|
-
new_data[path_str] = subdata[key]
|
140
|
-
end
|
141
|
-
|
142
|
-
new_data
|
143
|
-
end
|
144
|
-
|
145
|
-
|
146
|
-
SPECIAL_CHARS = "*?()|/."
|
147
|
-
R_SPECIAL_CHARS = /[\0#{Regexp.escape SPECIAL_CHARS}]/u
|
148
|
-
|
149
|
-
##
|
150
|
-
# Joins an Array into a path String.
|
151
|
-
|
152
|
-
def self.join path_arr, escape=true
|
153
|
-
path_str = path_arr.join("\0")
|
154
|
-
if escape
|
155
|
-
path_str.gsub!(R_SPECIAL_CHARS){|c| c == "\0" ? DCH : "\\#{c}"}
|
156
|
-
else
|
157
|
-
path_str.gsub! "\0", DCH
|
158
|
-
end
|
159
|
-
path_str
|
160
|
-
end
|
161
|
-
|
162
|
-
|
163
|
-
##
|
164
|
-
# Fully streamed version of:
|
165
|
-
# Path.new(str_path).find_in data
|
166
|
-
#
|
167
|
-
# See Path#find_in for usage.
|
168
|
-
|
169
|
-
def self.find path_str, data, regex_opts=nil, &block
|
170
|
-
matches = {[] => data}
|
171
|
-
|
172
|
-
parse_path_str path_str, regex_opts do |matcher, last_item|
|
173
|
-
assign_find matches, data, matcher do |sdata, key, spath|
|
174
|
-
yield sdata, key, spath if last_item && block_given?
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
matches
|
179
|
-
end
|
180
|
-
|
181
|
-
|
182
|
-
##
|
183
|
-
# Common find functionality that assigns to the matches hash.
|
184
|
-
|
185
|
-
def self.assign_find matches, data, matcher
|
186
|
-
matches.keys.each do |path|
|
187
|
-
pdata = matches.delete path
|
188
|
-
|
189
|
-
if matcher.key == PARENT
|
190
|
-
path = path[0..-2]
|
191
|
-
subdata = data_at_path path[0..-2], data
|
192
|
-
|
193
|
-
#!! Avoid yielding parent more than once
|
194
|
-
next if matches[path]
|
195
|
-
|
196
|
-
yield subdata, path.last, path if block_given?
|
197
|
-
matches[path] = subdata[path.last]
|
198
|
-
next
|
199
|
-
end
|
200
|
-
|
201
|
-
matcher.find_in pdata, path do |sdata, key, spath|
|
202
|
-
yield sdata, key, spath if block_given?
|
203
|
-
matches[spath] = sdata[key]
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
|
209
|
-
##
|
210
|
-
# Returns the data object found at the given path array.
|
211
|
-
# Returns nil if not found.
|
212
|
-
|
213
|
-
def self.data_at_path path_arr, data
|
214
|
-
c_data = data
|
215
|
-
|
216
|
-
path_arr.each do |key|
|
217
|
-
c_data = c_data[key]
|
218
|
-
end
|
219
|
-
|
220
|
-
c_data
|
221
|
-
|
222
|
-
rescue NoMethodError, TypeError
|
223
|
-
nil
|
224
|
-
end
|
225
|
-
|
226
|
-
|
227
|
-
##
|
228
|
-
# Parses a path String into an Array of arrays containing
|
229
|
-
# matchers for key, value, and any special modifiers
|
230
|
-
# such as recursion.
|
231
|
-
#
|
232
|
-
# Path.parse_path_str "/path/**/to/*=bar/../../**/last"
|
233
|
-
# #=> [["path",ANY_VALUE,false],["to",ANY_VALUE,true],[/.*/,"bar",false],
|
234
|
-
# # [PARENT,ANY_VALUE,false],[PARENT,ANY_VALUE,false],
|
235
|
-
# # ["last",ANY_VALUE,true]]
|
236
|
-
#
|
237
|
-
# Note: Path.parse_path_str will slice the original path string
|
238
|
-
# until it is empty.
|
239
|
-
|
240
|
-
def self.parse_path_str path, regex_opts=nil
|
241
|
-
path = path.dup
|
242
|
-
regex_opts = parse_regex_opts! path, regex_opts
|
243
|
-
|
244
|
-
parsed = []
|
245
|
-
|
246
|
-
escaped = false
|
247
|
-
key = ""
|
248
|
-
value = nil
|
249
|
-
recur = false
|
250
|
-
next_item = false
|
251
|
-
|
252
|
-
until path.empty?
|
253
|
-
char = path.slice!(0..0)
|
254
|
-
|
255
|
-
case char
|
256
|
-
when DCH
|
257
|
-
next_item = true
|
258
|
-
char = ""
|
259
|
-
|
260
|
-
when VCH
|
261
|
-
value = ""
|
262
|
-
next
|
263
|
-
|
264
|
-
when ECH
|
265
|
-
escaped = true
|
266
|
-
next
|
267
|
-
end unless escaped
|
268
|
-
|
269
|
-
char = "#{ECH}#{char}" if escaped
|
270
|
-
|
271
|
-
if value
|
272
|
-
value << char
|
273
|
-
else
|
274
|
-
key << char
|
275
|
-
end
|
276
|
-
|
277
|
-
next_item = true if path.empty?
|
278
|
-
|
279
|
-
if next_item
|
280
|
-
next_item = false
|
281
|
-
|
282
|
-
if key == RECUR_KEY
|
283
|
-
key = "*"
|
284
|
-
recur = true
|
285
|
-
key = "" and next unless value || path.empty?
|
286
|
-
|
287
|
-
elsif key == PARENT_KEY
|
288
|
-
key = PARENT
|
289
|
-
|
290
|
-
if recur
|
291
|
-
key = "" and next unless value
|
292
|
-
key = "*"
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
# Make sure we're not trying to access /./thing
|
297
|
-
unless key =~ /^\.?$/ && !value
|
298
|
-
matcher = Matcher.new :key => key,
|
299
|
-
:value => value,
|
300
|
-
:recursive => recur,
|
301
|
-
:regex_opts => regex_opts
|
302
|
-
|
303
|
-
parsed << matcher
|
304
|
-
yield matcher, path.empty? if block_given?
|
305
|
-
end
|
306
|
-
|
307
|
-
key = ""
|
308
|
-
value = nil
|
309
|
-
recur = false
|
310
|
-
end
|
311
|
-
|
312
|
-
escaped = false
|
313
|
-
end
|
314
|
-
|
315
|
-
parsed
|
316
|
-
end
|
317
|
-
|
318
|
-
|
319
|
-
##
|
320
|
-
# Parses the tail end of a path String to determine regexp matching flags.
|
321
|
-
|
322
|
-
def self.parse_regex_opts! path, default=nil
|
323
|
-
opts = default || 0
|
324
|
-
|
325
|
-
return default unless
|
326
|
-
path =~ %r{[^#{RECH}]#{EOP}[#{REGEX_OPTS.keys.join}]+\Z}
|
327
|
-
|
328
|
-
path.slice!(%r{#{EOP}[#{REGEX_OPTS.keys.join}]+\Z}).to_s.
|
329
|
-
each_char{|c| opts |= REGEX_OPTS[c] || 0}
|
330
|
-
|
331
|
-
opts if opts > 0
|
332
|
-
end
|
333
|
-
end
|
334
|
-
end
|
data/lib/kronk/path/match.rb
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
##
|
2
|
-
# Represents the single match of a relative path against a data set.
|
3
|
-
|
4
|
-
class Kronk::Path::Match < Array
|
5
|
-
|
6
|
-
attr_accessor :matches, :splat
|
7
|
-
|
8
|
-
def initialize *args
|
9
|
-
@matches = []
|
10
|
-
@splat = []
|
11
|
-
super
|
12
|
-
end
|
13
|
-
|
14
|
-
|
15
|
-
def [] selector
|
16
|
-
path_match = super
|
17
|
-
|
18
|
-
if self.class === path_match
|
19
|
-
path_match.matches = @matches.dup
|
20
|
-
path_match.splat = @splat.map{|key, sp| [key, sp.dup]}
|
21
|
-
end
|
22
|
-
|
23
|
-
path_match
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
def append_splat id, key # :nodoc:
|
28
|
-
if @splat[-1] && @splat[-1][0] == id
|
29
|
-
@splat[-1][1] << key
|
30
|
-
else
|
31
|
-
@splat << [id, [key]]
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
|
-
def dup # :nodoc:
|
37
|
-
path_match = super
|
38
|
-
path_match.matches = @matches.dup
|
39
|
-
path_match.splat = @splat.map{|key, sp| [key, sp.dup]}
|
40
|
-
path_match
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
def append_match_for str, path # :nodoc:
|
45
|
-
match = @matches[str.to_i-1]
|
46
|
-
if match && !(String === match) && path[-1].empty?
|
47
|
-
path[-1] = match
|
48
|
-
else
|
49
|
-
path[-1] << match.to_s
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
|
54
|
-
##
|
55
|
-
# Builds a path array by replacing %n and %% values with matches and splat.
|
56
|
-
#
|
57
|
-
# matches = Path.find_in "**/foo=bar", data
|
58
|
-
# # [["path", "to", "foo"]]
|
59
|
-
#
|
60
|
-
# matches.first.make_path "root/%%/foo"
|
61
|
-
# # ["root", "path", "to", "foo"]
|
62
|
-
#
|
63
|
-
# matches = Path.find_in "path/*/(foo)=bar", data
|
64
|
-
# # [["path", "to", "foo"]]
|
65
|
-
#
|
66
|
-
# matches.first.make_path "root/%1/%2"
|
67
|
-
# # ["root", "to", "foo"]
|
68
|
-
|
69
|
-
def make_path path_map, regex_opts=nil, &block
|
70
|
-
tmpsplat = @splat.dup
|
71
|
-
path = []
|
72
|
-
escape = false
|
73
|
-
replace = false
|
74
|
-
new_item = true
|
75
|
-
rindex = ""
|
76
|
-
|
77
|
-
path_map.to_s.chars do |chr|
|
78
|
-
case chr
|
79
|
-
when Kronk::Path::ECH
|
80
|
-
escape = true
|
81
|
-
|
82
|
-
when Kronk::Path::DCH
|
83
|
-
new_item = true
|
84
|
-
|
85
|
-
when Kronk::Path::RCH
|
86
|
-
if replace
|
87
|
-
if rindex.empty?
|
88
|
-
unless tmpsplat.empty?
|
89
|
-
items = tmpsplat.shift[1].dup
|
90
|
-
if new_item
|
91
|
-
new_item = false
|
92
|
-
else
|
93
|
-
path[-1] = path[-1].dup << items.shift
|
94
|
-
end
|
95
|
-
path.concat items
|
96
|
-
end
|
97
|
-
replace = false
|
98
|
-
else
|
99
|
-
append_match_for(rindex, path)
|
100
|
-
rindex = ""
|
101
|
-
end
|
102
|
-
|
103
|
-
next
|
104
|
-
else
|
105
|
-
replace = true
|
106
|
-
end
|
107
|
-
end and next unless escape
|
108
|
-
|
109
|
-
if replace
|
110
|
-
if new_item && !rindex.empty? || chr.to_i.to_s != chr || escape
|
111
|
-
append_match_for(rindex, path) unless rindex.empty?
|
112
|
-
rindex = ""
|
113
|
-
replace = false
|
114
|
-
else
|
115
|
-
rindex << chr
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
path << "" if new_item
|
120
|
-
path.last << chr unless replace
|
121
|
-
|
122
|
-
new_item = false
|
123
|
-
escape = false
|
124
|
-
end
|
125
|
-
|
126
|
-
append_match_for(rindex, path) unless rindex.empty?
|
127
|
-
|
128
|
-
path
|
129
|
-
end
|
130
|
-
end
|