qualitysmith_extensions 0.0.34 → 0.0.49
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/lib/qualitysmith_extensions/array/shell_escape.rb +2 -2
- data/lib/qualitysmith_extensions/kernel/remove_const.rb +178 -0
- data/lib/qualitysmith_extensions/kernel/remove_module.rb +127 -0
- data/lib/qualitysmith_extensions/module/ancestry_of_instance_method.rb +43 -0
- data/lib/qualitysmith_extensions/module/attribute_accessors.rb +3 -0
- data/lib/qualitysmith_extensions/module/basename.rb +76 -0
- data/lib/qualitysmith_extensions/module/class_methods.rb +87 -0
- data/lib/qualitysmith_extensions/module/create.rb +315 -0
- data/lib/qualitysmith_extensions/module/dirname.rb +4 -0
- data/lib/qualitysmith_extensions/module/guard_method.rb +0 -1
- data/lib/qualitysmith_extensions/module/join.rb +66 -0
- data/lib/qualitysmith_extensions/module/module_methods.rb +4 -0
- data/lib/qualitysmith_extensions/module/namespace.rb +111 -0
- data/lib/qualitysmith_extensions/module/parents.rb +61 -0
- data/lib/qualitysmith_extensions/module/remove_const.rb +117 -0
- data/lib/qualitysmith_extensions/module/split.rb +55 -0
- data/lib/qualitysmith_extensions/object/ancestry_of_method.rb +257 -0
- data/lib/qualitysmith_extensions/object/methods.rb +7 -2
- data/lib/qualitysmith_extensions/string/constantize.rb +4 -0
- data/lib/qualitysmith_extensions/string/shell_escape.rb +1 -1
- data/lib/qualitysmith_extensions/symbol/constantize.rb +69 -0
- data/lib/qualitysmith_extensions/template.rb +1 -0
- data/lib/qualitysmith_extensions/test/assert_anything.rb +93 -0
- metadata +19 -3
- data/lib/qualitysmith_extensions/class/class_methods.rb +0 -5
@@ -2,11 +2,11 @@
|
|
2
2
|
# Author:: Tyler Rick
|
3
3
|
# Copyright:: Copyright (c) 2007 QualitySmith, Inc.
|
4
4
|
# License:: Ruby License
|
5
|
-
# Submit to Facets?:: Yes.
|
5
|
+
# Submit to Facets?:: Yes.
|
6
6
|
#++
|
7
7
|
|
8
8
|
require 'rubygems'
|
9
|
-
require '
|
9
|
+
require 'facets/core/symbol/to_proc' unless Symbol.method_defined?(:to_proc)
|
10
10
|
require 'facets/core/kernel/require_local'
|
11
11
|
require_local '../string/shell_escape.rb'
|
12
12
|
|
@@ -0,0 +1,178 @@
|
|
1
|
+
#--
|
2
|
+
# Author:: Tyler Rick
|
3
|
+
# Copyright:: Copyright (c) 2007 QualitySmith, Inc.
|
4
|
+
# License:: Ruby License
|
5
|
+
# Submit to Facets?:: Yes!
|
6
|
+
# Developer notes::
|
7
|
+
#++
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'qualitysmith_extensions/object/ignore_access'
|
11
|
+
require 'qualitysmith_extensions/module/split'
|
12
|
+
require 'facets/core/module/by_name'
|
13
|
+
require 'facets/core/module/modspace'
|
14
|
+
|
15
|
+
class Module
|
16
|
+
alias_method :remove_const_before_was_added_to_Kernel, :remove_const
|
17
|
+
end
|
18
|
+
module Kernel
|
19
|
+
# This is similar to the built-in <tt>Module#remove_const</tt>, but it is accessible from all "levels" (because it is defined
|
20
|
+
# in +Kernel+) and can handle hierarchy.
|
21
|
+
#
|
22
|
+
# Makes it possible to write simply:
|
23
|
+
# remove_const(A::B::C.name)
|
24
|
+
# rather than having to think about which module the constant is actually defined in and calling +remove_const+ on that module.
|
25
|
+
#
|
26
|
+
# This is how you would otherwise have to do it:
|
27
|
+
# A::B.send(:remove_const, :C)
|
28
|
+
#
|
29
|
+
# +const_name+ must be an object that responds to +to_s+.
|
30
|
+
#
|
31
|
+
# +const_name+ must be a <i>fully qualified name</i>. For example, this will not work as expected:
|
32
|
+
#
|
33
|
+
# module Mod
|
34
|
+
# Foo = 'foo'
|
35
|
+
# remove_const(:Foo)
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# because it will try to remove ::Foo instead of Mod::Foo. Fortunately, however, this will work as expected:
|
39
|
+
#
|
40
|
+
# module Mod
|
41
|
+
# Foo = 'foo'
|
42
|
+
# remove_const(Foo.name)
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# This method is partially inspired by Facets' Kernel#constant method, which provided a more user-friendly alternative to const_get.
|
46
|
+
#
|
47
|
+
def remove_const(const_name)
|
48
|
+
#require 'pp'
|
49
|
+
#puts "remove_const(#{const_name})"
|
50
|
+
raise ArgumentError unless const_name.respond_to?(:to_s)
|
51
|
+
nesting = const_name.to_s.split(/::/).map(&:to_sym)
|
52
|
+
if nesting.size > 1
|
53
|
+
parent_module = constant(nesting[0..-2].join('::')) # For example, would be A::B for A::B::C
|
54
|
+
const_to_remove = nesting[-1] # For example, would be :C for A::B::C
|
55
|
+
parent_module.ignore_access.remove_const_before_was_added_to_Kernel(const_to_remove)
|
56
|
+
else
|
57
|
+
ignore_access.remove_const_before_was_added_to_Kernel(const_name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#p Module.private_instance_methods.grep(/remove_const/) # Lists it
|
63
|
+
Module.send(:remove_method, :remove_const)
|
64
|
+
#p Module.instance_methods.grep(/remove_const/) # Does list it, because inherits *public* remove_const from Kernel
|
65
|
+
#p Module.private_instance_methods.grep(/remove_const/) # Does not list it, because it's no longer private
|
66
|
+
Module.send(:define_method, :remove_const, Kernel.method(:remove_const))
|
67
|
+
#p Module.private_instance_methods.grep(/remove_const/) # Lists it
|
68
|
+
|
69
|
+
# _____ _
|
70
|
+
# |_ _|__ ___| |_
|
71
|
+
# | |/ _ \/ __| __|
|
72
|
+
# | | __/\__ \ |_
|
73
|
+
# |_|\___||___/\__|
|
74
|
+
#
|
75
|
+
=begin test
|
76
|
+
require 'test/unit'
|
77
|
+
|
78
|
+
# Important regression test. This was failing at one point.
|
79
|
+
module A
|
80
|
+
B = nil
|
81
|
+
remove_const :B
|
82
|
+
end
|
83
|
+
|
84
|
+
# How it would be done *without* this extension:
|
85
|
+
module TestRemoveABC_TheOldWay
|
86
|
+
module A
|
87
|
+
module B
|
88
|
+
C = 'foo'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class TheTest < Test::Unit::TestCase
|
93
|
+
def test_1
|
94
|
+
assert_nothing_raised { A::B::C }
|
95
|
+
A::B.send(:remove_const, :C)
|
96
|
+
assert_raise(NameError) { A::B::C }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# How it would be done *with* this extension (all tests that follow):
|
102
|
+
|
103
|
+
module TestRemoveABC_CIsString
|
104
|
+
module A
|
105
|
+
module B
|
106
|
+
C = 'foo'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class TheTest < Test::Unit::TestCase
|
111
|
+
def test_1
|
112
|
+
assert_nothing_raised { A::B::C }
|
113
|
+
assert_raise(NoMethodError) { remove_const(A::B::C.name) } # Because C is a *string*, not a *module*
|
114
|
+
assert_nothing_raised { remove_const A::B.name + '::C' }
|
115
|
+
assert_raise(NameError) { A::B::C }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
module TestRemoveAB_UsingName
|
121
|
+
module A
|
122
|
+
module B
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class TheTest < Test::Unit::TestCase
|
127
|
+
def test_1
|
128
|
+
assert_nothing_raised { A::B }
|
129
|
+
remove_const(A::B.name)
|
130
|
+
assert_raise(NameError) { A::B }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
module TestRemoveAB_Symbol
|
136
|
+
module A
|
137
|
+
module B
|
138
|
+
Foo = :Foo
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
remove_const(:'A::B::Foo') # This tests that Module#remove_const was overriden as well.
|
143
|
+
# If we hadn't also overriden Module#remove_const, then this would have caused this error:
|
144
|
+
# in `remove_const': `A::B::Foo' is not allowed as a constant name (NameError)
|
145
|
+
|
146
|
+
class TheTest < Test::Unit::TestCase
|
147
|
+
def test_1
|
148
|
+
assert_nothing_raised { A::B }
|
149
|
+
|
150
|
+
assert_equal 'TestRemoveAB_Symbol::A', A.name
|
151
|
+
assert_raise(NameError) { remove_const(:'A::B') } # This doesn't work because A, when evaluated in this context,
|
152
|
+
# is TestRemoveAB_Symbol::TheTest::A, which is *not* defined.
|
153
|
+
|
154
|
+
remove_const(:'TestRemoveAB_Symbol::A::B')
|
155
|
+
assert_raise(NameError) { A::B }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
module TestRemoveAB_Symbol2
|
161
|
+
class TheTest < Test::Unit::TestCase
|
162
|
+
module A
|
163
|
+
module B
|
164
|
+
end
|
165
|
+
end
|
166
|
+
def test_1
|
167
|
+
assert_nothing_raised { A::B }
|
168
|
+
|
169
|
+
assert_equal 'TestRemoveAB_Symbol2::TheTest::A', A.name
|
170
|
+
remove_const(:'A::B') # Does work, because A is defined *within* TheTest this time.
|
171
|
+
assert_raise(NameError) { A::B }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
=end
|
177
|
+
|
178
|
+
|
@@ -0,0 +1,127 @@
|
|
1
|
+
#--
|
2
|
+
# Author:: Tyler Rick
|
3
|
+
# Copyright:: Copyright (c) 2007 QualitySmith, Inc.
|
4
|
+
# License:: Ruby License
|
5
|
+
# Submit to Facets?:: No.
|
6
|
+
# Developer notes::
|
7
|
+
# * Deprecated by qualitysmith_extensions/module/remove.rb
|
8
|
+
#++
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
require 'qualitysmith_extensions/object/ignore_access'
|
12
|
+
require 'qualitysmith_extensions/module/split'
|
13
|
+
require 'facets/core/module/by_name'
|
14
|
+
require 'facets/core/module/modspace'
|
15
|
+
|
16
|
+
module Kernel
|
17
|
+
# This is similar to +remove_const+, but it _only_ works for modules/classes.
|
18
|
+
#
|
19
|
+
# This is similar to the built-in <tt>Module#remove_module</tt>, but it is accessible from all "levels" (because it is defined
|
20
|
+
# in +Kernel+) and can handle hierarchy.
|
21
|
+
#
|
22
|
+
# Makes it possible to write simply:
|
23
|
+
# remove_module(A::B::C)
|
24
|
+
# rather than having to think about which module the constant is actually defined in and calling +remove_const+ on that module.
|
25
|
+
# This is how you would have to otherwise do it:
|
26
|
+
# A::B.send(:remove_const, :C)
|
27
|
+
#
|
28
|
+
# You can pass in either a constant or a symbol. Passing in a constant is preferred
|
29
|
+
#
|
30
|
+
# This method is partially inspired by Facets' Kernel#constant method, which provided a more user-friendly alternative to const_get.
|
31
|
+
#
|
32
|
+
def remove_module(const)
|
33
|
+
const = Module.by_name(const.to_s) if const.is_a?(Symbol)
|
34
|
+
if const.split.size > 1
|
35
|
+
parent_module = const.modspace # For example, would be A::B for A::B::C
|
36
|
+
const_to_remove = const.split.last # For example, would be :C for A::B::C
|
37
|
+
parent_module.ignore_access.remove_const(const_to_remove)
|
38
|
+
else
|
39
|
+
Object.ignore_access.remove_const(const.name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# _____ _
|
45
|
+
# |_ _|__ ___| |_
|
46
|
+
# | |/ _ \/ __| __|
|
47
|
+
# | | __/\__ \ |_
|
48
|
+
# |_|\___||___/\__|
|
49
|
+
#
|
50
|
+
=begin test
|
51
|
+
require 'test/unit'
|
52
|
+
require 'qualitysmith_extensions/kernel/remove_const' # Test for compatibility. Just in case the remove_const_before_was_added_to_Kernel alias might have thrown something off.
|
53
|
+
|
54
|
+
# How it would be done *without* this extension:
|
55
|
+
module TestRemoveABC_TheOldWay
|
56
|
+
module A
|
57
|
+
module B
|
58
|
+
class C
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class TheTest < Test::Unit::TestCase
|
64
|
+
def test_1
|
65
|
+
assert_nothing_raised { A::B::C }
|
66
|
+
A::B.send(:remove_const, :C)
|
67
|
+
assert_raise(NameError) { A::B::C }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# How it would be done *with* this extension:
|
73
|
+
module TestRemoveABC
|
74
|
+
module A
|
75
|
+
module B
|
76
|
+
class C
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class TheTest < Test::Unit::TestCase
|
82
|
+
def test_1
|
83
|
+
assert_nothing_raised { A::B::C }
|
84
|
+
remove_module(A::B::C)
|
85
|
+
assert_raise(NameError) { A::B::C }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module TestRemoveAB
|
91
|
+
module A
|
92
|
+
module B
|
93
|
+
module C
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class TheTest < Test::Unit::TestCase
|
99
|
+
def test_1
|
100
|
+
assert_nothing_raised { A::B }
|
101
|
+
remove_module(A::B)
|
102
|
+
assert_raise(NameError) { A::B }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module TestRemoveAB_Symbol
|
108
|
+
module A
|
109
|
+
module B
|
110
|
+
module C
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class TheTest < Test::Unit::TestCase
|
116
|
+
def test_1
|
117
|
+
assert_nothing_raised { A::B }
|
118
|
+
assert_raise(NameError) { remove_module(:'A::B') } # This is why passing in the module itself is preferred.
|
119
|
+
remove_module(:'TestRemoveAB_Symbol::A::B')
|
120
|
+
assert_raise(NameError) { A::B }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
=end
|
126
|
+
|
127
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#--
|
2
|
+
# Author:: Tyler Rick
|
3
|
+
# Copyright:: Copyright (c) 2007 QualitySmith, Inc.
|
4
|
+
# License:: Ruby License
|
5
|
+
# Submit to Facets?:: Yes.
|
6
|
+
# Developer notes::
|
7
|
+
# * May not have taken every single case into consideration. Needs a bit more testing.
|
8
|
+
# * public/private/protected?
|
9
|
+
# * Tests for this method can be found in ../object/ancestry_of_method.rb . It made more sense to test those two methods together;
|
10
|
+
# yet it's still to _use_ module/ancestry_of_instance_method.rb without using ../object/ancestry_of_method.rb.
|
11
|
+
#++
|
12
|
+
|
13
|
+
class Module
|
14
|
+
|
15
|
+
# Returns the module/class which defined the given instance method. If more than one module/class defined the method, returns the _closest_
|
16
|
+
# ancestor to have defined it (would be +self+ if it is defined in +self+).
|
17
|
+
#
|
18
|
+
# This looks at the results of <tt>instance_methods</tt>, which means that if you call this on a module/class, it will _only_ look
|
19
|
+
# at _instance_ methods. Thus, (unlike +ancestry_of_method+) it _only_ makes sense to call this method on modules/classes,
|
20
|
+
# not _instances_ of those modules/classes.
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
# class Base
|
24
|
+
# def it; end
|
25
|
+
# end
|
26
|
+
# class SubWithIt < Base
|
27
|
+
# def it; end
|
28
|
+
# end
|
29
|
+
# class SubWithoutIt < Base
|
30
|
+
# end
|
31
|
+
# SubWithIt.ancestors # => [SubWithIt, Base, Object, Kernel]
|
32
|
+
# SubWithIt.ancestry_of_instance_method(:it) # => SubWithIt # (Stops with self)
|
33
|
+
# SubWithoutIt.ancestry_of_instance_method(:it) # => Base # (Goes one step up the ancestry tree)
|
34
|
+
#
|
35
|
+
# Returns nil if it cannot be found in self or in any ancestor.
|
36
|
+
def ancestry_of_instance_method(method_name)
|
37
|
+
method_name = method_name.to_s
|
38
|
+
self.ancestors.find do |ancestor|
|
39
|
+
ancestor.instance_methods(false).include? method_name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -20,6 +20,7 @@ class Module # :nodoc:
|
|
20
20
|
EOS
|
21
21
|
end
|
22
22
|
end
|
23
|
+
alias_method :cattr_reader, :mattr_reader
|
23
24
|
|
24
25
|
def mattr_writer(*syms)
|
25
26
|
syms.each do |sym|
|
@@ -38,9 +39,11 @@ class Module # :nodoc:
|
|
38
39
|
EOS
|
39
40
|
end
|
40
41
|
end
|
42
|
+
alias_method :cattr_writer, :mattr_writer
|
41
43
|
|
42
44
|
def mattr_accessor(*syms)
|
43
45
|
mattr_reader(*syms)
|
44
46
|
mattr_writer(*syms)
|
45
47
|
end
|
48
|
+
alias_method :cattr_accessor, :mattr_accessor
|
46
49
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#--
|
2
|
+
# Author:: Tyler Rick
|
3
|
+
# Copyright:: Copyright (c) 2007 QualitySmith, Inc.
|
4
|
+
# License:: Ruby License
|
5
|
+
# Submit to Facets?:: Yes.
|
6
|
+
# Developer notes::
|
7
|
+
# Changes::
|
8
|
+
#++
|
9
|
+
|
10
|
+
|
11
|
+
require 'rubygems'
|
12
|
+
require 'facets/core/module/basename'
|
13
|
+
require 'facets/core/string/basename'
|
14
|
+
require 'qualitysmith_extensions/module/namespace'
|
15
|
+
|
16
|
+
class Module
|
17
|
+
# Gets the basename of a "module path" (the name of the module without any of the namespace modules that it is contained in),
|
18
|
+
# in the same sense that <tt>File.basename</tt> returns the basename of a _filesystem_ path.
|
19
|
+
#
|
20
|
+
# This is identical to Facets' String#basename ('facets/core/string/basename') except that:
|
21
|
+
# * it is a class method instead of an instance method of String,
|
22
|
+
# * it accepts modules, strings, and symbols.
|
23
|
+
#
|
24
|
+
# See also <tt>Module.dirname</tt>/<tt>Module.namespace_name_of</tt>.
|
25
|
+
#
|
26
|
+
# These can be used together, such that the following is always true:
|
27
|
+
# OuterModule::MiddleModule::InnerModule == Module.join(Module.dirname(some_module), Module.basename(some_module)).constantize
|
28
|
+
#
|
29
|
+
def self.basename(module_or_name)
|
30
|
+
case module_or_name
|
31
|
+
when Module
|
32
|
+
module_or_name.basename
|
33
|
+
when Symbol
|
34
|
+
module_or_name.to_s.basename
|
35
|
+
when String
|
36
|
+
module_or_name.basename
|
37
|
+
else
|
38
|
+
raise ArgumentError
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
# _____ _
|
47
|
+
# |_ _|__ ___| |_
|
48
|
+
# | |/ _ \/ __| __|
|
49
|
+
# | | __/\__ \ |_
|
50
|
+
# |_|\___||___/\__|
|
51
|
+
#
|
52
|
+
=begin test
|
53
|
+
require 'test/unit'
|
54
|
+
|
55
|
+
module OuterModule; end
|
56
|
+
module OuterModule::MiddleModule; end
|
57
|
+
module OuterModule::MiddleModule::InnerModule; end
|
58
|
+
|
59
|
+
class BasenameTest < Test::Unit::TestCase
|
60
|
+
def test_simple
|
61
|
+
assert_equal 'OuterModule', Module.basename(OuterModule)
|
62
|
+
assert_equal 'OuterModule', Module.basename(:OuterModule)
|
63
|
+
assert_equal 'OuterModule', Module.basename('OuterModule')
|
64
|
+
end
|
65
|
+
def test_nesting
|
66
|
+
assert_equal 'InnerModule',
|
67
|
+
Module.basename(OuterModule::MiddleModule::InnerModule)
|
68
|
+
assert_equal 'InnerModule',
|
69
|
+
Module.basename(:'OuterModule::MiddleModule::InnerModule')
|
70
|
+
assert_equal 'InnerModule',
|
71
|
+
Module.basename('OuterModule::MiddleModule::InnerModule')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
=end
|
75
|
+
|
76
|
+
|