re2 2.9.0 → 2.27.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 -0
- data/README.md +130 -22
- data/Rakefile +52 -90
- data/dependencies.yml +4 -4
- data/ext/re2/extconf.rb +256 -290
- data/ext/re2/re2.cc +1037 -318
- data/ext/re2/recipes.rb +24 -21
- data/lib/re2/regexp.rb +2 -0
- data/lib/re2/scanner.rb +2 -0
- data/lib/re2/string.rb +8 -6
- data/lib/re2/version.rb +1 -1
- data/lib/re2.rb +2 -0
- data/ports/archives/20260107.1.tar.gz +0 -0
- data/ports/archives/re2-2025-11-05.tar.gz +0 -0
- data/re2.gemspec +6 -4
- data/spec/kernel_spec.rb +2 -0
- data/spec/re2/match_data_spec.rb +520 -5
- data/spec/re2/regexp_spec.rb +342 -0
- data/spec/re2/scanner_spec.rb +185 -15
- data/spec/re2/set_spec.rb +123 -6
- data/spec/re2/string_spec.rb +2 -0
- data/spec/re2_spec.rb +219 -43
- data/spec/spec_helper.rb +2 -0
- metadata +17 -21
- data/ports/archives/20240116.1.tar.gz +0 -0
- data/ports/archives/re2-2024-03-01.tar.gz +0 -0
data/spec/re2/match_data_spec.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require 'objspace'
|
|
3
4
|
|
|
4
5
|
RSpec.describe RE2::MatchData do
|
|
@@ -9,6 +10,55 @@ RSpec.describe RE2::MatchData do
|
|
|
9
10
|
expect(ObjectSpace.memsize_of(matches1)).to be < ObjectSpace.memsize_of(matches2)
|
|
10
11
|
end
|
|
11
12
|
|
|
13
|
+
describe "#dup" do
|
|
14
|
+
it "returns a copy of the match data" do
|
|
15
|
+
md = RE2::Regexp.new('(\d+) (\d+)').match("123 456")
|
|
16
|
+
copy = md.dup
|
|
17
|
+
|
|
18
|
+
expect(copy.to_a).to eq(["123 456", "123", "456"])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "returns a different object" do
|
|
22
|
+
md = RE2::Regexp.new('(\d+) (\d+)').match("123 456")
|
|
23
|
+
copy = md.dup
|
|
24
|
+
|
|
25
|
+
expect(copy).to_not equal(md)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "preserves the original string" do
|
|
29
|
+
str = "bob 123"
|
|
30
|
+
md = RE2::Regexp.new('(\d+)').match(str)
|
|
31
|
+
copy = md.dup
|
|
32
|
+
|
|
33
|
+
expect(copy.string).to equal(str)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "preserves the original regexp" do
|
|
37
|
+
re = RE2::Regexp.new('(\d+)')
|
|
38
|
+
md = re.match("123")
|
|
39
|
+
copy = md.dup
|
|
40
|
+
|
|
41
|
+
expect(copy.regexp).to equal(re)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "raises an error when called on an uninitialized object" do
|
|
45
|
+
expect { described_class.allocate.dup }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe "#clone" do
|
|
50
|
+
it "returns a copy of the match data" do
|
|
51
|
+
md = RE2::Regexp.new('(\d+) (\d+)').match("123 456")
|
|
52
|
+
copy = md.clone
|
|
53
|
+
|
|
54
|
+
expect(copy.to_a).to eq(["123 456", "123", "456"])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "raises an error when called on an uninitialized object" do
|
|
58
|
+
expect { described_class.allocate.clone }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
12
62
|
describe "#to_a" do
|
|
13
63
|
it "is populated with the match and capturing groups" do
|
|
14
64
|
a = RE2::Regexp.new('w(o)(o)').match('woo').to_a
|
|
@@ -16,10 +66,16 @@ RSpec.describe RE2::MatchData do
|
|
|
16
66
|
expect(a).to eq(["woo", "o", "o"])
|
|
17
67
|
end
|
|
18
68
|
|
|
19
|
-
it "populates optional capturing groups with
|
|
69
|
+
it "populates optional capturing groups with empty strings if they match zero characters" do
|
|
20
70
|
a = RE2::Regexp.new('(\d?)(a)(b)').match('ab').to_a
|
|
21
71
|
|
|
22
|
-
expect(a).to eq(["ab",
|
|
72
|
+
expect(a).to eq(["ab", "", "a", "b"])
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "distinguishes between zero-length matches and unmatched groups" do
|
|
76
|
+
a = RE2::Regexp.new('()(a)?').match('b').to_a
|
|
77
|
+
|
|
78
|
+
expect(a).to eq(["", "", nil])
|
|
23
79
|
end
|
|
24
80
|
|
|
25
81
|
it "returns UTF-8 strings if the pattern is UTF-8" do
|
|
@@ -33,6 +89,10 @@ RSpec.describe RE2::MatchData do
|
|
|
33
89
|
|
|
34
90
|
expect(a.map(&:encoding)).to all eq(Encoding::ISO_8859_1)
|
|
35
91
|
end
|
|
92
|
+
|
|
93
|
+
it "raises an error when called on an uninitialized object" do
|
|
94
|
+
expect { described_class.allocate.to_a }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
95
|
+
end
|
|
36
96
|
end
|
|
37
97
|
|
|
38
98
|
describe "#[]" do
|
|
@@ -104,6 +164,12 @@ RSpec.describe RE2::MatchData do
|
|
|
104
164
|
expect(md[:numbers]).to eq("123")
|
|
105
165
|
end
|
|
106
166
|
|
|
167
|
+
it "returns an empty string for a zero-length capturing group" do
|
|
168
|
+
md = RE2::Regexp.new('()').match("bob")
|
|
169
|
+
|
|
170
|
+
expect(md[1]).to eq("")
|
|
171
|
+
end
|
|
172
|
+
|
|
107
173
|
it "returns nil if no such named group exists", :aggregate_failures do
|
|
108
174
|
md = RE2::Regexp.new('(\d+)').match("bob 123")
|
|
109
175
|
|
|
@@ -132,6 +198,17 @@ RSpec.describe RE2::MatchData do
|
|
|
132
198
|
expect(md["name"].encoding.name).to eq("ISO-8859-1")
|
|
133
199
|
expect(md[:name].encoding.name).to eq("ISO-8859-1")
|
|
134
200
|
end
|
|
201
|
+
|
|
202
|
+
it "supports GC compaction" do
|
|
203
|
+
md = RE2::Regexp.new('(wo{2})').match('woohoo' * 5)
|
|
204
|
+
GC.compact
|
|
205
|
+
|
|
206
|
+
expect(md[1]).to eq("woo")
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
it "raises an error when called on an uninitialized object" do
|
|
210
|
+
expect { described_class.allocate[0] }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
211
|
+
end
|
|
135
212
|
end
|
|
136
213
|
|
|
137
214
|
describe "#string" do
|
|
@@ -142,7 +219,7 @@ RSpec.describe RE2::MatchData do
|
|
|
142
219
|
end
|
|
143
220
|
|
|
144
221
|
it "returns a copy, not the actual original" do
|
|
145
|
-
string = "bob"
|
|
222
|
+
string = +"bob"
|
|
146
223
|
re = RE2::Regexp.new('(\D+)').match(string)
|
|
147
224
|
|
|
148
225
|
expect(re.string).to_not equal(string)
|
|
@@ -155,11 +232,15 @@ RSpec.describe RE2::MatchData do
|
|
|
155
232
|
end
|
|
156
233
|
|
|
157
234
|
it "does not copy the string if it was already frozen" do
|
|
158
|
-
string = "bob"
|
|
235
|
+
string = "bob"
|
|
159
236
|
re = RE2::Regexp.new('(\D+)').match(string)
|
|
160
237
|
|
|
161
238
|
expect(re.string).to equal(string)
|
|
162
239
|
end
|
|
240
|
+
|
|
241
|
+
it "raises an error when called on an uninitialized object" do
|
|
242
|
+
expect { described_class.allocate.string }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
243
|
+
end
|
|
163
244
|
end
|
|
164
245
|
|
|
165
246
|
describe "#size" do
|
|
@@ -168,6 +249,10 @@ RSpec.describe RE2::MatchData do
|
|
|
168
249
|
|
|
169
250
|
expect(md.size).to eq(3)
|
|
170
251
|
end
|
|
252
|
+
|
|
253
|
+
it "raises an error when called on an uninitialized object" do
|
|
254
|
+
expect { described_class.allocate.size }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
255
|
+
end
|
|
171
256
|
end
|
|
172
257
|
|
|
173
258
|
describe "#length" do
|
|
@@ -176,6 +261,10 @@ RSpec.describe RE2::MatchData do
|
|
|
176
261
|
|
|
177
262
|
expect(md.length).to eq(3)
|
|
178
263
|
end
|
|
264
|
+
|
|
265
|
+
it "raises an error when called on an uninitialized object" do
|
|
266
|
+
expect { described_class.allocate.length }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
267
|
+
end
|
|
179
268
|
end
|
|
180
269
|
|
|
181
270
|
describe "#regexp" do
|
|
@@ -185,6 +274,10 @@ RSpec.describe RE2::MatchData do
|
|
|
185
274
|
|
|
186
275
|
expect(md.regexp).to equal(re)
|
|
187
276
|
end
|
|
277
|
+
|
|
278
|
+
it "raises an error when called on an uninitialized object" do
|
|
279
|
+
expect { described_class.allocate.regexp }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
280
|
+
end
|
|
188
281
|
end
|
|
189
282
|
|
|
190
283
|
describe "#inspect" do
|
|
@@ -200,11 +293,21 @@ RSpec.describe RE2::MatchData do
|
|
|
200
293
|
expect(md.inspect).to eq('#<RE2::MatchData "1234 " 1:"1234" 2:nil>')
|
|
201
294
|
end
|
|
202
295
|
|
|
296
|
+
it "represents zero-length capturing groups as empty strings" do
|
|
297
|
+
md = RE2::Regexp.new('()').match("bob")
|
|
298
|
+
|
|
299
|
+
expect(md.inspect).to eq('#<RE2::MatchData "" 1:"">')
|
|
300
|
+
end
|
|
301
|
+
|
|
203
302
|
it "supports matches with null bytes" do
|
|
204
303
|
md = RE2::Regexp.new("(\\w\0\\w) (\\w\0\\w)").match("a\0b c\0d")
|
|
205
304
|
|
|
206
305
|
expect(md.inspect).to eq("#<RE2::MatchData \"a\0b c\0d\" 1:\"a\0b\" 2:\"c\0d\">")
|
|
207
306
|
end
|
|
307
|
+
|
|
308
|
+
it "raises an error when called on an uninitialized object" do
|
|
309
|
+
expect { described_class.allocate.inspect }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
310
|
+
end
|
|
208
311
|
end
|
|
209
312
|
|
|
210
313
|
describe "#to_s" do
|
|
@@ -213,6 +316,16 @@ RSpec.describe RE2::MatchData do
|
|
|
213
316
|
|
|
214
317
|
expect(md.to_s).to eq("23456")
|
|
215
318
|
end
|
|
319
|
+
|
|
320
|
+
it "returns an empty string for a zero-length match" do
|
|
321
|
+
md = RE2::Regexp.new('()').match("bob")
|
|
322
|
+
|
|
323
|
+
expect(md.to_s).to eq("")
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
it "raises an error when called on an uninitialized object" do
|
|
327
|
+
expect { described_class.allocate.to_s }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
328
|
+
end
|
|
216
329
|
end
|
|
217
330
|
|
|
218
331
|
describe "#to_ary" do
|
|
@@ -257,6 +370,12 @@ RSpec.describe RE2::MatchData do
|
|
|
257
370
|
expect(md.string[md.begin(0)..-1]).to eq('Ruby')
|
|
258
371
|
end
|
|
259
372
|
|
|
373
|
+
it "returns the offset for a zero-length capturing group" do
|
|
374
|
+
md = RE2::Regexp.new('()').match("bob")
|
|
375
|
+
|
|
376
|
+
expect(md.begin(1)).to eq(0)
|
|
377
|
+
end
|
|
378
|
+
|
|
260
379
|
it "returns nil for non-existent numerical matches" do
|
|
261
380
|
md = RE2::Regexp.new('(\d)').match('123')
|
|
262
381
|
|
|
@@ -286,6 +405,17 @@ RSpec.describe RE2::MatchData do
|
|
|
286
405
|
|
|
287
406
|
expect { md.begin(nil) }.to raise_error(TypeError)
|
|
288
407
|
end
|
|
408
|
+
|
|
409
|
+
it "supports GC compaction" do
|
|
410
|
+
md = RE2::Regexp.new('(wo{2})').match('woohoo' * 5)
|
|
411
|
+
GC.compact
|
|
412
|
+
|
|
413
|
+
expect(md.string[md.begin(0)..-1]).to eq('woohoo' * 5)
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
it "raises an error when called on an uninitialized object" do
|
|
417
|
+
expect { described_class.allocate.begin(0) }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
418
|
+
end
|
|
289
419
|
end
|
|
290
420
|
|
|
291
421
|
describe "#end" do
|
|
@@ -319,6 +449,12 @@ RSpec.describe RE2::MatchData do
|
|
|
319
449
|
expect(md.string[0...md.end(0)]).to eq('I ♥ Ruby')
|
|
320
450
|
end
|
|
321
451
|
|
|
452
|
+
it "returns the offset for a zero-length capturing group" do
|
|
453
|
+
md = RE2::Regexp.new('()').match("bob")
|
|
454
|
+
|
|
455
|
+
expect(md.end(1)).to eq(0)
|
|
456
|
+
end
|
|
457
|
+
|
|
322
458
|
it "returns nil for non-existent numerical matches" do
|
|
323
459
|
md = RE2::Regexp.new('(\d)').match('123')
|
|
324
460
|
|
|
@@ -348,6 +484,275 @@ RSpec.describe RE2::MatchData do
|
|
|
348
484
|
|
|
349
485
|
expect { md.end(nil) }.to raise_error(TypeError)
|
|
350
486
|
end
|
|
487
|
+
|
|
488
|
+
it "supports GC compaction" do
|
|
489
|
+
md = RE2::Regexp.new('(wo{2})').match('woohoo' * 5)
|
|
490
|
+
GC.compact
|
|
491
|
+
|
|
492
|
+
expect(md.string[0...md.end(0)]).to eq('woo')
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
it "raises an error when called on an uninitialized object" do
|
|
496
|
+
expect { described_class.allocate.end(0) }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
describe "#pre_match" do
|
|
501
|
+
it "returns the portion of the string before the match" do
|
|
502
|
+
md = RE2::Regexp.new('(\d+)').match("bob 123 456")
|
|
503
|
+
|
|
504
|
+
expect(md.pre_match).to eq("bob ")
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
it "returns an empty string when the match starts at the beginning" do
|
|
508
|
+
md = RE2::Regexp.new('(\w+)').match("bob 123")
|
|
509
|
+
|
|
510
|
+
expect(md.pre_match).to eq("")
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
it "supports multibyte characters" do
|
|
514
|
+
md = RE2::Regexp.new('(\d+)').match("I ♥ 123")
|
|
515
|
+
|
|
516
|
+
expect(md.pre_match).to eq("I ♥ ")
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
it "returns UTF-8 strings by default" do
|
|
520
|
+
md = RE2::Regexp.new('(\d+)').match("abc 123")
|
|
521
|
+
|
|
522
|
+
expect(md.pre_match.encoding).to eq(Encoding::UTF_8)
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
it "returns ISO-8859-1 strings if the pattern is not UTF-8" do
|
|
526
|
+
md = RE2::Regexp.new('(\d+)', utf8: false).match("abc 123")
|
|
527
|
+
|
|
528
|
+
expect(md.pre_match.encoding).to eq(Encoding::ISO_8859_1)
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
it "returns the text before a zero-length match" do
|
|
532
|
+
md = RE2::Regexp.new('()').match("bob")
|
|
533
|
+
|
|
534
|
+
expect(md.pre_match).to eq("")
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
it "raises an error when called on an uninitialized object" do
|
|
538
|
+
expect { described_class.allocate.pre_match }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
describe "#post_match" do
|
|
543
|
+
it "returns the portion of the string after the match" do
|
|
544
|
+
md = RE2::Regexp.new('(\d+)').match("bob 123 456")
|
|
545
|
+
|
|
546
|
+
expect(md.post_match).to eq(" 456")
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
it "returns an empty string when the match ends at the end" do
|
|
550
|
+
md = RE2::Regexp.new('(\d+)$').match("bob 123")
|
|
551
|
+
|
|
552
|
+
expect(md.post_match).to eq("")
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
it "supports multibyte characters" do
|
|
556
|
+
md = RE2::Regexp.new('(\d+)').match("123 ♥ world")
|
|
557
|
+
|
|
558
|
+
expect(md.post_match).to eq(" ♥ world")
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
it "returns UTF-8 strings by default" do
|
|
562
|
+
md = RE2::Regexp.new('(\d+)').match("abc 123 def")
|
|
563
|
+
|
|
564
|
+
expect(md.post_match.encoding).to eq(Encoding::UTF_8)
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
it "returns ISO-8859-1 strings if the pattern is not UTF-8" do
|
|
568
|
+
md = RE2::Regexp.new('(\d+)', utf8: false).match("abc 123 def")
|
|
569
|
+
|
|
570
|
+
expect(md.post_match.encoding).to eq(Encoding::ISO_8859_1)
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
it "returns the text after a zero-length match" do
|
|
574
|
+
md = RE2::Regexp.new('()').match("bob")
|
|
575
|
+
|
|
576
|
+
expect(md.post_match).to eq("bob")
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
it "raises an error when called on an uninitialized object" do
|
|
580
|
+
expect { described_class.allocate.post_match }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
581
|
+
end
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
describe "#offset" do
|
|
585
|
+
it "returns the offset of a match by index" do
|
|
586
|
+
md = RE2::Regexp.new('ob (\d+)').match("bob 123")
|
|
587
|
+
|
|
588
|
+
expect(md.offset(0)).to eq([1, 7])
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
it "returns the offset of a submatch by index" do
|
|
592
|
+
md = RE2::Regexp.new('ob (\d+)').match("bob 123")
|
|
593
|
+
|
|
594
|
+
expect(md.offset(1)).to eq([4, 7])
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
it "returns the offset of a match by string name" do
|
|
598
|
+
md = RE2::Regexp.new('(?P<number>\d+)').match("bob 123")
|
|
599
|
+
|
|
600
|
+
expect(md.offset("number")).to eq([4, 7])
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
it "returns the offset of a match by symbol name" do
|
|
604
|
+
md = RE2::Regexp.new('(?P<number>\d+)').match("bob 123")
|
|
605
|
+
|
|
606
|
+
expect(md.offset(:number)).to eq([4, 7])
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
it "returns character offsets despite multibyte characters" do
|
|
610
|
+
md = RE2::Regexp.new('(Ruby)').match("I ♥ Ruby")
|
|
611
|
+
|
|
612
|
+
expect(md.offset(0)).to eq([4, 8])
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
it "returns identical offsets for a zero-length capturing group" do
|
|
616
|
+
md = RE2::Regexp.new('()').match("bob")
|
|
617
|
+
|
|
618
|
+
expect(md.offset(1)).to eq([0, 0])
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
it "returns nil for non-existent numerical matches" do
|
|
622
|
+
md = RE2::Regexp.new('(\d)').match("123")
|
|
623
|
+
|
|
624
|
+
expect(md.offset(10)).to be_nil
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
it "returns nil for non-existent named matches" do
|
|
628
|
+
md = RE2::Regexp.new('(\d)').match("123")
|
|
629
|
+
|
|
630
|
+
expect(md.offset("foo")).to be_nil
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
it "raises a type error if given an invalid name or number" do
|
|
634
|
+
md = RE2::Regexp.new('(\d)').match("123")
|
|
635
|
+
|
|
636
|
+
expect { md.offset(nil) }.to raise_error(TypeError)
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
it "raises an error when called on an uninitialized object" do
|
|
640
|
+
expect { described_class.allocate.offset(0) }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
641
|
+
end
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
describe "#match_length" do
|
|
645
|
+
it "returns the length of the overall match" do
|
|
646
|
+
md = RE2::Regexp.new('ob (\d+)').match("bob 123")
|
|
647
|
+
|
|
648
|
+
expect(md.match_length(0)).to eq(6)
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
it "returns the length of a submatch by index" do
|
|
652
|
+
md = RE2::Regexp.new('ob (\d+)').match("bob 123")
|
|
653
|
+
|
|
654
|
+
expect(md.match_length(1)).to eq(3)
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
it "returns the length of a match by string name" do
|
|
658
|
+
md = RE2::Regexp.new('(?P<number>\d+)').match("bob 123")
|
|
659
|
+
|
|
660
|
+
expect(md.match_length("number")).to eq(3)
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
it "returns the length of a match by symbol name" do
|
|
664
|
+
md = RE2::Regexp.new('(?P<number>\d+)').match("bob 123")
|
|
665
|
+
|
|
666
|
+
expect(md.match_length(:number)).to eq(3)
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
it "returns character length despite multibyte characters" do
|
|
670
|
+
md = RE2::Regexp.new('(♥ Ruby)').match("I ♥ Ruby!")
|
|
671
|
+
|
|
672
|
+
expect(md.match_length(0)).to eq(6)
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
it "returns zero for a zero-length capturing group" do
|
|
676
|
+
md = RE2::Regexp.new('()').match("bob")
|
|
677
|
+
|
|
678
|
+
expect(md.match_length(1)).to eq(0)
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
it "returns nil for non-existent numerical matches" do
|
|
682
|
+
md = RE2::Regexp.new('(\d)').match("123")
|
|
683
|
+
|
|
684
|
+
expect(md.match_length(10)).to be_nil
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
it "returns nil for non-existent named matches" do
|
|
688
|
+
md = RE2::Regexp.new('(\d)').match("123")
|
|
689
|
+
|
|
690
|
+
expect(md.match_length("foo")).to be_nil
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
it "raises a type error if given an invalid name or number" do
|
|
694
|
+
md = RE2::Regexp.new('(\d)').match("123")
|
|
695
|
+
|
|
696
|
+
expect { md.match_length(nil) }.to raise_error(TypeError)
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
it "raises an error when called on an uninitialized object" do
|
|
700
|
+
expect { described_class.allocate.match_length(0) }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
701
|
+
end
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
describe "#values_at" do
|
|
705
|
+
it "returns match values at the given indices" do
|
|
706
|
+
md = RE2::Regexp.new('(\d+) (\d+) (\d+)').match("123 456 789")
|
|
707
|
+
|
|
708
|
+
expect(md.values_at(1, 3)).to eq(["123", "789"])
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
it "returns match values by named groups" do
|
|
712
|
+
md = RE2::Regexp.new('(?P<a>\d+) (?P<b>\d+)').match("123 456")
|
|
713
|
+
|
|
714
|
+
expect(md.values_at(:a, :b)).to eq(["123", "456"])
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
it "supports a mix of indices and names" do
|
|
718
|
+
md = RE2::Regexp.new('(?P<a>\d+) (\d+)').match("123 456")
|
|
719
|
+
|
|
720
|
+
expect(md.values_at(2, :a)).to eq(["456", "123"])
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
it "returns nil for non-existent indices" do
|
|
724
|
+
md = RE2::Regexp.new('(\d+)').match("123")
|
|
725
|
+
|
|
726
|
+
expect(md.values_at(1, 5)).to eq(["123", nil])
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
it "returns nil for non-existent names" do
|
|
730
|
+
md = RE2::Regexp.new('(?P<a>\d+)').match("123")
|
|
731
|
+
|
|
732
|
+
expect(md.values_at(:a, :z)).to eq(["123", nil])
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
it "returns an empty string for a zero-length capturing group" do
|
|
736
|
+
md = RE2::Regexp.new('()(b)').match("bob")
|
|
737
|
+
|
|
738
|
+
expect(md.values_at(1, 2)).to eq(["", "b"])
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
it "returns the full match when given index 0" do
|
|
742
|
+
md = RE2::Regexp.new('(\d+) (\d+)').match("123 456")
|
|
743
|
+
|
|
744
|
+
expect(md.values_at(0, 1)).to eq(["123 456", "123"])
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
it "returns an empty array if given no arguments" do
|
|
748
|
+
md = RE2::Regexp.new('(\d+) (\d+)').match("123 456")
|
|
749
|
+
|
|
750
|
+
expect(md.values_at).to be_empty
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
it "raises an error when called on an uninitialized object" do
|
|
754
|
+
expect { described_class.allocate.values_at(1) }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
755
|
+
end
|
|
351
756
|
end
|
|
352
757
|
|
|
353
758
|
describe "#deconstruct" do
|
|
@@ -362,6 +767,106 @@ RSpec.describe RE2::MatchData do
|
|
|
362
767
|
|
|
363
768
|
expect(md.deconstruct).to eq(['o', 'o', nil])
|
|
364
769
|
end
|
|
770
|
+
|
|
771
|
+
it "includes zero-length capturing groups as empty strings" do
|
|
772
|
+
md = RE2::Regexp.new('()').match("bob")
|
|
773
|
+
|
|
774
|
+
expect(md.deconstruct).to eq([""])
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
it "raises an error when called on an uninitialized object" do
|
|
778
|
+
expect { described_class.allocate.deconstruct }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
779
|
+
end
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
describe "#captures" do
|
|
783
|
+
it "returns all capturing groups" do
|
|
784
|
+
md = RE2::Regexp.new('w(o)(o)').match('woo')
|
|
785
|
+
|
|
786
|
+
expect(md.captures).to eq(['o', 'o'])
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
it "includes optional capturing groups as nil" do
|
|
790
|
+
md = RE2::Regexp.new('w(.)(.)(.)?').match('woo')
|
|
791
|
+
|
|
792
|
+
expect(md.captures).to eq(['o', 'o', nil])
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
it "raises an error when called on an uninitialized object" do
|
|
796
|
+
expect { described_class.allocate.captures }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
797
|
+
end
|
|
798
|
+
end
|
|
799
|
+
|
|
800
|
+
describe "#named_captures" do
|
|
801
|
+
it "returns a hash of capturing group names to matched strings" do
|
|
802
|
+
md = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').match('123 abc')
|
|
803
|
+
|
|
804
|
+
expect(md.named_captures).to eq("numbers" => "123", "letters" => "abc")
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
it "returns an empty hash if there are no named capturing groups" do
|
|
808
|
+
md = RE2::Regexp.new('(\d+)').match('123')
|
|
809
|
+
|
|
810
|
+
expect(md.named_captures).to be_empty
|
|
811
|
+
end
|
|
812
|
+
|
|
813
|
+
it "returns unmatched optional groups as nil" do
|
|
814
|
+
md = RE2::Regexp.new('(?P<a>\d+) (?P<b>\w+)?').match('123 ')
|
|
815
|
+
|
|
816
|
+
expect(md.named_captures).to eq("a" => "123", "b" => nil)
|
|
817
|
+
end
|
|
818
|
+
|
|
819
|
+
it "returns an empty string for a zero-length named capturing group" do
|
|
820
|
+
md = RE2::Regexp.new('(?P<empty>)(?P<word>\w+)').match("bob")
|
|
821
|
+
|
|
822
|
+
expect(md.named_captures).to eq("empty" => "", "word" => "bob")
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
it "returns symbol keys when symbolize_names: true" do
|
|
826
|
+
md = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').match('123 abc')
|
|
827
|
+
|
|
828
|
+
expect(md.named_captures(symbolize_names: true)).to eq(numbers: "123", letters: "abc")
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
it "returns string keys when symbolize_names: false" do
|
|
832
|
+
md = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').match('123 abc')
|
|
833
|
+
|
|
834
|
+
expect(md.named_captures(symbolize_names: false)).to eq("numbers" => "123", "letters" => "abc")
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
it "raises an error when called on an uninitialized object" do
|
|
838
|
+
expect { described_class.allocate.named_captures }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
839
|
+
end
|
|
840
|
+
end
|
|
841
|
+
|
|
842
|
+
describe "#names" do
|
|
843
|
+
it "returns an array of names of named capturing groups" do
|
|
844
|
+
md = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').match('123 abc')
|
|
845
|
+
|
|
846
|
+
expect(md.names).to eq(["letters", "numbers"])
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
it "returns an empty array if there are no named capturing groups" do
|
|
850
|
+
md = RE2::Regexp.new('(\d+)').match('123')
|
|
851
|
+
|
|
852
|
+
expect(md.names).to be_empty
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
it "returns UTF-8 strings if the pattern is UTF-8" do
|
|
856
|
+
md = RE2::Regexp.new('(?P<numbers>\d+)').match('123')
|
|
857
|
+
|
|
858
|
+
expect(md.names.first.encoding).to eq(Encoding::UTF_8)
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
it "returns ISO-8859-1 strings if the pattern is not UTF-8" do
|
|
862
|
+
md = RE2::Regexp.new('(?P<numbers>\d+)', utf8: false).match('123')
|
|
863
|
+
|
|
864
|
+
expect(md.names.first.encoding).to eq(Encoding::ISO_8859_1)
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
it "raises an error when called on an uninitialized object" do
|
|
868
|
+
expect { described_class.allocate.names }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
869
|
+
end
|
|
365
870
|
end
|
|
366
871
|
|
|
367
872
|
describe "#deconstruct_keys" do
|
|
@@ -395,6 +900,12 @@ RSpec.describe RE2::MatchData do
|
|
|
395
900
|
expect(md.deconstruct_keys(nil)).to eq({})
|
|
396
901
|
end
|
|
397
902
|
|
|
903
|
+
it "returns an empty string for a zero-length named capturing group" do
|
|
904
|
+
md = RE2::Regexp.new('(?P<empty>)(?P<word>\w+)').match("bob")
|
|
905
|
+
|
|
906
|
+
expect(md.deconstruct_keys(nil)).to eq(empty: "", word: "bob")
|
|
907
|
+
end
|
|
908
|
+
|
|
398
909
|
it "raises an error if given a non-array of keys" do
|
|
399
910
|
md = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').match('123 abc')
|
|
400
911
|
|
|
@@ -406,5 +917,9 @@ RSpec.describe RE2::MatchData do
|
|
|
406
917
|
|
|
407
918
|
expect { md.deconstruct_keys([0]) }.to raise_error(TypeError)
|
|
408
919
|
end
|
|
920
|
+
|
|
921
|
+
it "raises an error when called on an uninitialized object" do
|
|
922
|
+
expect { described_class.allocate.deconstruct_keys(nil) }.to raise_error(TypeError, /uninitialized RE2::MatchData/)
|
|
923
|
+
end
|
|
409
924
|
end
|
|
410
925
|
end
|