multimethod 0.0.1 → 0.1.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/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]
|