quality_extensions 1.1.6 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/License +403 -0
- data/Rakefile +2 -0
- data/Readme.rdoc +2 -3
- data/VERSION +1 -0
- data/doc_include/ReleaseNotes-0.0.10 +1 -0
- data/doc_include/ReleaseNotes-0.0.12 +1 -0
- data/doc_include/ReleaseNotes-0.0.13 +2 -0
- data/doc_include/ReleaseNotes-0.0.14 +1 -0
- data/doc_include/ReleaseNotes-0.0.15 +2 -0
- data/doc_include/ReleaseNotes-0.0.16 +1 -0
- data/doc_include/ReleaseNotes-0.0.17 +1 -0
- data/doc_include/ReleaseNotes-0.0.18 +1 -0
- data/doc_include/ReleaseNotes-0.0.19 +1 -0
- data/doc_include/ReleaseNotes-0.0.20 +1 -0
- data/doc_include/ReleaseNotes-0.0.21 +7 -0
- data/doc_include/ReleaseNotes-0.0.22 +2 -0
- data/doc_include/ReleaseNotes-0.0.23 +1 -0
- data/doc_include/ReleaseNotes-0.0.24 +3 -0
- data/doc_include/ReleaseNotes-0.0.25 +3 -0
- data/doc_include/ReleaseNotes-0.0.26 +2 -0
- data/doc_include/ReleaseNotes-0.0.27 +1 -0
- data/doc_include/ReleaseNotes-0.0.28 +1 -0
- data/doc_include/ReleaseNotes-0.0.29 +3 -0
- data/doc_include/ReleaseNotes-0.0.3 +1 -0
- data/doc_include/ReleaseNotes-0.0.30 +4 -0
- data/doc_include/ReleaseNotes-0.0.31 +1 -0
- data/doc_include/ReleaseNotes-0.0.32 +1 -0
- data/doc_include/ReleaseNotes-0.0.33 +1 -0
- data/doc_include/ReleaseNotes-0.0.34 +2 -0
- data/doc_include/ReleaseNotes-0.0.35 +1 -0
- data/doc_include/ReleaseNotes-0.0.36 +1 -0
- data/doc_include/ReleaseNotes-0.0.37 +1 -0
- data/doc_include/ReleaseNotes-0.0.38 +1 -0
- data/doc_include/ReleaseNotes-0.0.39 +1 -0
- data/doc_include/ReleaseNotes-0.0.4 +2 -0
- data/doc_include/ReleaseNotes-0.0.40 +1 -0
- data/doc_include/ReleaseNotes-0.0.41 +1 -0
- data/doc_include/ReleaseNotes-0.0.42 +2 -0
- data/doc_include/ReleaseNotes-0.0.43 +2 -0
- data/doc_include/ReleaseNotes-0.0.44 +3 -0
- data/doc_include/ReleaseNotes-0.0.45 +1 -0
- data/doc_include/ReleaseNotes-0.0.46 +1 -0
- data/doc_include/ReleaseNotes-0.0.47 +1 -0
- data/doc_include/ReleaseNotes-0.0.48 +2 -0
- data/doc_include/ReleaseNotes-0.0.49 +2 -0
- data/doc_include/ReleaseNotes-0.0.5 +1 -0
- data/doc_include/ReleaseNotes-0.0.50 +1 -0
- data/doc_include/ReleaseNotes-0.0.51 +1 -0
- data/doc_include/ReleaseNotes-0.0.52 +1 -0
- data/doc_include/ReleaseNotes-0.0.53 +3 -0
- data/doc_include/ReleaseNotes-0.0.54 +1 -0
- data/doc_include/ReleaseNotes-0.0.55 +2 -0
- data/doc_include/ReleaseNotes-0.0.56 +1 -0
- data/doc_include/ReleaseNotes-0.0.57 +3 -0
- data/doc_include/ReleaseNotes-0.0.58 +1 -0
- data/doc_include/ReleaseNotes-0.0.59 +1 -0
- data/doc_include/ReleaseNotes-0.0.6 +3 -0
- data/doc_include/ReleaseNotes-0.0.60 +3 -0
- data/doc_include/ReleaseNotes-0.0.61 +1 -0
- data/doc_include/ReleaseNotes-0.0.62 +1 -0
- data/doc_include/ReleaseNotes-0.0.63 +1 -0
- data/doc_include/ReleaseNotes-0.0.64 +1 -0
- data/doc_include/ReleaseNotes-0.0.7 +1 -0
- data/doc_include/ReleaseNotes-0.0.8 +2 -0
- data/doc_include/ReleaseNotes-0.0.9 +1 -0
- data/doc_include/ReleaseNotes-0.1.1 +1 -0
- data/doc_include/ReleaseNotes-0.1.2 +1 -0
- data/doc_include/ReleaseNotes-0.1.3 +1 -0
- data/doc_include/ReleaseNotes-0.1.4 +1 -0
- data/doc_include/ReleaseNotes-0.1.5 +0 -0
- data/doc_include/ReleaseNotes-1.0.0 +1 -0
- data/doc_include/ReleaseNotes-1.0.1 +1 -0
- data/doc_include/ReleaseNotes-1.0.2 +1 -0
- data/doc_include/ReleaseNotes-1.0.3 +1 -0
- data/doc_include/ReleaseNotes-1.1.0 +1 -0
- data/doc_include/ReleaseNotes-1.1.1 +1 -0
- data/doc_include/ReleaseNotes-1.1.6 +1 -0
- data/doc_include/assert_equal_with_difference_highlighting-there_he_is.png +0 -0
- data/doc_include/assert_equal_with_difference_highlighting-wheres_waldo.png +0 -0
- data/escape/Rakefile +62 -0
- data/escape/Readme +16 -0
- data/escape/doc_include/ReleaseNotes-0.0.1 +1 -0
- data/escape/doc_include/ReleaseNotes-0.0.3 +1 -0
- data/escape/doc_include/ReleaseNotes-0.0.4 +1 -0
- data/escape/doc_include/ReleaseNotes-0.0.5 +1 -0
- data/escape/doc_include/template/qualitysmith.rb +631 -0
- data/escape/lib/escape.rb +302 -0
- data/escape/pkg/escape-0.0.5.tgz +0 -0
- data/escape/pkg/escape-0.0.5.zip +0 -0
- data/escape/pkg/escape-0.0.5/Readme +16 -0
- data/escape/pkg/escape-0.0.5/doc_include/template/qualitysmith.rb +631 -0
- data/escape/pkg/escape-0.0.5/lib/escape.rb +302 -0
- data/lib/quality_extensions.rb +3 -1
- data/lib/quality_extensions/array/average.rb +45 -0
- data/lib/quality_extensions/array/expand_ranges.rb +2 -2
- data/lib/quality_extensions/array/shell_escape.rb +2 -2
- data/lib/quality_extensions/color/gradiate.rb +5 -5
- data/lib/quality_extensions/enumerable/every.rb +2 -2
- data/lib/quality_extensions/enumerable/grep_with_index.rb +51 -0
- data/lib/quality_extensions/enumerable/grepv.rb +1 -0
- data/lib/quality_extensions/enumerable/map_with_index.rb +2 -0
- data/lib/quality_extensions/enumerable/max_by_value.rb +72 -0
- data/lib/quality_extensions/enumerable/select_while.rb +7 -2
- data/lib/quality_extensions/enumerable/select_with_index.rb +2 -2
- data/lib/quality_extensions/float/truncate.rb +2 -1
- data/lib/quality_extensions/hash/except.rb +2 -2
- data/lib/quality_extensions/hash/to_query_string.rb +6 -6
- data/lib/quality_extensions/helpers/numbers.rb +259 -0
- data/lib/quality_extensions/kernel/die.rb +2 -2
- data/lib/quality_extensions/kernel/filter_output.rb +1 -0
- data/lib/quality_extensions/kernel/require_all.rb +7 -7
- data/lib/quality_extensions/module/guard_method.rb +3 -3
- data/lib/quality_extensions/object/non.rb +10 -4
- data/lib/quality_extensions/pathname.rb +51 -3
- data/lib/quality_extensions/regexp/named_captures.rb +7 -6
- data/lib/quality_extensions/regexp/to_plain_s.rb +116 -0
- data/lib/quality_extensions/safe_method.rb +100 -0
- data/lib/quality_extensions/safe_nil.rb +87 -11
- data/lib/quality_extensions/string/chomped_lines.rb +0 -0
- data/lib/quality_extensions/string/integer_eh.rb +2 -2
- data/lib/quality_extensions/string/prefix.rb +120 -0
- data/lib/quality_extensions/string/prefix_lines.rb +4 -69
- data/lib/quality_extensions/string/safe_numeric_conversion.rb +5 -5
- data/lib/quality_extensions/string/shell_escape.rb +3 -3
- data/lib/quality_extensions/string/to_proc.rb +119 -0
- data/lib/quality_extensions/template.rb +2 -2
- data/lib/quality_extensions/test/difference_highlighting.rb +2 -2
- data/lib/quality_extensions/version.rb +3 -0
- data/quality_extensions.gemspec +21 -0
- metadata +249 -145
- data/lib/Xfind_bug_test.rb +0 -28
@@ -4,6 +4,7 @@
|
|
4
4
|
# License:: Ruby License
|
5
5
|
# Submit to Facets?:: Yes.
|
6
6
|
# Developer notes:
|
7
|
+
# To do: write an add_prefix_to_output method based on this that prefixes all lines output with some string, to help group them together when looking through a long log file
|
7
8
|
#++
|
8
9
|
|
9
10
|
require 'stringio'
|
@@ -9,8 +9,8 @@ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
|
9
9
|
require 'rubygems'
|
10
10
|
require 'facets/filelist'
|
11
11
|
|
12
|
-
require 'facets/kernel/
|
13
|
-
|
12
|
+
require 'facets/kernel/require_relative'
|
13
|
+
require_relative '../file/exact_match_regexp'
|
14
14
|
|
15
15
|
module Kernel
|
16
16
|
|
@@ -60,7 +60,7 @@ module Kernel
|
|
60
60
|
#
|
61
61
|
# All of the +options+ available for +require_all+ are still available here.
|
62
62
|
#
|
63
|
-
def
|
63
|
+
def require_relative_all(dir = './', options = {})
|
64
64
|
raise ArgumentError.new("dir must be a String") unless dir.is_a?(String)
|
65
65
|
local_dir = File.dirname( caller[0] )
|
66
66
|
require_all(
|
@@ -86,7 +86,7 @@ require 'English'
|
|
86
86
|
class TheTest < Test::Unit::TestCase
|
87
87
|
def setup
|
88
88
|
@base_dir = "#{Dir.tmpdir}/require_all_test"
|
89
|
-
@base_local_dir = File.dirname(__FILE__) # To allow testing of
|
89
|
+
@base_local_dir = File.dirname(__FILE__) # To allow testing of require_relative_all. But tests should put everything in "#{@base_local_dir}/require_all_test" to avoid clutter or name conflicts with other files!
|
90
90
|
FileUtils.mkdir @base_dir
|
91
91
|
@deep_dir = "really/really/deep/subdir"
|
92
92
|
$loaded = []
|
@@ -137,11 +137,11 @@ class TheTest < Test::Unit::TestCase
|
|
137
137
|
assert_equal ['require_me.rb'], $loaded
|
138
138
|
end
|
139
139
|
|
140
|
-
def
|
140
|
+
def test_require_relative_all
|
141
141
|
create_ruby_file 'require_all_test/lib/require_me.rb', @base_local_dir
|
142
142
|
create_ruby_file 'require_all_test/lib/please_ignore_me.rb', @base_local_dir
|
143
143
|
|
144
|
-
|
144
|
+
require_relative_all 'require_all_test/lib', :exclude => [/ignore/]
|
145
145
|
assert_equal ['require_all_test/lib/require_me.rb'], $loaded
|
146
146
|
end
|
147
147
|
|
@@ -172,7 +172,7 @@ class TheTest < Test::Unit::TestCase
|
|
172
172
|
#require_all File.dirname(@base_dir), :exclude => [/^all\.rb$/]
|
173
173
|
# This works, but it's too much to expect users to type out!:
|
174
174
|
#require_all File.dirname(@base_dir), :exclude => [/(\/|^)all\.rb$/]
|
175
|
-
|
175
|
+
|
176
176
|
# So...... I added an :exclude_files option so that people wouldn't have to!
|
177
177
|
require_all File.dirname(@base_dir), :exclude_files => ['all.rb']
|
178
178
|
|
@@ -14,8 +14,8 @@
|
|
14
14
|
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
15
15
|
require 'rubygems'
|
16
16
|
require 'quality_extensions/module/attribute_accessors'
|
17
|
-
require 'facets/kernel/
|
18
|
-
|
17
|
+
require 'facets/kernel/require_relative'
|
18
|
+
require_relative 'bool_attr_accessor'
|
19
19
|
#require 'quality_extensions/module/bool_attr_accessor'
|
20
20
|
require 'quality_extensions/symbol/match'
|
21
21
|
|
@@ -33,7 +33,7 @@ class Module
|
|
33
33
|
# # Section of code during which you don't want any stupid stuff to happen
|
34
34
|
# end # Causes @stupid_stuff_disabled to be set back to false
|
35
35
|
# # Okay, a, you can resume doing stupid stuff again...
|
36
|
-
#
|
36
|
+
#
|
37
37
|
# If you want your guard method to *disable* the flag rather than *enable* it, simply pass false to the guard method.
|
38
38
|
#
|
39
39
|
# These calls can be nested however you wish:
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# Copyright:: Copyright (c) 2009, Tyler Rick
|
5
5
|
# License:: Ruby License
|
6
6
|
# Submit to Facets?::
|
7
|
-
# Developer notes::
|
7
|
+
# Developer notes::
|
8
8
|
# * Make a patch to actual pathname source.
|
9
9
|
# * document all Pathname methods better, copying from docs for delegated class if necessary, but NOT just saying 'See FileTest.file?.' How useless that is to have to keep flipping back and forth referring to like 4 different classes!
|
10
10
|
# History::
|
@@ -18,6 +18,54 @@
|
|
18
18
|
#++
|
19
19
|
|
20
20
|
|
21
|
+
|
22
|
+
# TODO: How much of this stuff already exists as private methods??
|
23
|
+
#
|
24
|
+
# # chop_basename(path) -> [pre-basename, basename] or nil
|
25
|
+
# def chop_basename(path)
|
26
|
+
# base = File.basename(path)
|
27
|
+
# if /\A#{SEPARATOR_PAT}?\z/ =~ base
|
28
|
+
# return nil
|
29
|
+
# else
|
30
|
+
# return path[0, path.rindex(base)], base
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
# private :chop_basename
|
34
|
+
#
|
35
|
+
# # split_names(path) -> prefix, [name, ...]
|
36
|
+
# def split_names(path)
|
37
|
+
# names = []
|
38
|
+
# while r = chop_basename(path)
|
39
|
+
# path, basename = r
|
40
|
+
# names.unshift basename
|
41
|
+
# end
|
42
|
+
# return path, names
|
43
|
+
# end
|
44
|
+
# private :split_names
|
45
|
+
#
|
46
|
+
# def prepend_prefix(prefix, relpath)
|
47
|
+
# if relpath.empty?
|
48
|
+
# File.dirname(prefix)
|
49
|
+
# elsif /#{SEPARATOR_PAT}/ =~ prefix
|
50
|
+
# prefix = File.dirname(prefix)
|
51
|
+
# prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a'
|
52
|
+
# prefix + relpath
|
53
|
+
# else
|
54
|
+
# prefix + relpath
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
# private :prepend_prefix
|
58
|
+
#
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
21
69
|
require 'pathname'
|
22
70
|
require 'tempfile'
|
23
71
|
|
@@ -71,7 +119,7 @@ class Pathname
|
|
71
119
|
Pathname.new(new_path)
|
72
120
|
end
|
73
121
|
alias_method :move, :mv
|
74
|
-
|
122
|
+
|
75
123
|
# Copies self to +dest+ using FileUtils.cp.
|
76
124
|
#
|
77
125
|
# See documentation for FileUtils.cp for a list of valid +options+.
|
@@ -84,7 +132,7 @@ class Pathname
|
|
84
132
|
end
|
85
133
|
alias_method :copy, :cp
|
86
134
|
|
87
|
-
# Copies self to +dest+ using FileUtils.cp_r. If self is a directory, this method copies all its contents recursively. If +dest+ is a directory, copies self to +dest/src+.
|
135
|
+
# Copies self to +dest+ using FileUtils.cp_r. If self is a directory, this method copies all its contents recursively. If +dest+ is a directory, copies self to +dest/src+.
|
88
136
|
#
|
89
137
|
# See documentation for FileUtils.cp_r for a list of valid +options+.
|
90
138
|
#
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# http://tfletcher.com/lib/named_captures.rb
|
2
2
|
require 'strscan'
|
3
3
|
|
4
|
+
# TODO: require the file that defines this instead
|
4
5
|
class Module
|
5
6
|
# c.f. ActiveSupport
|
6
7
|
def alias_method_chain(target, feature)
|
@@ -13,7 +14,7 @@ end
|
|
13
14
|
|
14
15
|
class MatchData
|
15
16
|
attr_accessor :capture_names
|
16
|
-
|
17
|
+
|
17
18
|
def method_missing(capture_name, *args, &block)
|
18
19
|
if index = capture_names.index(capture_name)
|
19
20
|
return self[index + 1]
|
@@ -37,10 +38,10 @@ class Regexp
|
|
37
38
|
end
|
38
39
|
|
39
40
|
private
|
40
|
-
|
41
|
+
|
41
42
|
def extract_capture_names_from(pattern)
|
42
43
|
names, scanner, last_close = [], StringScanner.new(pattern), nil
|
43
|
-
|
44
|
+
|
44
45
|
while scanner.skip_until(/\(/)
|
45
46
|
next if scanner.pre_match =~ /\\$/
|
46
47
|
|
@@ -51,7 +52,7 @@ class Regexp
|
|
51
52
|
else
|
52
53
|
names << :capture
|
53
54
|
end
|
54
|
-
|
55
|
+
|
55
56
|
while scanner.skip_until(/[()]/)
|
56
57
|
if scanner.matched =~ /\)$/
|
57
58
|
(names.size - 1).downto(0) do |i|
|
@@ -66,14 +67,14 @@ class Regexp
|
|
66
67
|
end
|
67
68
|
end
|
68
69
|
end
|
69
|
-
|
70
|
+
|
70
71
|
return names
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
74
75
|
if __FILE__ == $0 then
|
75
76
|
require 'test/unit'
|
76
|
-
|
77
|
+
|
77
78
|
class NamedCapturesTest < Test::Unit::TestCase
|
78
79
|
def test_escaped_brackets_are_ignored
|
79
80
|
assert /\(\)\(\)/.capture_names.empty?
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#--
|
2
|
+
# Author:: Tyler Rick
|
3
|
+
# Copyright:: Copyright (c) 2009 Tyler Rick
|
4
|
+
# License:: Ruby License
|
5
|
+
# Submit to Facets?:: Yes.
|
6
|
+
# Developer notes::
|
7
|
+
#++
|
8
|
+
|
9
|
+
|
10
|
+
class Regexp
|
11
|
+
def self.to_plain_s(input, also_unescape = nil)
|
12
|
+
input.
|
13
|
+
gsub(/((?<!\\)(\\\\)*)\(((?=\?[^):]*:)\?[^):]*:)?/, '\1'). # remove all unescaped '('s and any '?-mix:b-style prefix that their contents begin with
|
14
|
+
gsub(/((?<!\\)(\\\\)*)[)?*+\[\]]/, '\1'). # remove all unescaped ')', '?', '*', etc.es
|
15
|
+
gsub(/((?<!\\)(\\\\)*)\{.*\}/, '\1'). # remove all unescaped /{.*}/
|
16
|
+
gsub(/((?<!\\)(\\\\)*)\\([().\/#{also_unescape}])/, '\1\3'). # unescape any remaining (escaped) '\(', '\)', '\.', etc.es
|
17
|
+
gsub(/\\\\/, '\\')
|
18
|
+
end
|
19
|
+
|
20
|
+
# Sometimes you don't want to see a [serialized] version of the regular expression:
|
21
|
+
# /a/.to_s => '(?-mix:a)'
|
22
|
+
# and you simply want a simple, human-readable version:
|
23
|
+
# /a/.to_plain_s => 'a'
|
24
|
+
#
|
25
|
+
# Since the Regexp object doesn't know what it was delimited with, to_plain_s can't know which
|
26
|
+
# delimiter character, if any, it should try to unescape.
|
27
|
+
#
|
28
|
+
# %r#a\#b#.to_plain_s('#') # => "a#b"
|
29
|
+
# %r#a\#b#.to_plain_s # => "a\\#b"
|
30
|
+
#
|
31
|
+
# An escaped '/' will always be unescaped, since Ruby escapes for you even when you don't:
|
32
|
+
# /a\/b/.to_plain_s # => 'a/b'
|
33
|
+
# %r#a\/b#.to_plain_s # => 'a/b'
|
34
|
+
# %r#a/b#. to_plain_s # => "a/b"
|
35
|
+
#
|
36
|
+
def to_plain_s(also_unescape = nil)
|
37
|
+
Regexp.to_plain_s(self.to_s, also_unescape)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# _____ _
|
43
|
+
# |_ _|__ ___| |_
|
44
|
+
# | |/ _ \/ __| __|
|
45
|
+
# | | __/\__ \ |_
|
46
|
+
# |_|\___||___/\__|
|
47
|
+
#
|
48
|
+
=begin test
|
49
|
+
require 'spec/autorun'
|
50
|
+
|
51
|
+
describe 'Regexp#to_plain_s' do
|
52
|
+
it "removes '?-mix:' prefix" do
|
53
|
+
/a/.to_plain_s.should == 'a'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "removes unescaped ()" do
|
57
|
+
/(a)bc/.to_plain_s.should == 'abc'
|
58
|
+
end
|
59
|
+
|
60
|
+
it "removes unescaped ?, *, +" do
|
61
|
+
/a?b*c+/.to_plain_s.should == 'abc'
|
62
|
+
end
|
63
|
+
|
64
|
+
it "removes unescaped /{3}/" do
|
65
|
+
/a{3}bc/.to_plain_s.should == 'abc'
|
66
|
+
/a{1,3}bc/.to_plain_s.should == 'abc'
|
67
|
+
end
|
68
|
+
|
69
|
+
it "unescapes escaped ()'s correctly" do
|
70
|
+
# 1 1 2 1 2
|
71
|
+
Regexp.to_plain_s( "(a) \\(b\\) \\\\(c\\\\)" ).should ==
|
72
|
+
"a (b) \\c\\"
|
73
|
+
|
74
|
+
# 1 1 12 12
|
75
|
+
/(a) \(b\) \\(c\\)/.to_plain_s.should ==
|
76
|
+
"a (b) \\c\\"
|
77
|
+
|
78
|
+
# 1 1 1 2 1 2 1 2 3 1 2 3 1 2 3 4 1 2 3 4 1 2 3 4 5 1 2 3 4 5
|
79
|
+
Regexp.to_plain_s( "(a) \\(b\\) \\\\(c\\\\) \\\\\\(d\\\\\\) \\\\\\\\(e\\\\\\\\) \\\\\\\\\\(f\\\\\\\\\\)" ).should ==
|
80
|
+
# 1 1 1 1 1 2 1 2 1 2 1 2
|
81
|
+
"a (b) \\c\\ \\(d\\) \\\\e\\\\ \\\\(f\\\\)"
|
82
|
+
|
83
|
+
# 1 1 12 12 123 123 1234 1234 12345 12345
|
84
|
+
/(a) \(b\) \\(c\\) \\\(d\\\) \\\\(e\\\\) \\\\\(f\\\\\)/.to_plain_s.should ==
|
85
|
+
# 1 1 1 1 1 2 1 2 1 2 1 2
|
86
|
+
"a (b) \\c\\ \\(d\\) \\\\e\\\\ \\\\(f\\\\)"
|
87
|
+
end
|
88
|
+
|
89
|
+
it "unescapes escaped /'s, iff they tell us that they need to be unescaped" do
|
90
|
+
/a\/b/.to_plain_s. should == 'a/b'
|
91
|
+
%r#a\#b#.to_plain_s. should == "a\\#b"
|
92
|
+
%r#a\#b#.to_plain_s('#').should == "a#b"
|
93
|
+
%r#a\/b#.to_plain_s. should == 'a/b'
|
94
|
+
%r#a/b#. to_plain_s. should == "a/b"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "unescapes escaped .'s" do
|
98
|
+
/a\.b/.to_plain_s.should == 'a.b'
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'matches the original regexp' do
|
102
|
+
(r=/a/).to_plain_s.should match(r)
|
103
|
+
(r=/(a)bc/).to_plain_s.should match(r)
|
104
|
+
(r=/a?b*c+/).to_plain_s.should match(r)
|
105
|
+
(r=/a{1}bc/).to_plain_s.should match(r)
|
106
|
+
(r=/(a) \(b\) \\(c\\)/).to_plain_s.should match(r)
|
107
|
+
(r=/(a) \(b\) \\(c\\) \\\(d\\\) \\\\(e\\\\) \\\\\(f\\\\\)/).to_plain_s.should match(r)
|
108
|
+
|
109
|
+
# Cannot handle the following cases:
|
110
|
+
#(r=/a{3}bc/).to_plain_s.should match(r)
|
111
|
+
#(r=/a{1,3}bc/).to_plain_s.should match(r)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
=end
|
116
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
#--
|
2
|
+
# Credits::
|
3
|
+
# * Tom Locke (http://hobocentral.net/blog/2007/08/25/quiz/) -- original version
|
4
|
+
# * coderrr (http://coderrr.wordpress.com/2007/09/15/the-ternary-destroyer/) -- some optimizations
|
5
|
+
# * Tyler Rick -- packaged it, added some documention, and added some tests
|
6
|
+
# Copyright:: ?
|
7
|
+
# License:: ?
|
8
|
+
# Submit to Facets?:: Yes
|
9
|
+
# Developer notes::
|
10
|
+
#++
|
11
|
+
|
12
|
+
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
13
|
+
require 'singleton'
|
14
|
+
require 'rubygems'
|
15
|
+
require 'builder/blankslate'
|
16
|
+
#require 'facets/basicobject'
|
17
|
+
#puts Object.methods.include?(:blank_slate_method_added) # not there?
|
18
|
+
|
19
|
+
|
20
|
+
class Object
|
21
|
+
# not sure if receiver responds_to?
|
22
|
+
def _r?(*args)
|
23
|
+
if respond_to(args[0])
|
24
|
+
send *args
|
25
|
+
else
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
# _____ _
|
34
|
+
# |_ _|__ ___| |_
|
35
|
+
# | |/ _ \/ __| __|
|
36
|
+
# | | __/\__ \ |_
|
37
|
+
# |_|\___||___/\__|
|
38
|
+
#
|
39
|
+
=begin test
|
40
|
+
require 'test/unit'
|
41
|
+
|
42
|
+
class TheTest < Test::Unit::TestCase
|
43
|
+
def test_simple__nil
|
44
|
+
hash = {}
|
45
|
+
assert_equal nil, hash[:a]._?.length
|
46
|
+
end
|
47
|
+
def test_simple__normal_objects
|
48
|
+
hash = {:a => 'abc'}
|
49
|
+
assert_equal 3, hash[:a]._?.length
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_chaining
|
53
|
+
hash = {}
|
54
|
+
assert_equal nil, hash[:a]._?.foo
|
55
|
+
assert_equal nil, hash[:a]._?[:b]._?[:c]
|
56
|
+
assert_equal nil, hash[:a]._?[:b]._?[:c]._?.some_method
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_inspect
|
60
|
+
assert_equal 'SafeNil', nil._?.inspect
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_nil?
|
64
|
+
assert SafeNil.instance.nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_does_not_permanently_modify_nil_class
|
68
|
+
assert_raise(NoMethodError) { nil.foo }
|
69
|
+
nil._?
|
70
|
+
assert_raise(NoMethodError) { nil.foo }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
=end
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
# Alternative version #1:
|
78
|
+
# This version is so nice and simple -- I wish we could use it...
|
79
|
+
# The problem with this implementation, however, is that it causes NilClass to be permanently modified with this new method_missing behavior.
|
80
|
+
# In other words, once you call _? once on nil, all instances of nil will forever behave the same as nil._? ! (So you may as well just modify NilClass
|
81
|
+
# directly.)
|
82
|
+
# This might have to do with the fact that nil itself is a “singleton object” (according to http://corelib.rubyonrails.org/classes/NilClass.html)
|
83
|
+
# Proof:
|
84
|
+
# puts nil.foo rescue p $! # => <NoMethodError: undefined method `foo' for nil:NilClass>
|
85
|
+
# nil._?
|
86
|
+
# puts nil.foo rescue p $! # => nil
|
87
|
+
# Too bad... it was so simple.
|
88
|
+
#
|
89
|
+
=begin
|
90
|
+
class NilClass
|
91
|
+
def _?
|
92
|
+
n = nil
|
93
|
+
def n.method_missing(*args)
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
n
|
97
|
+
end
|
98
|
+
end
|
99
|
+
=end
|
100
|
+
|