facets 2.7.0 → 2.8.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.
- data/HISTORY.rdoc +135 -294
- data/MANIFEST +40 -91
- data/NOTES +1 -1
- data/README.rdoc +10 -8
- data/Rakefile +11 -34
- data/demo/{hook.rd → hook.rdoc} +2 -0
- data/demo/{scenario_require.rd → scenario_require.rdoc} +3 -0
- data/lib/core/facets-live.rb +7 -5
- data/lib/core/facets.rb +379 -359
- data/lib/core/facets/array/conjoin.rb +2 -2
- data/lib/core/facets/array/pad.rb +1 -1
- data/lib/core/facets/array/recursively.rb +2 -2
- data/lib/core/facets/array/splice.rb +1 -1
- data/lib/core/facets/binding/caller.rb +2 -4
- data/lib/core/facets/comparable/comparable.rb +2 -2
- data/lib/core/facets/dir/ascend.rb +3 -0
- data/lib/core/facets/dir/recurse.rb +4 -0
- data/lib/core/facets/duplicable.rb +6 -8
- data/lib/core/facets/enumerable/count.rb +22 -13
- data/lib/core/facets/enumerable/map_detect.rb +28 -0
- data/lib/core/facets/enumerable/mash.rb +13 -5
- data/lib/core/facets/enumerable/per.rb +3 -1
- data/lib/core/facets/hash/count.rb +14 -0
- data/lib/core/facets/hash/data.rb +14 -0
- data/lib/core/facets/kernel/__method__.rb +1 -1
- data/lib/core/facets/kernel/d.rb +9 -8
- data/lib/core/facets/kernel/eigenclass.rb +20 -0
- data/lib/core/facets/kernel/extend.rb +10 -0
- data/lib/core/facets/kernel/instance_class.rb +1 -0
- data/lib/core/facets/kernel/instance_variables.rb +6 -6
- data/lib/core/facets/kernel/meta_alias.rb +18 -0
- data/lib/core/facets/kernel/meta_class.rb +17 -0
- data/lib/core/facets/kernel/meta_def.rb +18 -0
- data/lib/core/facets/kernel/meta_eval.rb +18 -0
- data/lib/core/facets/kernel/object_hexid.rb +21 -6
- data/lib/core/facets/kernel/object_state.rb +4 -2
- data/lib/core/facets/kernel/populate.rb +3 -1
- data/lib/core/facets/kernel/with.rb +1 -1
- data/lib/core/facets/metaid.rb +6 -93
- data/lib/core/facets/module/class_def.rb +2 -0
- data/lib/core/facets/module/extend.rb +10 -11
- data/lib/core/facets/module/is.rb +5 -5
- data/lib/core/facets/module/module_def.rb +31 -0
- data/lib/core/facets/string/camelcase.rb +14 -12
- data/lib/core/facets/string/cleanlines.rb +35 -0
- data/lib/core/facets/string/edit_distance.rb +62 -0
- data/lib/core/facets/string/indent.rb +86 -4
- data/lib/core/facets/string/index_all.rb +24 -0
- data/lib/core/facets/string/lines.rb +3 -6
- data/lib/core/facets/string/margin.rb +2 -1
- data/lib/core/facets/string/newlines.rb +35 -0
- data/lib/core/facets/string/op_div.rb +14 -0
- data/lib/core/facets/string/range.rb +2 -22
- data/lib/core/facets/string/range_all.rb +1 -0
- data/lib/core/facets/string/range_of_line.rb +1 -0
- data/lib/core/facets/string/similarity.rb +92 -0
- data/lib/core/facets/string/start_with.rb +6 -6
- data/lib/core/facets/string/titlecase.rb +1 -1
- data/lib/more/facets/basicobject.rb +16 -15
- data/lib/more/facets/blankslate.rb +8 -0
- data/lib/more/facets/class_extend.rb +126 -1
- data/lib/more/facets/continuation.rb +53 -54
- data/lib/more/facets/dictionary.rb +9 -63
- data/lib/more/facets/erb.rb +63 -0
- data/lib/more/facets/filelist.rb +5 -5
- data/lib/more/facets/hashbuilder.rb +101 -0
- data/lib/more/facets/inheritor.rb +36 -45
- data/lib/more/facets/ini.rb +267 -0
- data/lib/more/facets/instance_eval.rb +4 -4
- data/lib/more/facets/ioredirect.rb +7 -60
- data/lib/more/facets/linkedlist.rb +195 -0
- data/lib/more/facets/matcher.rb +140 -0
- data/lib/more/facets/memoizer.rb +64 -0
- data/lib/more/facets/methodspace.rb +9 -4
- data/lib/more/facets/module/class_extend.rb +2 -121
- data/lib/more/facets/ostruct.rb +9 -9
- data/lib/more/facets/pathlist.rb +1 -9
- data/lib/more/facets/pathname.rb +11 -4
- data/lib/more/facets/plugin_manager.rb +50 -0
- data/lib/more/facets/random.rb +25 -3
- data/lib/more/facets/roman.rb +174 -0
- data/lib/more/facets/semaphore.rb +92 -0
- data/lib/more/facets/shellwords.rb +21 -48
- data/lib/more/facets/succ.rb +1 -1
- data/meta/{modified → released} +0 -0
- data/meta/repository +1 -0
- data/meta/suite +1 -0
- data/meta/version +1 -1
- data/script/conflicts +63 -0
- data/script/methods +49 -0
- data/test/core/binding/test_caller.rb +11 -4
- data/test/core/enumerable/test_count.rb +19 -10
- data/test/core/enumerable/test_map_detect.rb +75 -0
- data/test/core/enumerable/test_take.rb +1 -1
- data/test/core/kernel/test_object_hexid.rb +2 -1
- data/test/core/proc/test_to_method.rb +1 -1
- data/test/core/string/test_cleanlines.rb +11 -0
- data/test/core/string/test_indent.rb +66 -4
- data/test/core/string/test_lines.rb +2 -1
- data/test/core/string/test_newlines.rb +13 -0
- data/test/core/time/test_change.rb +1 -1
- data/test/core/time/test_stamp.rb +4 -7
- data/test/core/unboundmethod/test_name.rb +1 -1
- data/test/more/test_basicobject.rb +1 -20
- data/test/more/test_class_extend.rb +7 -0
- data/test/more/test_continuation.rb +8 -6
- data/test/more/test_inheritor.rb +12 -6
- data/test/more/test_random.rb +19 -10
- data/test/more/test_shellwords.rb +33 -0
- metadata +60 -31
- data/TODO +0 -5
- data/doc/README.core +0 -102
- data/doc/README.more +0 -61
- data/doc/manual/about.rb +0 -47
- data/doc/manual/annotations.rdoc +0 -60
- data/doc/manual/associations.rdoc +0 -55
- data/doc/manual/blockups.rdoc +0 -101
- data/doc/manual/capsule.rdoc +0 -34
- data/doc/manual/command.rdoc +0 -177
- data/doc/manual/core.rdoc +0 -37
- data/doc/manual/faq.rdoc +0 -32
- data/doc/manual/typecast.html +0 -112
- data/lib/more/facets/capsule.rb +0 -258
- data/lib/more/facets/coroutine.rb +0 -159
- data/lib/more/facets/enumerablepass.rb +0 -3
- data/lib/more/facets/fileable.rb +0 -162
- data/lib/more/facets/progressbar.rb +0 -253
- data/lib/more/facets/recorder.rb +0 -108
- data/meta/releases +0 -14
- data/test/more/test_coroutine.rb +0 -46
@@ -0,0 +1,24 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
# Like index but returns an array of all index locations.
|
4
|
+
# The reuse flag allows the trailing portion of a match to be
|
5
|
+
# reused for subsquent matches.
|
6
|
+
#
|
7
|
+
# "abcabcabc".index_all('a') #=> [0,3,6]
|
8
|
+
#
|
9
|
+
# "bbb".index_all('bb', false) #=> [0]
|
10
|
+
# "bbb".index_all('bb', true) #=> [0,1]
|
11
|
+
#
|
12
|
+
# TODO: Culd probably be defined for Indexable in general too.
|
13
|
+
|
14
|
+
def index_all(s, reuse=false)
|
15
|
+
s = Regexp.new(Regexp.escape(s)) unless Regexp===s
|
16
|
+
ia = []; i = 0
|
17
|
+
while (i = index(s,i))
|
18
|
+
ia << i
|
19
|
+
i += (reuse ? 1 : $~[0].size)
|
20
|
+
end
|
21
|
+
ia
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -4,16 +4,13 @@ class String
|
|
4
4
|
|
5
5
|
# Returns an array of characters.
|
6
6
|
#
|
7
|
-
# "abc\n123".lines #=> ["abc","123"]
|
8
|
-
#
|
9
|
-
# Note, this is not 100% compatible with 1.8.7+
|
10
|
-
# which returns an enumerator instead of an array.
|
7
|
+
# "abc\n123".lines #=> ["abc\n","123"]
|
11
8
|
#
|
12
9
|
def lines(&blk)
|
13
10
|
if block_given?
|
14
|
-
|
11
|
+
each_line(&blk) #scan(/$.*?\n/).each(&blk)
|
15
12
|
else
|
16
|
-
self
|
13
|
+
Enumerator.new(self, :lines) #.split(/\n/)
|
17
14
|
end
|
18
15
|
end
|
19
16
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "facets/enumerator"
|
2
|
+
|
3
|
+
class String
|
4
|
+
|
5
|
+
# Returns an Enumerator for iterating over each
|
6
|
+
# line of the string, void of the termining newline
|
7
|
+
# character, in contrast to #lines which retains it.
|
8
|
+
#
|
9
|
+
def newlines(&block)
|
10
|
+
if block
|
11
|
+
scan(/^.*?$/) do |line|
|
12
|
+
block.call(line.chomp)
|
13
|
+
end
|
14
|
+
else
|
15
|
+
Enumerator.new(self) do |output|
|
16
|
+
scan(/^.*?$/) do |line|
|
17
|
+
output.yield(line.chomp)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
=begin test
|
26
|
+
|
27
|
+
"a\nb\nc".newlines.class.must == Enumerator
|
28
|
+
"a\nb\nc".newlines.to_a.must == %w{a b c}
|
29
|
+
|
30
|
+
a = []
|
31
|
+
"a\nb\nc".newlines{|nl| a << nl}
|
32
|
+
a.must == %w{a b c}
|
33
|
+
|
34
|
+
=end
|
35
|
+
|
@@ -45,32 +45,12 @@ class String
|
|
45
45
|
|
46
46
|
def range_of_line
|
47
47
|
offset=0; charmap = []
|
48
|
-
|
48
|
+
each_line do |line|
|
49
49
|
charmap << (offset..(offset + line.length - 1))
|
50
50
|
offset += line.length
|
51
51
|
end
|
52
52
|
charmap
|
53
53
|
end
|
54
54
|
|
55
|
-
# Like index but returns an array of all index locations.
|
56
|
-
# The reuse flag allows the trailing portion of a match to be
|
57
|
-
# reused for subsquent matches.
|
58
|
-
#
|
59
|
-
# "abcabcabc".index_all('a') #=> [0,3,6]
|
60
|
-
#
|
61
|
-
# "bbb".index_all('bb', false) #=> [0]
|
62
|
-
# "bbb".index_all('bb', true) #=> [0,1]
|
63
|
-
#
|
64
|
-
# TODO: Culd probably be defined for Indexable in general too.
|
65
|
-
|
66
|
-
def index_all(s, reuse=false)
|
67
|
-
s = Regexp.new(Regexp.escape(s)) unless Regexp===s
|
68
|
-
ia = []; i = 0
|
69
|
-
while (i = index(s,i))
|
70
|
-
ia << i
|
71
|
-
i += (reuse ? 1 : $~[0].size)
|
72
|
-
end
|
73
|
-
ia
|
74
|
-
end
|
75
|
-
|
76
55
|
end
|
56
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/string/range'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/string/range'
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#require 'facets/string/cmp'
|
2
|
+
#require 'facets/blank'
|
3
|
+
#require 'facets/string/natcmp'
|
4
|
+
|
5
|
+
class String
|
6
|
+
|
7
|
+
# A fuzzy matching mechanism. Returns a score from 0-1,
|
8
|
+
# based on the number of shared edges.
|
9
|
+
# To be effective, the strings must be of length 2 or greater.
|
10
|
+
#
|
11
|
+
# "Alexsander".fuzzy_match( "Aleksander" ) #=> 0.9
|
12
|
+
#
|
13
|
+
# The way it works:
|
14
|
+
#
|
15
|
+
# * Converts each string into a "graph like" object, with edges
|
16
|
+
# "alexsander" -> [ alexsander, alexsand, alexsan ... lexsand ... san ... an, etc ]
|
17
|
+
# "aleksander" -> [ aleksander, aleksand ... etc. ]
|
18
|
+
# * Perform match, then remove any subsets from this matched set (i.e. a hit
|
19
|
+
# on "san" is a subset of a hit on "sander")
|
20
|
+
# Above example, once reduced -> [ ale, sander ]
|
21
|
+
# * See's how many of the matches remain, and calculates a score based
|
22
|
+
# on how many matches, their length, and compare to the length of the
|
23
|
+
# larger of the two words.
|
24
|
+
#
|
25
|
+
# Still a bit rough. Any suggestions for improvement are welcome.
|
26
|
+
#
|
27
|
+
# CREDIT: Derek Lewis.
|
28
|
+
#
|
29
|
+
def similarity(str_in)
|
30
|
+
return 0 if str_in == nil
|
31
|
+
return 1 if self == str_in
|
32
|
+
|
33
|
+
# Make a graph of each word (okay, so its not a true graph, but is similar)
|
34
|
+
graph_A = Array.new
|
35
|
+
graph_B = Array.new
|
36
|
+
|
37
|
+
# "graph" self
|
38
|
+
last = self.length
|
39
|
+
(0..last).each do |ff|
|
40
|
+
loc = self.length
|
41
|
+
break if ff == last - 1
|
42
|
+
wordB = (1..(last-1)).to_a.reverse!
|
43
|
+
if (wordB != nil)
|
44
|
+
wordB.each do |ss|
|
45
|
+
break if ss == ff
|
46
|
+
graph_A.push( "#{self[ff..ss]}" )
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# "graph" input string
|
52
|
+
last = str_in.length
|
53
|
+
(0..last).each{ |ff|
|
54
|
+
loc = str_in.length
|
55
|
+
break if ff == last - 1
|
56
|
+
wordB = (1..(last-1)).to_a.reverse!
|
57
|
+
wordB.each do |ss|
|
58
|
+
break if ss == ff
|
59
|
+
graph_B.push( "#{str_in[ff..ss]}" )
|
60
|
+
end
|
61
|
+
}
|
62
|
+
|
63
|
+
# count how many of these "graph edges" we have that are the same
|
64
|
+
matches = graph_A & graph_B
|
65
|
+
#matches = Array.new
|
66
|
+
#graph_A.each do |aa|
|
67
|
+
# matches.push( aa ) if( graph_B.include?( aa ) )
|
68
|
+
#end
|
69
|
+
|
70
|
+
# For eliminating subsets, we want to start with the smallest hits.
|
71
|
+
matches.sort!{|x,y| x.length <=> y.length}
|
72
|
+
|
73
|
+
# eliminate any subsets
|
74
|
+
mclone = matches.dup
|
75
|
+
mclone.each_index do |ii|
|
76
|
+
reg = Regexp.compile( Regexp.escape(mclone[ii]) )
|
77
|
+
count = 0.0
|
78
|
+
matches.each{|xx| count += 1 if xx =~ reg}
|
79
|
+
matches.delete(mclone[ii]) if count > 1
|
80
|
+
end
|
81
|
+
|
82
|
+
score = 0.0
|
83
|
+
matches.each{ |mm| score += mm.length }
|
84
|
+
self.length > str_in.length ? largest = self.length : largest = str_in.length
|
85
|
+
return score/largest
|
86
|
+
end
|
87
|
+
|
88
|
+
# DEPRECATED alias
|
89
|
+
#alias_method :fuzzy_match, :similarity
|
90
|
+
|
91
|
+
end
|
92
|
+
|
@@ -2,10 +2,10 @@ class String
|
|
2
2
|
|
3
3
|
unless method_defined?(:start_with?) # 1.8.7+
|
4
4
|
|
5
|
-
# Does a string start with the given prefix
|
5
|
+
# Does a string start with the given prefix?
|
6
6
|
#
|
7
|
-
# "hello".
|
8
|
-
# "hello".
|
7
|
+
# "hello".start_with?("he") #=> true
|
8
|
+
# "hello".start_with?("to") #=> false
|
9
9
|
#
|
10
10
|
# CREDIT: Lucas Carlson, Blaine Cook
|
11
11
|
|
@@ -19,8 +19,8 @@ class String
|
|
19
19
|
|
20
20
|
# Does a string end with the given suffix?
|
21
21
|
#
|
22
|
-
# "hello".
|
23
|
-
# "hello".
|
22
|
+
# "hello".end_with?("lo") #=> true
|
23
|
+
# "hello".end_with?("to") #=> false
|
24
24
|
#
|
25
25
|
# CREDIT: Lucas Carlson, Blaine Cook
|
26
26
|
|
@@ -31,7 +31,7 @@ class String
|
|
31
31
|
end
|
32
32
|
|
33
33
|
alias_method :starts_with?, :start_with?
|
34
|
-
alias_method :ends_with? , :
|
34
|
+
alias_method :ends_with? , :end_with?
|
35
35
|
|
36
36
|
end
|
37
37
|
|
@@ -15,20 +15,29 @@ unless defined? BasicObject # just in case it already exists!
|
|
15
15
|
# depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
|
16
16
|
class BasicObject
|
17
17
|
class << self
|
18
|
-
|
19
18
|
# Hide the method named +name+ in the BlankSlate class. Don't
|
20
19
|
# hide +instance_eval+ or any method beginning with "__".
|
20
|
+
#
|
21
|
+
# According to 1.9.1 it should have only these methods:
|
22
|
+
#
|
23
|
+
# * #__send__
|
24
|
+
# * #instance_eval
|
25
|
+
# * #instance_exec
|
26
|
+
# * #equal?
|
27
|
+
# * #==
|
28
|
+
# * #!
|
29
|
+
# * #!=
|
30
|
+
#
|
31
|
+
# Seems to me it should have #__id__ too.
|
21
32
|
def hide(name)
|
22
33
|
undef_method name if
|
23
|
-
|
24
|
-
|
34
|
+
instance_methods.include?(name.to_s) and
|
35
|
+
name !~ /^(__|instance_eval$|instance_exec$|equal\?$|\=\=$)/
|
25
36
|
end
|
26
37
|
end
|
27
|
-
|
28
38
|
instance_methods.each { |m| hide(m) }
|
29
39
|
end
|
30
40
|
|
31
|
-
|
32
41
|
# Since Ruby is very dynamic, methods added to the ancestors of
|
33
42
|
# BlankSlate <em>after BlankSlate is defined</em> will show up in the
|
34
43
|
# list of available BlankSlate methods. We handle this by defining a
|
@@ -42,7 +51,7 @@ unless defined? BasicObject # just in case it already exists!
|
|
42
51
|
def method_added(name)
|
43
52
|
blank_slate_method_added(name)
|
44
53
|
return if self != Kernel
|
45
|
-
|
54
|
+
BasicObject.hide(name)
|
46
55
|
end
|
47
56
|
end
|
48
57
|
end
|
@@ -56,18 +65,10 @@ unless defined? BasicObject # just in case it already exists!
|
|
56
65
|
def method_added(name)
|
57
66
|
blank_slate_method_added(name)
|
58
67
|
return if self != Object
|
59
|
-
|
68
|
+
BasicObject.hide(name)
|
60
69
|
end
|
61
70
|
end
|
62
71
|
end
|
63
72
|
|
64
73
|
end
|
65
74
|
|
66
|
-
unless defined? BlankSlate
|
67
|
-
|
68
|
-
# ActiveSupport compatiable version of BasicObject
|
69
|
-
# if not Ruby 1.9+ uses Jim Weirich's BlankSlate.
|
70
|
-
BlankSlate = BasicObject
|
71
|
-
|
72
|
-
end
|
73
|
-
|
@@ -1,2 +1,10 @@
|
|
1
1
|
warn "Use BasicObject instead of BlankSlate for future versions."
|
2
2
|
require 'facets/basicobject'
|
3
|
+
|
4
|
+
|
5
|
+
unless defined? BlankSlate
|
6
|
+
# ActiveSupport compatiable version of BasicObject
|
7
|
+
# if not Ruby 1.9+ uses Jim Weirich's BlankSlate.
|
8
|
+
BlankSlate = ::BasicObject
|
9
|
+
end
|
10
|
+
|
@@ -1 +1,126 @@
|
|
1
|
-
|
1
|
+
# = Class Extension
|
2
|
+
#
|
3
|
+
# Normally when including modules, class/module methods are not
|
4
|
+
# extended. To achieve this behavior requires some clever
|
5
|
+
# Ruby Karate. Instead class_extension provides an easy to use
|
6
|
+
# and clean solution. Simply place the extending class methods
|
7
|
+
# in a block of the special module method #class_extension.
|
8
|
+
#
|
9
|
+
# module Mix
|
10
|
+
# def inst_meth
|
11
|
+
# puts 'inst_meth'
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# class_extend do
|
15
|
+
# def class_meth
|
16
|
+
# "Class Method!"
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# class X
|
22
|
+
# include Mix
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# X.class_meth #=> "Class Method!"
|
26
|
+
#
|
27
|
+
# == History
|
28
|
+
#
|
29
|
+
# Thanks to Trans, Nobu and Ulysses for their original on this concept.
|
30
|
+
#
|
31
|
+
# == Authors
|
32
|
+
#
|
33
|
+
# * Daniel Schierbeck
|
34
|
+
# * Thomas Sawyer
|
35
|
+
# * Nobu Nakada
|
36
|
+
# * Ulysses
|
37
|
+
#
|
38
|
+
# == Copying
|
39
|
+
#
|
40
|
+
# Copyright (c) 2006 Daniel Schierbeck
|
41
|
+
#
|
42
|
+
# Ruby License
|
43
|
+
#
|
44
|
+
# This module is free software. You may use, modify, and/or redistribute this
|
45
|
+
# software under the same terms as Ruby.
|
46
|
+
#
|
47
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
48
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
49
|
+
# FOR A PARTICULAR PURPOSE.
|
50
|
+
|
51
|
+
#
|
52
|
+
class Module
|
53
|
+
|
54
|
+
alias_method :append_features_without_class_extension, :append_features
|
55
|
+
|
56
|
+
# Normally when including modules, class/module methods are not
|
57
|
+
# extended. To achieve this behavior requires some clever
|
58
|
+
# Ruby Karate. Instead #class_extend provides an easy to use
|
59
|
+
# and clean solution. Simply place the extending class methods
|
60
|
+
# in a block of the special module method #class_extend.
|
61
|
+
#
|
62
|
+
# module Mix
|
63
|
+
# def inst_meth
|
64
|
+
# puts 'inst_meth'
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# class_extend do
|
68
|
+
# def class_meth
|
69
|
+
# "Class Method!"
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# class X
|
75
|
+
# include Mix
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# X.class_meth #=> "Class Method!"
|
79
|
+
#
|
80
|
+
# NOTE: This old #class_extension version of this method
|
81
|
+
# did not extend the containing class automatically --it had
|
82
|
+
# to be done by hand. With #class_extend, that is no longer
|
83
|
+
# the case.
|
84
|
+
#
|
85
|
+
def class_extend(*mods, &block)
|
86
|
+
@class_extension ||= Module.new do
|
87
|
+
def self.append_features(mod)
|
88
|
+
append_features_without_class_extension(mod)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
@class_extension.__send__(:include, *mods)
|
92
|
+
@class_extension.module_eval(&block) if block_given?
|
93
|
+
extend(@class_extension) # extend this module too
|
94
|
+
@class_extension
|
95
|
+
end
|
96
|
+
|
97
|
+
# TODO: DEPRECATE
|
98
|
+
alias_method :class_extension, :class_extend
|
99
|
+
|
100
|
+
#private :class_extend
|
101
|
+
|
102
|
+
# Override +append_features+ to handle class-inheritable extensions.
|
103
|
+
def append_features(mod)
|
104
|
+
append_features_without_class_extension(mod)
|
105
|
+
mod.extend(class_extend)
|
106
|
+
if mod.instance_of? Module
|
107
|
+
mod.__send__(:class_extend).__send__(:include, class_extend)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
class Class
|
114
|
+
# For Class, #class_extend is the same as class_eval.
|
115
|
+
# The alternative is to "undef_method :class_extend",
|
116
|
+
# but this seems uneccessarily limited.
|
117
|
+
#
|
118
|
+
def class_extend(*mods, &block)
|
119
|
+
m = Module.new
|
120
|
+
m.__send__(:include, *mods)
|
121
|
+
m.module_eval(&block)
|
122
|
+
extend(m)
|
123
|
+
m
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|