re2 0.7.0 → 1.0.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: 32a2347d40362a3014dfd03f948405964468a3c2
4
- data.tar.gz: ea15968dae15568249200c6ba91bad66d0e28f6f
3
+ metadata.gz: 118d0c677843bd7166804670d548504e6a30410b
4
+ data.tar.gz: 9efe563b54262ad28c5b71c2317b3f18117e2706
5
5
  SHA512:
6
- metadata.gz: 00588950e6c82fb5720043c04d0f833d819f3107a3c953f2284d9eaede74210c0c803f34b3e9fd89413c886fae1e4b853b5a85b18b3c2d788ff0a7e8b46036cf
7
- data.tar.gz: af2d23503d089a15ea6dd32e00bc80622cd7b0b1773ed471edcc982ac6791a6d1bf0bf5332d2440446796b5a4da15a169fd7396e3cf729af8a300e52a5985bad
6
+ metadata.gz: 07d895cafa392c75483586a37b44f57b5d5979b7473529c52eff05f3c6bccca3b11f8d406363ece9f3ab2ad02df4952031dcdb8104cdc275484666a0ceb7db01
7
+ data.tar.gz: 8147a0194f92a7850285339d9d81fe74b4865d12f67a1e64fa9db3eeec35c3adaaf4cfd97862f1d954b7b475b4dc4b0d01ef28dd10c3f39cba3338caac6ff51a
data/README.md CHANGED
@@ -4,8 +4,8 @@ re2 [![Build Status](https://travis-ci.org/mudge/re2.svg?branch=master)](http://
4
4
  A Ruby binding to [re2][], an "efficient, principled regular expression
5
5
  library".
6
6
 
7
- **Current version:** 0.7.0
8
- **Supported Ruby versions:** 1.8.7, 1.9.2, 1.9.3, 2.0.0, 2.1.0, Rubinius 2.2
7
+ **Current version:** 1.0.0
8
+ **Supported Ruby versions:** 1.8.7, 1.9.2, 1.9.3, 2.0.0, 2.1.0, 2.2, 2.3, Rubinius 2.2
9
9
 
10
10
  Installation
11
11
  ------------
@@ -21,6 +21,8 @@ If you are using Debian, you can install the [libre2-dev][] package like so:
21
21
 
22
22
  $ sudo apt-get install libre2-dev
23
23
 
24
+ Recent versions of re2 require a compiler with C++11 support such as [clang](http://clang.llvm.org/) 3.4 or [gcc](https://gcc.gnu.org/) 4.8.
25
+
24
26
  If you are using a packaged Ruby distribution, make sure you also have the
25
27
  Ruby header files installed such as those provided by the [ruby-dev][] package
26
28
  on Debian and Ubuntu.
@@ -35,14 +37,17 @@ Documentation
35
37
  Full documentation automatically generated from the latest version is
36
38
  available at <http://mudge.name/re2/>.
37
39
 
38
- Bear in mind that re2's regular expression syntax differs from PCRE, see the
39
- [official syntax page][] for more details.
40
+ Note that re2's regular expression syntax differs from PCRE and Ruby's
41
+ built-in [`Regexp`][Regexp] library, see the [official syntax page][] for more
42
+ details.
40
43
 
41
44
  Usage
42
45
  -----
43
46
 
44
- You can use re2 as a mostly drop-in replacement for Ruby's own [Regexp][] and
45
- [MatchData][] classes:
47
+ While re2 uses the same naming scheme as Ruby's built-in regular expression
48
+ library (with [`Regexp`](http://mudge.name/re2/RE2/Regexp.html) and
49
+ [`MatchData`](http://mudge.name/re2/RE2/MatchData.html)), its API is slightly
50
+ different:
46
51
 
47
52
  ```console
48
53
  $ irb -rubygems
@@ -67,9 +72,11 @@ $ irb -rubygems
67
72
  => nil
68
73
  ```
69
74
 
70
- As `RE2::Regexp.new` (or `RE2::Regexp.compile`) can be quite verbose, a helper
71
- method has been defined against `Kernel` so you can use a shorter version to
72
- create regular expressions:
75
+ As
76
+ [`RE2::Regexp.new`](http://mudge.name/re2/RE2/Regexp.html#initialize-instance_method)
77
+ (or `RE2::Regexp.compile`) can be quite verbose, a helper method has been
78
+ defined against `Kernel` so you can use a shorter version to create regular
79
+ expressions:
73
80
 
74
81
  ```console
75
82
  > RE2('(\d+)')
@@ -123,7 +130,7 @@ Features
123
130
  --------
124
131
 
125
132
  * Pre-compiling regular expressions with
126
- [`RE2::Regexp.new(re)`](http://code.google.com/p/re2/source/browse/re2/re2.h#96),
133
+ [`RE2::Regexp.new(re)`](https://github.com/google/re2/blob/2016-02-01/re2/re2.h#L100),
127
134
  `RE2::Regexp.compile(re)` or `RE2(re)` (including specifying options, e.g.
128
135
  `RE2::Regexp.new("pattern", :case_sensitive => false)`
129
136
 
@@ -152,21 +159,22 @@ Features
152
159
  `pattern.replace_all(replacement, original)`
153
160
 
154
161
  * Escaping regular expressions with
155
- [`RE2.escape(unquoted)`](http://code.google.com/p/re2/source/browse/re2/re2.h#377) and
162
+ [`RE2.escape(unquoted)`](https://github.com/google/re2/blob/2016-02-01/re2/re2.h#L418) and
156
163
  `RE2.quote(unquoted)`
157
164
 
158
165
  Contributions
159
166
  -------------
160
167
 
161
- Thanks to [Jason Woods](https://github.com/driskell) who contributed the
162
- original implementations of `RE2::MatchData#begin` and `RE2::MatchData#end`.
168
+ * Thanks to [Jason Woods](https://github.com/driskell) who contributed the
169
+ original implementations of `RE2::MatchData#begin` and `RE2::MatchData#end`;
170
+ * Thanks to [Stefano Rivera](https://github.com/stefanor) who first contributed C++11 support.
163
171
 
164
172
  Contact
165
173
  -------
166
174
 
167
175
  All feedback should go to the mailing list: <mailto:ruby.re2@librelist.com>
168
176
 
169
- [re2]: http://code.google.com/p/re2/
177
+ [re2]: https://github.com/google/re2
170
178
  [gcc]: http://gcc.gnu.org/
171
179
  [ruby-dev]: http://packages.debian.org/ruby-dev
172
180
  [build-essential]: http://packages.debian.org/build-essential
@@ -174,5 +182,5 @@ All feedback should go to the mailing list: <mailto:ruby.re2@librelist.com>
174
182
  [MatchData]: http://ruby-doc.org/core/classes/MatchData.html
175
183
  [Homebrew]: http://mxcl.github.com/homebrew
176
184
  [libre2-dev]: http://packages.debian.org/search?keywords=libre2-dev
177
- [official syntax page]: http://code.google.com/p/re2/wiki/Syntax
185
+ [official syntax page]: https://github.com/google/re2/wiki/Syntax
178
186
 
data/Rakefile CHANGED
@@ -1,15 +1,10 @@
1
1
  require 'rake/extensiontask'
2
- require 'rake/testtask'
2
+ require 'rspec/core/rake_task'
3
3
 
4
4
  Rake::ExtensionTask.new('re2')
5
5
 
6
- Rake::TestTask.new do |t|
7
- t.libs << "spec"
8
- t.test_files = FileList["spec/**/*_spec.rb"]
9
- t.verbose = true
10
- end
6
+ RSpec::Core::RakeTask.new(:spec)
11
7
 
12
- task :test => :compile
13
- task :spec => :test
14
- task :default => :test
8
+ task :spec => :compile
9
+ task :default => :spec
15
10
 
@@ -10,20 +10,46 @@ incl, lib = dir_config("re2", "/usr/local/include", "/usr/local/lib")
10
10
 
11
11
  $CFLAGS << " -Wall -Wextra -funroll-loops"
12
12
 
13
+ # Pass -x c++ to force gcc to compile the test program
14
+ # as C++ (as it will end in .c by default).
15
+ compile_options = "-x c++"
16
+
13
17
  have_library("stdc++")
14
18
  have_header("stdint.h")
15
19
  have_func("rb_str_sublen")
16
20
 
17
- if have_library("re2")
21
+ unless have_library("re2")
22
+ abort "You must have re2 installed and specified with --with-re2-dir, please see https://github.com/google/re2/wiki/Install"
23
+ end
18
24
 
19
- # Determine which version of re2 the user has installed.
20
- # Revision d9f8806c004d added an `endpos` argument to the
21
- # generic Match() function.
22
- #
23
- # To test for this, try to compile a simple program that uses
24
- # the newer form of Match() and set a flag if it is successful.
25
- checking_for("RE2::Match() with endpos argument") do
26
- test_re2_match_signature = <<SRC
25
+ # Recent versions of re2 now require a compiler with C++11 support
26
+ checking_for("re2 requires C++11 compiler") do
27
+ minimal_program = <<SRC
28
+ #include <re2/re2.h>
29
+ int main() { return 0; }
30
+ SRC
31
+
32
+ unless try_compile(minimal_program, compile_options)
33
+ if try_compile(minimal_program, compile_options + " -std=c++11")
34
+ compile_options << " -std=c++11"
35
+ $CPPFLAGS << " -std=c++11"
36
+ elsif try_compile(minimal_program, compile_options + " -std=c++0x")
37
+ compile_options << " -std=c++0x"
38
+ $CPPFLAGS << " -std=c++0x"
39
+ else
40
+ abort "Cannot compile re2 with your compiler: recent versions require C++11 support."
41
+ end
42
+ end
43
+ end
44
+
45
+ # Determine which version of re2 the user has installed.
46
+ # Revision d9f8806c004d added an `endpos` argument to the
47
+ # generic Match() function.
48
+ #
49
+ # To test for this, try to compile a simple program that uses
50
+ # the newer form of Match() and set a flag if it is successful.
51
+ checking_for("RE2::Match() with endpos argument") do
52
+ test_re2_match_signature = <<SRC
27
53
  #include <re2/re2.h>
28
54
 
29
55
  int main() {
@@ -35,14 +61,9 @@ int main() {
35
61
  }
36
62
  SRC
37
63
 
38
- # Pass -x c++ to force gcc to compile the test program
39
- # as C++ (as it will end in .c by default).
40
- if try_compile(test_re2_match_signature, "-x c++")
41
- $defs.push("-DHAVE_ENDPOS_ARGUMENT")
42
- end
64
+ if try_compile(test_re2_match_signature, compile_options)
65
+ $defs.push("-DHAVE_ENDPOS_ARGUMENT")
43
66
  end
44
-
45
- create_makefile("re2")
46
- else
47
- abort "You must have re2 installed and specified with --with-re2-dir, please see http://code.google.com/p/re2/wiki/Install"
48
67
  end
68
+
69
+ create_makefile("re2")
@@ -6,8 +6,8 @@
6
6
  * Released under the BSD Licence, please see LICENSE.txt
7
7
  */
8
8
 
9
- #include <re2/re2.h>
10
9
  #include <ruby.h>
10
+ #include <re2/re2.h>
11
11
  #include <stdint.h>
12
12
  #include <string>
13
13
  #include <sstream>
@@ -1,15 +1,13 @@
1
- require "spec_helper"
2
-
3
- describe Kernel do
1
+ RSpec.describe Kernel do
4
2
  describe "#RE2" do
5
3
  it "returns an RE2::Regexp instance given a pattern" do
6
- RE2('w(o)(o)').must_be_instance_of(RE2::Regexp)
4
+ expect(RE2('w(o)(o)')).to be_a(RE2::Regexp)
7
5
  end
8
6
 
9
7
  it "returns an RE2::Regexp instance given a pattern and options" do
10
8
  re = RE2('w(o)(o)', :case_sensitive => false)
11
- re.must_be_instance_of(RE2::Regexp)
12
- re.wont_be(:case_sensitive?)
9
+ expect(re).to be_a(RE2::Regexp)
10
+ expect(re).to_not be_case_sensitive
13
11
  end
14
12
  end
15
13
  end
@@ -1,94 +1,90 @@
1
1
  # encoding: utf-8
2
-
3
- require "spec_helper"
4
-
5
- describe RE2::MatchData do
6
-
2
+ RSpec.describe RE2::MatchData do
7
3
  describe "#to_a" do
8
4
  it "is populated with the match and capturing groups" do
9
5
  a = RE2::Regexp.new('w(o)(o)').match('woo').to_a
10
- a.must_equal(["woo", "o", "o"])
6
+ expect(a).to eq(["woo", "o", "o"])
11
7
  end
12
8
 
13
9
  it "populates optional capturing groups with nil if they are missing" do
14
10
  a = RE2::Regexp.new('(\d?)(a)(b)').match('ab').to_a
15
- a.must_equal(["ab", nil, "a", "b"])
11
+ expect(a).to eq(["ab", nil, "a", "b"])
16
12
  end
17
13
  end
18
14
 
19
15
  describe "#[]" do
20
16
  it "accesses capturing groups by numerical index" do
21
17
  md = RE2::Regexp.new('(\d)(\d{2})').match("123")
22
- md[1].must_equal("1")
23
- md[2].must_equal("23")
18
+ expect(md[1]).to eq("1")
19
+ expect(md[2]).to eq("23")
24
20
  end
25
21
 
26
22
  it "has the whole match as the 0th item" do
27
23
  md = RE2::Regexp.new('(\d)(\d{2})').match("123")
28
- md[0].must_equal("123")
24
+ expect(md[0]).to eq("123")
29
25
  end
30
26
 
31
27
  it "supports access by numerical ranges" do
32
28
  md = RE2::Regexp.new('(\d+) (\d+) (\d+)').match("123 456 789")
33
- md[1..3].must_equal(["123", "456", "789"])
34
- md[1...3].must_equal(["123", "456"])
29
+ expect(md[1..3]).to eq(["123", "456", "789"])
30
+ expect(md[1...3]).to eq(["123", "456"])
35
31
  end
36
32
 
37
33
  it "supports slicing" do
38
34
  md = RE2::Regexp.new('(\d+) (\d+) (\d+)').match("123 456 789")
39
- md[1, 3].must_equal(["123", "456", "789"])
40
- md[1, 2].must_equal(["123", "456"])
35
+ expect(md[1, 3]).to eq(["123", "456", "789"])
36
+ expect(md[1, 2]).to eq(["123", "456"])
41
37
  end
42
38
 
43
39
  it "returns nil if attempting to access non-existent capturing groups by index" do
44
40
  md = RE2::Regexp.new('(\d+)').match('bob 123')
45
- md[2].must_be_nil
46
- md[3].must_be_nil
41
+ expect(md[2]).to be_nil
42
+ expect(md[3]).to be_nil
47
43
  end
48
44
 
49
45
  it "allows access by string names when there are named groups" do
50
46
  md = RE2::Regexp.new('(?P<numbers>\d+)').match('bob 123')
51
- md["numbers"].must_equal("123")
47
+ expect(md["numbers"]).to eq("123")
52
48
  end
53
49
 
54
50
  it "allows access by symbol names when there are named groups" do
55
51
  md = RE2::Regexp.new('(?P<numbers>\d+)').match('bob 123')
56
- md[:numbers].must_equal("123")
52
+ expect(md[:numbers]).to eq("123")
57
53
  end
58
54
 
59
55
  it "allows access by names and indices with mixed groups" do
60
56
  md = RE2::Regexp.new('(?P<name>\w+)(\s*)(?P<numbers>\d+)').match("bob 123")
61
- md["name"].must_equal("bob")
62
- md[:name].must_equal("bob")
63
- md[2].must_equal(" ")
64
- md["numbers"].must_equal("123")
65
- md[:numbers].must_equal("123")
57
+ expect(md["name"]).to eq("bob")
58
+ expect(md[:name]).to eq("bob")
59
+ expect(md[2]).to eq(" ")
60
+ expect(md["numbers"]).to eq("123")
61
+ expect(md[:numbers]).to eq("123")
66
62
  end
67
63
 
68
64
  it "returns nil if no such named group exists" do
69
65
  md = RE2::Regexp.new('(\d+)').match("bob 123")
70
- md["missing"].must_be_nil
71
- md[:missing].must_be_nil
66
+ expect(md["missing"]).to be_nil
67
+ expect(md[:missing]).to be_nil
72
68
  end
73
69
 
74
70
  it "raises an error if given an inappropriate index" do
75
71
  md = RE2::Regexp.new('(\d+)').match("bob 123")
76
- lambda { md[nil] }.must_raise(TypeError)
72
+ expect { md[nil] }.to raise_error(TypeError)
77
73
  end
78
74
 
79
75
  if String.method_defined?(:encoding)
80
76
  it "returns UTF-8 encoded strings by default" do
81
77
  md = RE2::Regexp.new('(?P<name>\S+)').match("bob")
82
- md[0].encoding.name.must_equal("UTF-8")
83
- md["name"].encoding.name.must_equal("UTF-8")
84
- md[:name].encoding.name.must_equal("UTF-8")
78
+ expect(md[0].encoding.name).to eq("UTF-8")
79
+ expect(md["name"].encoding.name).to eq("UTF-8")
80
+ expect(md[:name].encoding.name).to eq("UTF-8")
85
81
  end
86
82
 
87
83
  it "returns Latin 1 strings encoding when utf-8 is false" do
88
84
  md = RE2::Regexp.new('(?P<name>\S+)', :utf8 => false).match('bob')
89
- md[0].encoding.name.must_equal("ISO-8859-1")
90
- md["name"].encoding.name.must_equal("ISO-8859-1")
91
- md[:name].encoding.name.must_equal("ISO-8859-1")
85
+ expect(md[0].encoding.name).to eq("ISO-8859-1")
86
+ expect(md["name"].encoding.name).to eq("ISO-8859-1")
87
+ expect(md[:name].encoding.name).to eq("ISO-8859-1")
92
88
  end
93
89
  end
94
90
  end
@@ -96,32 +92,32 @@ describe RE2::MatchData do
96
92
  describe "#string" do
97
93
  it "returns the original string to match against" do
98
94
  re = RE2::Regexp.new('(\D+)').match("bob")
99
- re.string.must_equal("bob")
95
+ expect(re.string).to eq("bob")
100
96
  end
101
97
 
102
98
  it "returns a copy, not the actual original" do
103
99
  string = "bob"
104
100
  re = RE2::Regexp.new('(\D+)').match(string)
105
- re.string.wont_be_same_as(string)
101
+ expect(re.string).to_not equal(string)
106
102
  end
107
103
 
108
104
  it "returns a frozen string" do
109
105
  re = RE2::Regexp.new('(\D+)').match("bob")
110
- re.string.must_be(:frozen?)
106
+ expect(re.string).to be_frozen
111
107
  end
112
108
  end
113
109
 
114
110
  describe "#size" do
115
111
  it "returns the number of capturing groups plus the matching string" do
116
112
  md = RE2::Regexp.new('(\d+) (\d+)').match("1234 56")
117
- md.size.must_equal(3)
113
+ expect(md.size).to eq(3)
118
114
  end
119
115
  end
120
116
 
121
117
  describe "#length" do
122
118
  it "returns the number of capturing groups plus the matching string" do
123
119
  md = RE2::Regexp.new('(\d+) (\d+)').match("1234 56")
124
- md.length.must_equal(3)
120
+ expect(md.length).to eq(3)
125
121
  end
126
122
  end
127
123
 
@@ -129,26 +125,26 @@ describe RE2::MatchData do
129
125
  it "returns the original RE2::Regexp used" do
130
126
  re = RE2::Regexp.new('(\d+)')
131
127
  md = re.match("123")
132
- md.regexp.must_be_same_as(re)
128
+ expect(md.regexp).to equal(re)
133
129
  end
134
130
  end
135
131
 
136
132
  describe "#inspect" do
137
133
  it "returns a text representation of the object and indices" do
138
134
  md = RE2::Regexp.new('(\d+) (\d+)').match("1234 56")
139
- md.inspect.must_equal('#<RE2::MatchData "1234 56" 1:"1234" 2:"56">')
135
+ expect(md.inspect).to eq('#<RE2::MatchData "1234 56" 1:"1234" 2:"56">')
140
136
  end
141
137
 
142
138
  it "represents missing matches as nil" do
143
139
  md = RE2::Regexp.new('(\d+) (\d+)?').match("1234 ")
144
- md.inspect.must_equal('#<RE2::MatchData "1234 " 1:"1234" 2:nil>')
140
+ expect(md.inspect).to eq('#<RE2::MatchData "1234 " 1:"1234" 2:nil>')
145
141
  end
146
142
  end
147
143
 
148
144
  describe "#to_s" do
149
145
  it "returns the matching part of the original string" do
150
146
  md = RE2::Regexp.new('(\d{2,5})').match("one two 23456")
151
- md.to_s.must_equal("23456")
147
+ expect(md.to_s).to eq("23456")
152
148
  end
153
149
  end
154
150
 
@@ -156,53 +152,93 @@ describe RE2::MatchData do
156
152
  it "allows the object to be expanded with an asterisk" do
157
153
  md = RE2::Regexp.new('(\d+) (\d+)').match("1234 56")
158
154
  m1, m2, m3 = *md
159
- m1.must_equal("1234 56")
160
- m2.must_equal("1234")
161
- m3.must_equal("56")
155
+ expect(m1).to eq("1234 56")
156
+ expect(m2).to eq("1234")
157
+ expect(m3).to eq("56")
162
158
  end
163
159
  end
164
160
 
165
161
  describe "#begin" do
166
162
  it "returns the offset of the start of a match by index" do
167
163
  md = RE2::Regexp.new('(wo{2})').match('a woohoo')
168
- md.string[md.begin(0)..-1].must_equal('woohoo')
164
+ expect(md.string[md.begin(0)..-1]).to eq('woohoo')
169
165
  end
170
166
 
171
167
  it "returns the offset of the start of a match by string name" do
172
168
  md = RE2::Regexp.new('(?P<foo>fo{2})').match('a foobar')
173
- md.string[md.begin('foo')..-1].must_equal('foobar')
169
+ expect(md.string[md.begin('foo')..-1]).to eq('foobar')
174
170
  end
175
171
 
176
172
  it "returns the offset of the start of a match by symbol name" do
177
173
  md = RE2::Regexp.new('(?P<foo>fo{2})').match('a foobar')
178
- md.string[md.begin(:foo)..-1].must_equal('foobar')
174
+ expect(md.string[md.begin(:foo)..-1]).to eq('foobar')
179
175
  end
180
176
 
181
177
  it "returns the offset despite multibyte characters" do
182
178
  md = RE2::Regexp.new('(Ruby)').match('I ♥ Ruby')
183
- md.string[md.begin(0)..-1].must_equal('Ruby')
179
+ expect(md.string[md.begin(0)..-1]).to eq('Ruby')
180
+ end
181
+
182
+ it "returns nil for non-existent numerical matches" do
183
+ md = RE2::Regexp.new('(\d)').match('123')
184
+ expect(md.begin(10)).to be_nil
185
+ end
186
+
187
+ it "returns nil for negative numerical matches" do
188
+ md = RE2::Regexp.new('(\d)').match('123')
189
+ expect(md.begin(-4)).to be_nil
190
+ end
191
+
192
+ it "returns nil for non-existent named matches" do
193
+ md = RE2::Regexp.new('(\d)').match('123')
194
+ expect(md.begin('foo')).to be_nil
195
+ end
196
+
197
+ it "returns nil for non-existent symbol named matches" do
198
+ md = RE2::Regexp.new('(\d)').match('123')
199
+ expect(md.begin(:foo)).to be_nil
184
200
  end
185
201
  end
186
202
 
187
203
  describe "#end" do
188
204
  it "returns the offset of the character following the end of a match" do
189
205
  md = RE2::Regexp.new('(wo{2})').match('a woohoo')
190
- md.string[0...md.end(0)].must_equal('a woo')
206
+ expect(md.string[0...md.end(0)]).to eq('a woo')
191
207
  end
192
208
 
193
209
  it "returns the offset of a match by string name" do
194
210
  md = RE2::Regexp.new('(?P<foo>fo{2})').match('a foobar')
195
- md.string[0...md.end('foo')].must_equal('a foo')
211
+ expect(md.string[0...md.end('foo')]).to eq('a foo')
196
212
  end
197
213
 
198
214
  it "returns the offset of a match by symbol name" do
199
215
  md = RE2::Regexp.new('(?P<foo>fo{2})').match('a foobar')
200
- md.string[0...md.end(:foo)].must_equal('a foo')
216
+ expect(md.string[0...md.end(:foo)]).to eq('a foo')
201
217
  end
202
218
 
203
219
  it "returns the offset despite multibyte characters" do
204
220
  md = RE2::Regexp.new('(Ruby)').match('I ♥ Ruby')
205
- md.string[0...md.end(0)].must_equal('I ♥ Ruby')
221
+ expect(md.string[0...md.end(0)]).to eq('I ♥ Ruby')
222
+ end
223
+
224
+ it "returns nil for non-existent numerical matches" do
225
+ md = RE2::Regexp.new('(\d)').match('123')
226
+ expect(md.end(10)).to be_nil
227
+ end
228
+
229
+ it "returns nil for negative numerical matches" do
230
+ md = RE2::Regexp.new('(\d)').match('123')
231
+ expect(md.end(-4)).to be_nil
232
+ end
233
+
234
+ it "returns nil for non-existent named matches" do
235
+ md = RE2::Regexp.new('(\d)').match('123')
236
+ expect(md.end('foo')).to be_nil
237
+ end
238
+
239
+ it "returns nil for non-existent symbol named matches" do
240
+ md = RE2::Regexp.new('(\d)').match('123')
241
+ expect(md.end(:foo)).to be_nil
206
242
  end
207
243
  end
208
244
  end