bahuvrihi-tap 0.10.6 → 0.10.7
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/rap +13 -5
- data/lib/tap.rb +0 -4
- data/lib/tap/app.rb +10 -138
- data/lib/tap/constants.rb +1 -1
- data/lib/tap/declarations.rb +201 -0
- data/lib/tap/env.rb +10 -2
- data/lib/tap/exe.rb +2 -2
- data/lib/tap/generator/generators/root/templates/Rakefile +1 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +1 -1
- data/lib/tap/spec.rb +38 -63
- data/lib/tap/spec/adapter.rb +8 -31
- data/lib/tap/spec/inheritable_class_test_root.rb +9 -0
- data/lib/tap/support/audit.rb +0 -2
- data/lib/tap/support/combinator.rb +83 -31
- data/lib/tap/support/configurable_class.rb +7 -7
- data/lib/tap/support/{dependable.rb → dependencies.rb} +9 -7
- data/lib/tap/support/executable.rb +226 -19
- data/lib/tap/support/gems/rake.rb +6 -3
- data/lib/tap/support/join.rb +87 -0
- data/lib/tap/support/joins.rb +13 -0
- data/lib/tap/support/joins/fork.rb +18 -0
- data/lib/tap/support/joins/merge.rb +20 -0
- data/lib/tap/support/joins/sequence.rb +21 -0
- data/lib/tap/support/joins/switch.rb +23 -0
- data/lib/tap/support/joins/sync_merge.rb +57 -0
- data/lib/tap/support/lazydoc/document.rb +10 -0
- data/lib/tap/support/node.rb +58 -0
- data/lib/tap/support/parser.rb +379 -0
- data/lib/tap/support/schema.rb +350 -0
- data/lib/tap/support/tdoc.rb +5 -0
- data/lib/tap/support/validation.rb +2 -0
- data/lib/tap/task.rb +26 -61
- data/lib/tap/test.rb +9 -82
- data/lib/tap/test/assertions.rb +38 -0
- data/lib/tap/test/extensions.rb +78 -0
- data/lib/tap/test/{file_methods.rb → file_test.rb} +64 -37
- data/lib/tap/test/{file_methods_class.rb → file_test_class.rb} +2 -2
- data/lib/tap/test/regexp_escape.rb +87 -0
- data/lib/tap/test/script_test.rb +46 -0
- data/lib/tap/test/script_tester.rb +109 -0
- data/lib/tap/test/{subset_methods.rb → subset_test.rb} +9 -9
- data/lib/tap/test/{subset_methods_class.rb → subset_test_class.rb} +22 -14
- data/lib/tap/test/{tap_methods.rb → tap_test.rb} +43 -6
- data/lib/tap/test/utils.rb +6 -3
- metadata +27 -24
- data/lib/tap/parser.rb +0 -619
- data/lib/tap/spec/file_methods.rb +0 -16
- data/lib/tap/spec/file_methods_class.rb +0 -13
- data/lib/tap/spec/subset_methods.rb +0 -14
- data/lib/tap/support/batchable.rb +0 -47
- data/lib/tap/support/batchable_class.rb +0 -107
- data/lib/tap/support/declarations.rb +0 -131
- data/lib/tap/support/lazydoc/declaration.rb +0 -20
- data/lib/tap/support/parsers/base.rb +0 -81
- data/lib/tap/support/parsers/server.rb +0 -113
- data/lib/tap/test/script_methods.rb +0 -75
- data/lib/tap/test/script_methods/regexp_escape.rb +0 -94
- data/lib/tap/test/script_methods/script_test.rb +0 -109
- data/lib/tap/workflow.rb +0 -161
data/lib/tap/test.rb
CHANGED
@@ -1,20 +1,26 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
$:.unshift File.expand_path("#{File.dirname(__FILE__)}/..")
|
3
|
+
require 'tap/test/extensions'
|
3
4
|
|
4
5
|
module Test # :nodoc:
|
5
6
|
module Unit # :nodoc:
|
6
7
|
|
7
8
|
# Methods extending TestCase. For more information see:
|
8
|
-
# - Tap::Test::
|
9
|
-
# - Tap::Test::
|
10
|
-
# - Tap::Test::
|
9
|
+
# - Tap::Test::SubsetTest
|
10
|
+
# - Tap::Test::FileTest
|
11
|
+
# - Tap::Test::TapTest
|
11
12
|
#
|
12
13
|
#--
|
13
14
|
#See the TestTutorial for more information.
|
14
15
|
class TestCase
|
16
|
+
extend Tap::Test::Extensions
|
17
|
+
|
15
18
|
class << self
|
19
|
+
alias tap_original_test_case_inherited inherited
|
16
20
|
|
17
21
|
def inherited(child)
|
22
|
+
super
|
23
|
+
tap_original_test_case_inherited(child)
|
18
24
|
child.instance_variable_set(:@skip_messages, [])
|
19
25
|
child.instance_variable_set(:@run_test_suite, true)
|
20
26
|
end
|
@@ -52,86 +58,7 @@ module Test # :nodoc:
|
|
52
58
|
Test::Unit::TestSuite.new(name)
|
53
59
|
end
|
54
60
|
end
|
55
|
-
|
56
|
-
# Causes a TestCase to act as a file test, by including FileMethods and
|
57
|
-
# instantiating class_test_root (a Tap::Root). The root and directories
|
58
|
-
# used by class_test_root may be specified as options.
|
59
|
-
#
|
60
|
-
# Note: by default acts_as_file_test determines a root directory
|
61
|
-
# <em>based on the calling file</em>. Be sure to specify the root
|
62
|
-
# directory manually if you call acts_as_file_test from a file that
|
63
|
-
# isn't the test file.
|
64
|
-
def acts_as_file_test(options={})
|
65
|
-
include Tap::Test::FileMethods
|
66
|
-
|
67
|
-
options = {
|
68
|
-
:root => test_root_dir,
|
69
|
-
:directories => {
|
70
|
-
:input => 'input',
|
71
|
-
:output => 'output',
|
72
|
-
:expected => 'expected'}
|
73
|
-
}.merge(options)
|
74
|
-
|
75
|
-
self.class_test_root = Tap::Root.new(options[:root], options[:directories])
|
76
|
-
end
|
77
|
-
|
78
|
-
# Causes a unit test to act as a tap test -- resulting in the following:
|
79
|
-
# - setup using acts_as_file_test
|
80
|
-
# - inclusion of Tap::Test::SubsetMethods
|
81
|
-
# - inclusion of Tap::Test::InstanceMethods
|
82
|
-
#
|
83
|
-
# Note: by default acts_as_tap_test determines a root directory
|
84
|
-
# <em>based on the calling file</em>. Be sure to specify the root
|
85
|
-
# directory manually if you call acts_as_file_test from a file that
|
86
|
-
# isn't the test file.
|
87
|
-
def acts_as_tap_test(options={})
|
88
|
-
include Tap::Test::SubsetMethods
|
89
|
-
include Tap::Test::FileMethods
|
90
|
-
include Tap::Test::TapMethods
|
91
|
-
|
92
|
-
acts_as_file_test({:root => test_root_dir}.merge(options))
|
93
|
-
end
|
94
|
-
|
95
|
-
def acts_as_script_test(options={})
|
96
|
-
include Tap::Test::FileMethods
|
97
|
-
include Tap::Test::ScriptMethods
|
98
|
-
|
99
|
-
acts_as_file_test({:root => test_root_dir}.merge(options))
|
100
|
-
end
|
101
|
-
|
102
|
-
private
|
103
|
-
|
104
|
-
# Infers the test root directory from the calling file.
|
105
|
-
# 'some_class.rb' => 'some_class'
|
106
|
-
# 'some_class_test.rb' => 'some_class'
|
107
|
-
def test_root_dir # :nodoc:
|
108
|
-
# caller[1] is considered the calling file (which should be the test case)
|
109
|
-
# note that the output of calller.first is like:
|
110
|
-
# ./path/to/file.rb:10
|
111
|
-
# ./path/to/file.rb:10:in 'method'
|
112
|
-
calling_file = caller[1].gsub(/:\d+(:in .*)?$/, "")
|
113
|
-
calling_file.chomp(File.extname(calling_file)).chomp("_test")
|
114
|
-
end
|
115
|
-
|
116
61
|
end
|
117
62
|
end
|
118
63
|
end
|
119
64
|
end
|
120
|
-
|
121
|
-
module Tap
|
122
|
-
|
123
|
-
# Modules facilitating testing. TapMethods are specific to
|
124
|
-
# Tap, but SubsetMethods and FileMethods are more general in
|
125
|
-
# their utility.
|
126
|
-
module Test
|
127
|
-
autoload(:SubsetMethods, 'tap/test/subset_methods')
|
128
|
-
autoload(:FileMethods, 'tap/test/file_methods')
|
129
|
-
autoload(:TapMethods, 'tap/test/tap_methods')
|
130
|
-
autoload(:ScriptMethods, 'tap/test/script_methods')
|
131
|
-
autoload(:Utils, 'tap/test/utils')
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'tap/test/utils'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
module Test
|
5
|
+
module Assertions
|
6
|
+
def assert_output_equal(a, b, msg=nil)
|
7
|
+
a = a[1..-1] if a[0] == ?\n
|
8
|
+
if a == b
|
9
|
+
assert true
|
10
|
+
else
|
11
|
+
flunk %Q{
|
12
|
+
#{msg}
|
13
|
+
==================== expected output ====================
|
14
|
+
#{Utils.whitespace_escape(a)}
|
15
|
+
======================== but was ========================
|
16
|
+
#{Utils.whitespace_escape(b)}
|
17
|
+
=========================================================
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def assert_alike(a, b, msg=nil)
|
23
|
+
if b =~ a
|
24
|
+
assert true
|
25
|
+
else
|
26
|
+
flunk %Q{
|
27
|
+
#{msg}
|
28
|
+
================= expected output like ==================
|
29
|
+
#{Utils.whitespace_escape(a)}
|
30
|
+
======================== but was ========================
|
31
|
+
#{Utils.whitespace_escape(b)}
|
32
|
+
=========================================================
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Tap
|
2
|
+
|
3
|
+
# Modules facilitating testing. TapTest are specific to
|
4
|
+
# Tap, but SubsetTest and FileTest are more general in
|
5
|
+
# their utility.
|
6
|
+
module Test
|
7
|
+
autoload(:SubsetTest, 'tap/test/subset_test')
|
8
|
+
autoload(:FileTest, 'tap/test/file_test')
|
9
|
+
autoload(:TapTest, 'tap/test/tap_test')
|
10
|
+
autoload(:ScriptTest, 'tap/test/script_test')
|
11
|
+
|
12
|
+
module Extensions
|
13
|
+
def acts_as_subset_test
|
14
|
+
include Tap::Test::SubsetTest
|
15
|
+
end
|
16
|
+
|
17
|
+
# Causes a TestCase to act as a file test, by including FileTest and
|
18
|
+
# instantiating class_test_root (a Tap::Root). The root and directories
|
19
|
+
# used by class_test_root may be specified as options.
|
20
|
+
#
|
21
|
+
# Note: by default acts_as_file_test determines a root directory
|
22
|
+
# <em>based on the calling file</em>. Be sure to specify the root
|
23
|
+
# directory manually if you call acts_as_file_test from a file that
|
24
|
+
# isn't the test file.
|
25
|
+
def acts_as_file_test(options={})
|
26
|
+
include Tap::Test::FileTest
|
27
|
+
|
28
|
+
options = {
|
29
|
+
:root => test_root_dir,
|
30
|
+
:directories => {
|
31
|
+
:input => 'input',
|
32
|
+
:output => 'output',
|
33
|
+
:expected => 'expected'}
|
34
|
+
}.merge(options)
|
35
|
+
|
36
|
+
self.class_test_root = Tap::Root.new(options[:root], options[:directories])
|
37
|
+
end
|
38
|
+
|
39
|
+
# Causes a unit test to act as a tap test -- resulting in the following:
|
40
|
+
# - setup using acts_as_file_test
|
41
|
+
# - inclusion of Tap::Test::SubsetTest
|
42
|
+
# - inclusion of Tap::Test::InstanceMethods
|
43
|
+
#
|
44
|
+
# Note: by default acts_as_tap_test determines a root directory
|
45
|
+
# <em>based on the calling file</em>. Be sure to specify the root
|
46
|
+
# directory manually if you call acts_as_file_test from a file that
|
47
|
+
# isn't the test file.
|
48
|
+
def acts_as_tap_test(options={})
|
49
|
+
acts_as_subset_test
|
50
|
+
acts_as_file_test({:root => test_root_dir}.merge(options))
|
51
|
+
|
52
|
+
include Tap::Test::TapTest
|
53
|
+
end
|
54
|
+
|
55
|
+
def acts_as_script_test(options={})
|
56
|
+
acts_as_file_test({:root => test_root_dir}.merge(options))
|
57
|
+
|
58
|
+
include Tap::Test::ScriptTest
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Infers the test root directory from the calling file.
|
64
|
+
# 'some_class.rb' => 'some_class'
|
65
|
+
# 'some_class_test.rb' => 'some_class'
|
66
|
+
def test_root_dir # :nodoc:
|
67
|
+
# caller[1] is considered the calling file (which should be the test case)
|
68
|
+
# note that caller entries are like this:
|
69
|
+
# ./path/to/file.rb:10
|
70
|
+
# ./path/to/file.rb:10:in 'method'
|
71
|
+
|
72
|
+
calling_file = caller[1].gsub(/:\d+(:in .*)?$/, "")
|
73
|
+
calling_file.chomp(File.extname(calling_file)).chomp("_test")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
@@ -1,19 +1,19 @@
|
|
1
|
-
require 'tap/test/utils'
|
2
1
|
require 'tap/test/env_vars'
|
3
|
-
require 'tap/test/
|
2
|
+
require 'tap/test/assertions'
|
3
|
+
require 'tap/test/regexp_escape'
|
4
|
+
require 'tap/test/file_test_class'
|
4
5
|
|
5
6
|
module Tap
|
6
7
|
module Test
|
7
8
|
|
8
|
-
|
9
|
-
#
|
10
|
-
# directories. FileMethods provides each test method is setup with a Tap::Root
|
9
|
+
# FileTest facilitates access and utilization of test-specific files and
|
10
|
+
# directories. FileTest provides each test method is setup with a Tap::Root
|
11
11
|
# (method_root) specific for the method, and defines a new assertion method
|
12
12
|
# (assert_files) to facilitate tests which involve the production and/or
|
13
13
|
# modification of files.
|
14
14
|
#
|
15
|
-
# [
|
16
|
-
# class
|
15
|
+
# [file_test_doc_test.rb]
|
16
|
+
# class FileTestDocTest < Test::Unit::TestCase
|
17
17
|
# acts_as_file_test
|
18
18
|
#
|
19
19
|
# def test_something
|
@@ -56,13 +56,14 @@ module Tap
|
|
56
56
|
# end
|
57
57
|
#
|
58
58
|
# See {Test::Unit::TestCase}[link:classes/Test/Unit/TestCase.html] and
|
59
|
-
#
|
60
|
-
module
|
59
|
+
# FileTestClass for more information.
|
60
|
+
module FileTest
|
61
61
|
include Tap::Test::EnvVars
|
62
|
+
include Tap::Test::Assertions
|
62
63
|
|
63
64
|
def self.included(base)
|
64
65
|
super
|
65
|
-
base.extend
|
66
|
+
base.extend FileTestClass
|
66
67
|
end
|
67
68
|
|
68
69
|
# Convenience method to access the class_test_root.
|
@@ -121,13 +122,19 @@ module Tap
|
|
121
122
|
# % rap test KEEP_FAILURES=true
|
122
123
|
#
|
123
124
|
# Be sure to call super when overriding this method.
|
124
|
-
def teardown
|
125
|
+
def teardown
|
126
|
+
# check that method_root still exists (nil may
|
127
|
+
# indicate setup was overridden without super)
|
128
|
+
unless method_root
|
129
|
+
raise "teardown failure: method_root is nil"
|
130
|
+
end
|
131
|
+
|
125
132
|
# clear out the output folder if it exists, unless flagged otherwise
|
126
133
|
unless env("KEEP_OUTPUTS") || (!@test_passed && env("KEEP_FAILURES"))
|
127
134
|
begin
|
128
135
|
Utils.clear_dir(method_root[:output])
|
129
136
|
rescue
|
130
|
-
raise("teardown failure: could not remove output files")
|
137
|
+
raise("teardown failure: could not remove output files (#{$!.message})")
|
131
138
|
end
|
132
139
|
end
|
133
140
|
|
@@ -180,7 +187,7 @@ module Tap
|
|
180
187
|
# Lets define a test that transforms input files into output files in a trivial
|
181
188
|
# way, simply by replacing 'input' with 'output' in the file.
|
182
189
|
#
|
183
|
-
# class
|
190
|
+
# class FileTestDocTest < Test::Unit::TestCase
|
184
191
|
# acts_as_file_test
|
185
192
|
#
|
186
193
|
# def test_sub
|
@@ -201,7 +208,7 @@ module Tap
|
|
201
208
|
#
|
202
209
|
# Now say you had some input and expected files for the 'test_sub' method:
|
203
210
|
#
|
204
|
-
#
|
211
|
+
# file_test_doc/test_sub
|
205
212
|
# |- expected
|
206
213
|
# | |- one.txt
|
207
214
|
# | `- two.txt
|
@@ -290,7 +297,7 @@ module Tap
|
|
290
297
|
# for more details).
|
291
298
|
#
|
292
299
|
# === Keeping Outputs
|
293
|
-
# By default
|
300
|
+
# By default FileTest sets teardown to cleans up the output directory. For
|
294
301
|
# ease in debugging, ENV variable flags can be specified to keep all output
|
295
302
|
# files (KEEP_OUTPUTS) or to keep the output files for just the tests that fail
|
296
303
|
# (KEEP_FAILURES). These flags can be specified from the command line if you're
|
@@ -303,7 +310,42 @@ module Tap
|
|
303
310
|
# TODO:
|
304
311
|
# * add debugging information to indicate, for instance,
|
305
312
|
# when dereferencing is going on.
|
306
|
-
def assert_files(options={}) # :yields: input_files
|
313
|
+
def assert_files(options={}, &block) # :yields: input_files
|
314
|
+
transform_test(block, options) do |expected_file, output_file|
|
315
|
+
unless FileUtils.cmp(expected_file, output_file)
|
316
|
+
flunk "<#{expected_file}> not equal to\n<#{output_file}>"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def assert_files_alike(options={}, &block) # :yields: input_files
|
322
|
+
transform_test(block, options) do |expected_file, output_file|
|
323
|
+
regexp = RegexpEscape.new(File.read(expected_file))
|
324
|
+
str = File.read(output_file)
|
325
|
+
assert_alike(regexp, str, "<#{expected_file}> not equal to\n<#{output_file}>")
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# The default assert_files options
|
330
|
+
def default_assert_files_options
|
331
|
+
{
|
332
|
+
:input_dir => method_root[:input],
|
333
|
+
:output_dir => method_root[:output],
|
334
|
+
:expected_dir => method_root[:expected],
|
335
|
+
|
336
|
+
:input_files => nil,
|
337
|
+
:expected_files => nil,
|
338
|
+
:include_input_directories => false,
|
339
|
+
:include_expected_directories => false,
|
340
|
+
|
341
|
+
:reference_dir => nil,
|
342
|
+
:reference_extname => '.ref'
|
343
|
+
}
|
344
|
+
end
|
345
|
+
|
346
|
+
private
|
347
|
+
|
348
|
+
def transform_test(block, options={}) # :yields: expected_files, output_files
|
307
349
|
make_test_directories
|
308
350
|
|
309
351
|
options = default_assert_files_options.merge(options)
|
@@ -342,7 +384,7 @@ module Tap
|
|
342
384
|
end
|
343
385
|
|
344
386
|
# get output files from the block, expand and sort
|
345
|
-
output_files = [
|
387
|
+
output_files = [block.call(input_files)].flatten.collect do |output_file|
|
346
388
|
File.expand_path(output_file)
|
347
389
|
end.sort
|
348
390
|
|
@@ -356,32 +398,17 @@ module Tap
|
|
356
398
|
errors = []
|
357
399
|
Utils.each_pair(expected_files, output_files) do |expected_file, output_file|
|
358
400
|
unless (File.directory?(expected_file) && File.directory?(output_file)) || FileUtils.cmp(expected_file, output_file)
|
359
|
-
|
401
|
+
begin
|
402
|
+
yield(expected_file, output_file)
|
403
|
+
rescue
|
404
|
+
errors << $!
|
405
|
+
end
|
360
406
|
end
|
361
407
|
end
|
362
408
|
flunk "File compare failed:\n" + errors.join("\n") unless errors.empty?
|
363
409
|
end
|
364
410
|
end
|
365
411
|
|
366
|
-
# The default assert_files options
|
367
|
-
def default_assert_files_options
|
368
|
-
{
|
369
|
-
:input_dir => method_root[:input],
|
370
|
-
:output_dir => method_root[:output],
|
371
|
-
:expected_dir => method_root[:expected],
|
372
|
-
|
373
|
-
:input_files => nil,
|
374
|
-
:expected_files => nil,
|
375
|
-
:include_input_directories => false,
|
376
|
-
:include_expected_directories => false,
|
377
|
-
|
378
|
-
:reference_dir => nil,
|
379
|
-
:reference_extname => '.ref'
|
380
|
-
}
|
381
|
-
end
|
382
|
-
|
383
|
-
private
|
384
|
-
|
385
412
|
# utility method for method_tempfile; increments index until the
|
386
413
|
# path base.indexext does not exist.
|
387
414
|
def next_indexed_path(base, index, ext) # :nodoc:
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Tap
|
2
2
|
module Test
|
3
3
|
|
4
|
-
# Class methods extending tests which include
|
5
|
-
module
|
4
|
+
# Class methods extending tests which include FileTest.
|
5
|
+
module FileTestClass
|
6
6
|
|
7
7
|
# The class-level test root (a Tap::Root).
|
8
8
|
attr_accessor :class_test_root
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'tap/test/utils'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
module Test
|
5
|
+
|
6
|
+
# RegexpEscape is a subclass of regexp that escapes all but the text in a
|
7
|
+
# special escape sequence. This allows the creation of complex regexps
|
8
|
+
# to match, for instance, console output.
|
9
|
+
#
|
10
|
+
# The RegexpEscape.escape (or equivalently the quote) method does the
|
11
|
+
# work; all regexp-active characters are escaped except for characters
|
12
|
+
# enclosed by ':.' and '.:' delimiters.
|
13
|
+
#
|
14
|
+
# RegexpEscape.escape('reg[exp]+ chars. are(quoted)') # => 'reg\[exp\]\+\ chars\.\ are\(quoted\)'
|
15
|
+
# RegexpEscape.escape('these are not: :.a(b*)c.:') # => 'these\ are\ not:\ a(b*)c'
|
16
|
+
#
|
17
|
+
# In addition, all-period regexps are automatically upgraded to '.*?';
|
18
|
+
# use the '.{n}' notation to specify n arbitrary characters.
|
19
|
+
#
|
20
|
+
# RegexpEscape.escape('_:..:_:...:_:....:') # => '_.*?_.*?_.*?'
|
21
|
+
# RegexpEscape.escape(':..{1}.:') # => '.{1}'
|
22
|
+
#
|
23
|
+
# RegexpEscape instances are initialized using the escaped input string
|
24
|
+
# and return the original string upon to_s.
|
25
|
+
#
|
26
|
+
# str = %q{
|
27
|
+
# a multiline
|
28
|
+
# :...:
|
29
|
+
# example}
|
30
|
+
# r = RegexpEscape.new(str)
|
31
|
+
#
|
32
|
+
# r =~ %q{
|
33
|
+
# a multiline
|
34
|
+
# matching
|
35
|
+
# example} # => true
|
36
|
+
#
|
37
|
+
# r !~ %q{
|
38
|
+
# a failing multiline
|
39
|
+
# example} # => true
|
40
|
+
#
|
41
|
+
# r.to_s # => str
|
42
|
+
#
|
43
|
+
class RegexpEscape < Regexp
|
44
|
+
|
45
|
+
# matches the escape sequence
|
46
|
+
ESCAPE_SEQUENCE = /:\..*?\.:/
|
47
|
+
|
48
|
+
class << self
|
49
|
+
|
50
|
+
# Escapes regexp-active characters in str, except for character
|
51
|
+
# delimited by ':.' and '.:'. See the class description for
|
52
|
+
# details.
|
53
|
+
def escape(str)
|
54
|
+
substituents = []
|
55
|
+
str.scan(ESCAPE_SEQUENCE) do
|
56
|
+
regexp_str = $&[2...-2]
|
57
|
+
regexp_str = ".*?" if regexp_str =~ /^\.*$/
|
58
|
+
substituents << regexp_str
|
59
|
+
end
|
60
|
+
substituents << ""
|
61
|
+
|
62
|
+
splits = str.split(ESCAPE_SEQUENCE).collect do |split|
|
63
|
+
super(split)
|
64
|
+
end
|
65
|
+
splits << "" if splits.empty?
|
66
|
+
|
67
|
+
splits.zip(substituents).to_a.flatten.join
|
68
|
+
end
|
69
|
+
|
70
|
+
# Same as escape.
|
71
|
+
def quote(str)
|
72
|
+
escape(str)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def initialize(str)
|
77
|
+
super(RegexpEscape.escape(str))
|
78
|
+
@original_str = str
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the original string for self
|
82
|
+
def to_s
|
83
|
+
@original_str
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|