multimethod 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/Manifest.txt +21 -54
  2. data/Rakefile +19 -2
  3. data/Releases.txt +7 -0
  4. data/lib/multimethod.rb +1 -0
  5. data/lib/multimethod/core_extensions.rb +6 -1
  6. data/lib/multimethod/method.rb +43 -185
  7. data/lib/multimethod/multimethod.rb +58 -3
  8. data/lib/multimethod/multimethod_version.rb +6 -0
  9. data/lib/multimethod/parameter.rb +124 -17
  10. data/lib/multimethod/signature.rb +322 -0
  11. data/lib/multimethod/table.rb +75 -30
  12. data/test/add_remove_test.rb +99 -0
  13. data/test/method_test.rb +2 -67
  14. data/test/multimethod_test.rb +18 -16
  15. data/test/signature_test.rb +190 -0
  16. metadata +23 -56
  17. data/.svn/README.txt +0 -2
  18. data/.svn/empty-file +0 -0
  19. data/.svn/entries +0 -68
  20. data/.svn/format +0 -1
  21. data/.svn/text-base/ChangeLog.svn-base +0 -3
  22. data/.svn/text-base/Manifest.txt.svn-base +0 -54
  23. data/.svn/text-base/README.txt.svn-base +0 -40
  24. data/.svn/text-base/Rakefile.svn-base +0 -132
  25. data/.svn/text-base/Releases.txt.svn-base +0 -7
  26. data/examples/.svn/README.txt +0 -2
  27. data/examples/.svn/empty-file +0 -0
  28. data/examples/.svn/entries +0 -22
  29. data/examples/.svn/format +0 -1
  30. data/examples/.svn/props/ex1.rb.svn-work +0 -5
  31. data/lib/.svn/README.txt +0 -2
  32. data/lib/.svn/empty-file +0 -0
  33. data/lib/.svn/entries +0 -24
  34. data/lib/.svn/format +0 -1
  35. data/lib/.svn/text-base/multimethod.rb.svn-base +0 -8
  36. data/lib/multimethod/.svn/README.txt +0 -2
  37. data/lib/multimethod/.svn/empty-file +0 -0
  38. data/lib/multimethod/.svn/entries +0 -53
  39. data/lib/multimethod/.svn/format +0 -1
  40. data/lib/multimethod/.svn/text-base/core_extensions.rb.svn-base +0 -38
  41. data/lib/multimethod/.svn/text-base/method.rb.svn-base +0 -232
  42. data/lib/multimethod/.svn/text-base/multimethod.rb.svn-base +0 -171
  43. data/lib/multimethod/.svn/text-base/parameter.rb.svn-base +0 -64
  44. data/lib/multimethod/.svn/text-base/table.rb.svn-base +0 -99
  45. data/test/.svn/README.txt +0 -2
  46. data/test/.svn/empty-file +0 -0
  47. data/test/.svn/entries +0 -53
  48. data/test/.svn/format +0 -1
  49. data/test/.svn/text-base/method_test.rb.svn-base +0 -89
  50. data/test/.svn/text-base/multimethod_test.rb.svn-base +0 -92
  51. data/test/.svn/text-base/parameter_test.rb.svn-base +0 -31
  52. data/test/.svn/text-base/test_base.rb.svn-base +0 -25
  53. data/test/.svn/text-base/usage_test.rb.svn-base +0 -146
@@ -1,54 +1,21 @@
1
- ./.svn/README.txt
2
- ./.svn/empty-file
3
- ./.svn/entries
4
- ./.svn/format
5
- ./.svn/text-base/ChangeLog.svn-base
6
- ./.svn/text-base/Manifest.txt.svn-base
7
- ./.svn/text-base/README.txt.svn-base
8
- ./.svn/text-base/Rakefile.svn-base
9
- ./.svn/text-base/Releases.txt.svn-base
10
- ./ChangeLog
11
- ./Manifest.txt
12
- ./README.txt
13
- ./Rakefile
14
- ./Releases.txt
15
- ./examples/.svn/README.txt
16
- ./examples/.svn/empty-file
17
- ./examples/.svn/entries
18
- ./examples/.svn/format
19
- ./examples/.svn/props/ex1.rb.svn-work
20
- ./examples/ex1.rb
21
- ./lib/.svn/README.txt
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 version_rb => :update_version
100
+ # task package => :update_version
101
+
102
+ #################################################################
103
+ # SVN
104
+ #
97
105
 
98
- # task :test => :update_version
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
+
@@ -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
@@ -4,5 +4,6 @@ $:.unshift(File.dirname(__FILE__)) unless
4
4
  require 'multimethod/table'
5
5
  require 'multimethod/multimethod'
6
6
  require 'multimethod/method'
7
+ require 'multimethod/signature'
7
8
  require 'multimethod/parameter'
8
9
  require 'multimethod/core_extensions'
@@ -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 Module
37
+ # Add to Object
33
38
  Object.class_eval do
34
39
  include Multimethod::ObjectExtension
35
40
  end
@@ -1,227 +1,85 @@
1
1
  module Multimethod
2
2
 
3
3
  class Method
4
- attr_accessor :mod
5
- attr_accessor :name
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
- @mod = mod
23
- @name = name
24
- @parameter = [ ]
25
- @min_args = 0
26
- @max_args = 0
27
- @restarg = nil
28
- @default = nil
29
-
30
- @score = { }
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
- # Add self parameter at front.
33
- add_parameter(Parameter.new('self', mod))
20
+ impl_name = Multimethod.normalize_name(impl_name)
34
21
 
35
- # Handle other parameters.
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
- # For sort
46
- def <=>(x)
47
- 0
26
+ def matches_signature(signature)
27
+ @signature == signature
48
28
  end
49
29
 
50
30
 
51
- def scan_parameters_string(params)
52
-
53
- #$stderr.puts "scan_parameters_string(#{params.inspect})"
54
-
55
- str = params.clone
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
- # $stderr.puts " str=#{str.inspect}"
65
-
66
- if md = /^(\w+(::\w+)*)\s+(\w+)/i.match(str)
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
- def scan_parameters(params)
100
- until params.empty?
101
- name = nil
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
- unless p.default || p.restarg
143
- @min_args = @parameter.size
144
- end
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
- unless x = @score[args]
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 score(args)
162
-
163
- if @min_args > args.size
164
- # Not enough args
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
- score
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
- def parameter_to_s(p = nil)
205
- p ||= @parameter
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 to_ruby
217
- "def #{name}(#{to_s_arg})"
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, gensym, *args)
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.name, *args)
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]