multimethod 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +21 -54
- data/Rakefile +19 -2
- data/Releases.txt +7 -0
- data/lib/multimethod.rb +1 -0
- data/lib/multimethod/core_extensions.rb +6 -1
- data/lib/multimethod/method.rb +43 -185
- data/lib/multimethod/multimethod.rb +58 -3
- data/lib/multimethod/multimethod_version.rb +6 -0
- data/lib/multimethod/parameter.rb +124 -17
- data/lib/multimethod/signature.rb +322 -0
- data/lib/multimethod/table.rb +75 -30
- data/test/add_remove_test.rb +99 -0
- data/test/method_test.rb +2 -67
- data/test/multimethod_test.rb +18 -16
- data/test/signature_test.rb +190 -0
- metadata +23 -56
- data/.svn/README.txt +0 -2
- data/.svn/empty-file +0 -0
- data/.svn/entries +0 -68
- data/.svn/format +0 -1
- data/.svn/text-base/ChangeLog.svn-base +0 -3
- data/.svn/text-base/Manifest.txt.svn-base +0 -54
- data/.svn/text-base/README.txt.svn-base +0 -40
- data/.svn/text-base/Rakefile.svn-base +0 -132
- data/.svn/text-base/Releases.txt.svn-base +0 -7
- data/examples/.svn/README.txt +0 -2
- data/examples/.svn/empty-file +0 -0
- data/examples/.svn/entries +0 -22
- data/examples/.svn/format +0 -1
- data/examples/.svn/props/ex1.rb.svn-work +0 -5
- data/lib/.svn/README.txt +0 -2
- data/lib/.svn/empty-file +0 -0
- data/lib/.svn/entries +0 -24
- data/lib/.svn/format +0 -1
- data/lib/.svn/text-base/multimethod.rb.svn-base +0 -8
- data/lib/multimethod/.svn/README.txt +0 -2
- data/lib/multimethod/.svn/empty-file +0 -0
- data/lib/multimethod/.svn/entries +0 -53
- data/lib/multimethod/.svn/format +0 -1
- data/lib/multimethod/.svn/text-base/core_extensions.rb.svn-base +0 -38
- data/lib/multimethod/.svn/text-base/method.rb.svn-base +0 -232
- data/lib/multimethod/.svn/text-base/multimethod.rb.svn-base +0 -171
- data/lib/multimethod/.svn/text-base/parameter.rb.svn-base +0 -64
- data/lib/multimethod/.svn/text-base/table.rb.svn-base +0 -99
- data/test/.svn/README.txt +0 -2
- data/test/.svn/empty-file +0 -0
- data/test/.svn/entries +0 -53
- data/test/.svn/format +0 -1
- data/test/.svn/text-base/method_test.rb.svn-base +0 -89
- data/test/.svn/text-base/multimethod_test.rb.svn-base +0 -92
- data/test/.svn/text-base/parameter_test.rb.svn-base +0 -31
- data/test/.svn/text-base/test_base.rb.svn-base +0 -25
- data/test/.svn/text-base/usage_test.rb.svn-base +0 -146
data/Manifest.txt
CHANGED
@@ -1,54 +1,21 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
./lib/.svn/empty-file
|
23
|
-
./lib/.svn/entries
|
24
|
-
./lib/.svn/format
|
25
|
-
./lib/.svn/text-base/multimethod.rb.svn-base
|
26
|
-
./lib/multimethod.rb
|
27
|
-
./lib/multimethod/.svn/README.txt
|
28
|
-
./lib/multimethod/.svn/empty-file
|
29
|
-
./lib/multimethod/.svn/entries
|
30
|
-
./lib/multimethod/.svn/format
|
31
|
-
./lib/multimethod/.svn/text-base/core_extensions.rb.svn-base
|
32
|
-
./lib/multimethod/.svn/text-base/method.rb.svn-base
|
33
|
-
./lib/multimethod/.svn/text-base/multimethod.rb.svn-base
|
34
|
-
./lib/multimethod/.svn/text-base/parameter.rb.svn-base
|
35
|
-
./lib/multimethod/.svn/text-base/table.rb.svn-base
|
36
|
-
./lib/multimethod/core_extensions.rb
|
37
|
-
./lib/multimethod/method.rb
|
38
|
-
./lib/multimethod/multimethod.rb
|
39
|
-
./lib/multimethod/parameter.rb
|
40
|
-
./lib/multimethod/table.rb
|
41
|
-
./test/.svn/README.txt
|
42
|
-
./test/.svn/empty-file
|
43
|
-
./test/.svn/entries
|
44
|
-
./test/.svn/format
|
45
|
-
./test/.svn/text-base/method_test.rb.svn-base
|
46
|
-
./test/.svn/text-base/multimethod_test.rb.svn-base
|
47
|
-
./test/.svn/text-base/parameter_test.rb.svn-base
|
48
|
-
./test/.svn/text-base/test_base.rb.svn-base
|
49
|
-
./test/.svn/text-base/usage_test.rb.svn-base
|
50
|
-
./test/method_test.rb
|
51
|
-
./test/multimethod_test.rb
|
52
|
-
./test/parameter_test.rb
|
53
|
-
./test/test_base.rb
|
54
|
-
./test/usage_test.rb
|
1
|
+
ChangeLog
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
Releases.txt
|
6
|
+
examples/ex1.rb
|
7
|
+
lib/multimethod.rb
|
8
|
+
lib/multimethod/core_extensions.rb
|
9
|
+
lib/multimethod/method.rb
|
10
|
+
lib/multimethod/multimethod.rb
|
11
|
+
lib/multimethod/multimethod_version.rb
|
12
|
+
lib/multimethod/parameter.rb
|
13
|
+
lib/multimethod/signature.rb
|
14
|
+
lib/multimethod/table.rb
|
15
|
+
test/add_remove_test.rb
|
16
|
+
test/method_test.rb
|
17
|
+
test/multimethod_test.rb
|
18
|
+
test/parameter_test.rb
|
19
|
+
test/signature_test.rb
|
20
|
+
test/test_base.rb
|
21
|
+
test/usage_test.rb
|
data/Rakefile
CHANGED
@@ -18,6 +18,7 @@ http://multimethod.rubyforge.org/
|
|
18
18
|
|
19
19
|
}
|
20
20
|
|
21
|
+
|
21
22
|
#################################################################
|
22
23
|
# Release notes
|
23
24
|
#
|
@@ -44,6 +45,7 @@ def get_release_notes(relfile = "Releases.txt")
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
48
|
+
$stderr.puts "Release #{release.inspect}"
|
47
49
|
[ release, notes.join('') ]
|
48
50
|
end
|
49
51
|
|
@@ -51,6 +53,8 @@ end
|
|
51
53
|
|
52
54
|
PKG_NAME = PKG_Name.gsub(/[a-z][A-Z]/) {|x| "#{x[0,1]}_#{x[1,1]}"}.downcase
|
53
55
|
|
56
|
+
PKG_SVN_ROOT="svn+ssh://rubyforge.org/var/svn/#{PKG_NAME}/#{PKG_NAME}"
|
57
|
+
|
54
58
|
release, release_notes = get_release_notes
|
55
59
|
|
56
60
|
hoe = Hoe.new(PKG_NAME, release) do |p|
|
@@ -93,9 +97,15 @@ task :update_version do
|
|
93
97
|
end
|
94
98
|
end
|
95
99
|
|
96
|
-
task
|
100
|
+
# task package => :update_version
|
101
|
+
|
102
|
+
#################################################################
|
103
|
+
# SVN
|
104
|
+
#
|
97
105
|
|
98
|
-
|
106
|
+
task :svn_release do
|
107
|
+
sh %{svn cp -m 'Release #{PKG_VERSION}' . #{PKG_SVN_ROOT}/release/#{PKG_VERSION}}
|
108
|
+
end
|
99
109
|
|
100
110
|
|
101
111
|
# Misc Tasks ---------------------------------------------------------
|
@@ -130,3 +140,10 @@ task :rubyfiles do
|
|
130
140
|
puts Dir['bin/*'].reject { |fn| fn =~ /CVS|.svn|(~$)|(\.rb$)/ }
|
131
141
|
end
|
132
142
|
|
143
|
+
task :make_manifest do
|
144
|
+
open("Manifest.txt", "w") do |f|
|
145
|
+
f.puts Dir['**/*'].reject { |fn| ! test(?f, fn) || fn =~ /CVS|.svn|(~$)/ }.sort.join("\n") + "\n"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
|
data/Releases.txt
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
= Multimethod Release History
|
2
2
|
|
3
|
+
== Release 0.1.0: 2006/11/24
|
4
|
+
|
5
|
+
* Multimethods can be added and removed.
|
6
|
+
* Better internal design: signatures vs methods.
|
7
|
+
* TODO
|
8
|
+
* Fix how default parameters are scored.
|
9
|
+
|
3
10
|
== Release 0.0.1: 2006/11/18
|
4
11
|
|
5
12
|
* Initial Release
|
data/lib/multimethod.rb
CHANGED
@@ -25,11 +25,16 @@ module Multimethod
|
|
25
25
|
::Multimethod::Table.instance.install_method(self, body, file, line)
|
26
26
|
end
|
27
27
|
end
|
28
|
+
|
29
|
+
def remove_multimethod(signature)
|
30
|
+
::Multimethod::Table.instance.remove_method(signature)
|
31
|
+
end
|
32
|
+
|
28
33
|
end # class
|
29
34
|
end # module
|
30
35
|
|
31
36
|
|
32
|
-
# Add to
|
37
|
+
# Add to Object
|
33
38
|
Object.class_eval do
|
34
39
|
include Multimethod::ObjectExtension
|
35
40
|
end
|
data/lib/multimethod/method.rb
CHANGED
@@ -1,227 +1,85 @@
|
|
1
1
|
module Multimethod
|
2
2
|
|
3
3
|
class Method
|
4
|
-
attr_accessor :
|
5
|
-
attr_accessor :
|
6
|
-
attr_accessor :parameter
|
7
|
-
|
8
|
-
attr_accessor :min_args
|
9
|
-
attr_accessor :max_args
|
10
|
-
attr_accessor :restarg
|
11
|
-
attr_accessor :default
|
4
|
+
attr_accessor :signature
|
5
|
+
attr_accessor :impl_name
|
12
6
|
|
13
7
|
attr_accessor :multimethod
|
14
|
-
attr_accessor :file
|
15
|
-
attr_accessor :line
|
16
|
-
|
17
|
-
def initialize(mod, name, params)
|
18
|
-
raise NameError, "multimethod method name not specified" unless name && name.to_s.size > 0
|
19
|
-
|
20
|
-
name = Multimethod.normalize_name(name)
|
21
8
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
9
|
+
def initialize(impl_name, *args)
|
10
|
+
if args.size == 1
|
11
|
+
@signature = args[0]
|
12
|
+
else
|
13
|
+
mod, name, params = *args
|
14
|
+
raise NameError, "multimethod method name not specified" unless name && name.to_s.size > 0
|
15
|
+
raise NameError, "multimethod method impl_name not specified" unless impl_name && impl_name.to_s.size > 0
|
16
|
+
|
17
|
+
@signature = Signature.new(:mod => mod, :name => name, :parameter => params)
|
18
|
+
end
|
31
19
|
|
32
|
-
|
33
|
-
add_parameter(Parameter.new('self', mod))
|
20
|
+
impl_name = Multimethod.normalize_name(impl_name)
|
34
21
|
|
35
|
-
|
36
|
-
case params
|
37
|
-
when Array
|
38
|
-
scan_parameters(params)
|
39
|
-
when String
|
40
|
-
scan_parameters_string(params)
|
41
|
-
end
|
22
|
+
@impl_name = impl_name
|
42
23
|
end
|
43
24
|
|
44
25
|
|
45
|
-
|
46
|
-
|
47
|
-
0
|
26
|
+
def matches_signature(signature)
|
27
|
+
@signature == signature
|
48
28
|
end
|
49
29
|
|
50
30
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
31
|
+
def remove_implementation
|
32
|
+
# Remove the method implementation
|
33
|
+
# $stderr.puts "Removing implementation for #{signature.to_s} => #{impl_name}"
|
34
|
+
signature.mod.class_eval("remove_method #{impl_name.inspect}")
|
35
|
+
end
|
56
36
|
|
57
|
-
until str.empty?
|
58
|
-
name = nil
|
59
|
-
type = nil
|
60
|
-
default = nil
|
61
|
-
|
62
|
-
str.sub!(/^\s+/, '')
|
63
37
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
str = md.post_match
|
68
|
-
type = md[1]
|
69
|
-
name = md[3]
|
70
|
-
elsif md = /^(\*?\w+)/i.match(str)
|
71
|
-
str = md.post_match
|
72
|
-
type = nil
|
73
|
-
name = md[1]
|
74
|
-
else
|
75
|
-
raise NameError, "Syntax error in multimethod parameters: #{params.inspect} before #{str.inspect}"
|
76
|
-
end
|
77
|
-
|
78
|
-
if md = /^\s*=\s*([^,]+)/.match(str)
|
79
|
-
str = md.post_match
|
80
|
-
default = md[1]
|
81
|
-
end
|
82
|
-
|
83
|
-
|
84
|
-
str.sub!(/^\s+/, '')
|
85
|
-
if ! str.empty?
|
86
|
-
if md = /^,/.match(str)
|
87
|
-
str = md.post_match
|
88
|
-
else
|
89
|
-
raise NameError, "Syntax error in multimethod parameters: expected ',' before #{str.inspect}"
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
p = Parameter.new(name, type, default)
|
94
|
-
add_parameter(p)
|
95
|
-
end
|
38
|
+
# For score sort
|
39
|
+
def <=>(x)
|
40
|
+
0
|
96
41
|
end
|
97
42
|
|
98
43
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
type = nil
|
103
|
-
restarg = false
|
104
|
-
default = nil
|
105
|
-
|
106
|
-
if x = params.shift
|
107
|
-
case x
|
108
|
-
when Class
|
109
|
-
type = x
|
110
|
-
else
|
111
|
-
name = x
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
if ! name && (x = params.shift)
|
116
|
-
name = x
|
117
|
-
end
|
118
|
-
|
119
|
-
raise("Parameter name expected, found #{name.inspect}") unless name.kind_of?(String) || name.kind_of?(Symbol)
|
120
|
-
raise("Parameter type expected, found #{type.inspect}") unless type.kind_of?(Module) || type.nil?
|
121
|
-
|
122
|
-
p = Parameter.new(name, type, default)
|
123
|
-
add_parameter(p)
|
124
|
-
end
|
125
|
-
|
44
|
+
# Parameters
|
45
|
+
def parameter
|
46
|
+
@signature.parameter
|
126
47
|
end
|
127
|
-
|
128
|
-
def add_parameter(p)
|
129
|
-
if p.restarg
|
130
|
-
raise("Too many restargs") if @restarg
|
131
|
-
@restarg = p
|
132
|
-
@max_args = nil
|
133
|
-
end
|
134
|
-
if p.default
|
135
|
-
(@default ||= [ ]).push(p)
|
136
|
-
end
|
137
48
|
|
138
|
-
p.i = @parameter.size
|
139
|
-
@parameter.push(p)
|
140
|
-
p.method = self
|
141
49
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
unless @restarg
|
147
|
-
@max_args = @parameter.size
|
148
|
-
end
|
50
|
+
# Scoring this method.
|
51
|
+
def score(args)
|
52
|
+
@signature.score(args)
|
149
53
|
end
|
150
54
|
|
151
55
|
|
152
56
|
def score_cached(args)
|
153
|
-
|
154
|
-
x = @score[args] =
|
155
|
-
score(args)
|
156
|
-
end
|
157
|
-
x
|
57
|
+
@signature.score_cached(args)
|
158
58
|
end
|
159
59
|
|
160
60
|
|
161
|
-
def
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
score = nil
|
166
|
-
elsif @max_args && @max_args < args.size
|
167
|
-
# Too many args?
|
168
|
-
# $stderr.puts "max_args = #{@max_args}, args.size = #{args.size}"
|
169
|
-
score = nil
|
170
|
-
else
|
171
|
-
# Interpret how close the argument type is to the parameter's type.
|
172
|
-
i = -1
|
173
|
-
score = args.collect{|a| parameter_at(i = i + 1).score(a)}
|
174
|
-
|
175
|
-
# Handle score for trailing restargs.
|
176
|
-
if @restarg || @default
|
177
|
-
while (i = i + 1) < @parameter.size
|
178
|
-
# $stderr.puts " Adding score i=#{i}"
|
179
|
-
score << parameter_at(i).score(NilClass)
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
# If any argument cannot match, avoid this method.
|
184
|
-
score = nil if score.index(nil)
|
185
|
-
end
|
61
|
+
def to_s(name = nil)
|
62
|
+
name ||= @impl_name
|
63
|
+
@signature.to_s(name)
|
64
|
+
end
|
186
65
|
|
187
|
-
# if true || @name =~ /_bar$/
|
188
|
-
# $stderr.puts " Method: score #{self.to_s} #{args.inspect} => #{score.inspect}"
|
189
|
-
# end
|
190
66
|
|
191
|
-
|
67
|
+
def to_ruby_def(name = nil)
|
68
|
+
name ||= @impl_name
|
69
|
+
@signature.to_ruby_def(name)
|
192
70
|
end
|
193
|
-
|
194
71
|
|
195
|
-
def parameter_at(i)
|
196
|
-
if i >= @parameter.size && @restarg
|
197
|
-
@restarg
|
198
|
-
else
|
199
|
-
@parameter[i]
|
200
|
-
end
|
201
|
-
end
|
202
72
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
p.collect{|x| x.to_s_long}.join(', ')
|
73
|
+
def to_ruby_signature(name = nil)
|
74
|
+
name ||= @impl_name
|
75
|
+
@signature.to_ruby_signature(name)
|
207
76
|
end
|
208
77
|
|
209
|
-
def to_s(name = nil)
|
210
|
-
name ||= @name
|
211
|
-
p = @parameter.clone
|
212
|
-
rcvr = p.shift
|
213
|
-
"#{rcvr.type.name}##{name}(#{parameter_to_s(p)})"
|
214
|
-
end
|
215
78
|
|
216
|
-
def
|
217
|
-
|
79
|
+
def to_ruby_arg
|
80
|
+
@signature.to_ruby_arg
|
218
81
|
end
|
219
82
|
|
220
|
-
def to_s_arg
|
221
|
-
x = @parameter.clone
|
222
|
-
x.shift
|
223
|
-
x.collect{|x| x.to_ruby}.join(', ')
|
224
|
-
end
|
225
83
|
|
226
84
|
def inspect
|
227
85
|
to_s
|
@@ -23,8 +23,15 @@ module Multimethod
|
|
23
23
|
end
|
24
24
|
|
25
25
|
|
26
|
-
def new_method(mod, *args)
|
27
|
-
m = Method.new(mod,
|
26
|
+
def new_method(mod, name, *args)
|
27
|
+
m = Method.new(gensym(name), mod, name, *args)
|
28
|
+
add_method(m)
|
29
|
+
m
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def new_method_from_signature(signature)
|
34
|
+
m = Method.new(gensym(name), signature)
|
28
35
|
add_method(m)
|
29
36
|
m
|
30
37
|
end
|
@@ -32,6 +39,7 @@ module Multimethod
|
|
32
39
|
|
33
40
|
def add_method(method)
|
34
41
|
# THREAD CRITICAL BEGIN
|
42
|
+
remove_method(method.signature)
|
35
43
|
@method.push(method)
|
36
44
|
method.multimethod = self
|
37
45
|
@lookup_method = { } # flush cache
|
@@ -39,6 +47,41 @@ module Multimethod
|
|
39
47
|
end
|
40
48
|
|
41
49
|
|
50
|
+
def matches_signature(signature)
|
51
|
+
@name == signature.name
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def find_method(signature)
|
56
|
+
m = @method.select{|x| x.matches_signature(signature)}
|
57
|
+
|
58
|
+
m
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def remove_method(x)
|
63
|
+
case x
|
64
|
+
when Signature
|
65
|
+
m = find_method(x)
|
66
|
+
m = m[0]
|
67
|
+
return unless m
|
68
|
+
raise("No method found for #{x.to_s}") unless m
|
69
|
+
else
|
70
|
+
m = x
|
71
|
+
end
|
72
|
+
|
73
|
+
m.remove_implementation
|
74
|
+
m.multimethod = nil
|
75
|
+
@method.delete(m)
|
76
|
+
@lookup_method = { } # flush cache
|
77
|
+
|
78
|
+
# Remove multimethod dispatch in the method's module?
|
79
|
+
if @method.collect{|x| m.signature.mod = m.signature.mod}.empty?
|
80
|
+
remove_dispatch(m.signature.mod)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
42
85
|
def dispatch(rcvr, args)
|
43
86
|
apply_method(lookup_method(rcvr, args), rcvr, args)
|
44
87
|
end
|
@@ -52,7 +95,7 @@ module Multimethod
|
|
52
95
|
$stderr.puts "\n"
|
53
96
|
end
|
54
97
|
raise NameError, "Cannot find multimethod for #{rcvr.class.name}##{@name}(#{args})" unless meth
|
55
|
-
rcvr.send(meth.
|
98
|
+
rcvr.send(meth.impl_name, *args)
|
56
99
|
end
|
57
100
|
|
58
101
|
|
@@ -95,6 +138,7 @@ module Multimethod
|
|
95
138
|
result
|
96
139
|
end
|
97
140
|
|
141
|
+
|
98
142
|
def score_methods(meths, args)
|
99
143
|
scores = meths.collect do |meth|
|
100
144
|
score = meth.score_cached(args)
|
@@ -116,6 +160,17 @@ module Multimethod
|
|
116
160
|
end
|
117
161
|
|
118
162
|
|
163
|
+
def remove_dispatch(mod)
|
164
|
+
# THREAD CRITICAL BEGIN
|
165
|
+
if @dispatch[mod]
|
166
|
+
@dispatch[mod] = false
|
167
|
+
# $stderr.puts "Removing dispatch for #{mod.name}##{name}"
|
168
|
+
mod.class_eval("remove_method #{name.inspect}")
|
169
|
+
end
|
170
|
+
# THREAD CRITICAL END
|
171
|
+
end
|
172
|
+
|
173
|
+
|
119
174
|
def install_dispatch(mod)
|
120
175
|
# THREAD CRITICAL BEGIN
|
121
176
|
unless @dispatch[mod]
|