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.
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]