facets 2.7.0 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|