multimethod 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,8 @@
1
+ 2006-11-29 Kurt A. Stephens <kurt@umleta.com>
2
+
3
+ * Major cleanup and documentation
4
+ * Fixed default parameter scoring.
5
+
1
6
  2006-11-18 Kurt A. Stephens <kurt@umleta.com>
2
7
 
3
8
  * Initial release
data/README.txt CHANGED
@@ -15,7 +15,7 @@ gem install multimethod
15
15
  The RubyForge package multimethod
16
16
  implements multi-method dispatching on argument types.
17
17
 
18
- See also: ?????http://umleta.com/node/?????
18
+ See also: http://umleta.com/node/7
19
19
 
20
20
  == Home page
21
21
 
data/Rakefile CHANGED
@@ -30,7 +30,7 @@ def get_release_notes(relfile = "Releases.txt")
30
30
 
31
31
  File.open(relfile) do |f|
32
32
  while ! f.eof? && line = f.readline
33
- if md = /^== Release ([\d\.]+)/i.match(line)
33
+ if md = /^=+ Release ([\d\.]+)/i.match(line)
34
34
  release = md[1]
35
35
  notes << line
36
36
  break
@@ -38,14 +38,14 @@ def get_release_notes(relfile = "Releases.txt")
38
38
  end
39
39
 
40
40
  while ! f.eof? && line = f.readline
41
- if md = /^== Release ([\d\.]+)/i.match(line)
41
+ if md = /^=+ Release ([\d\.]+)/i.match(line)
42
42
  break
43
43
  end
44
44
  notes << line
45
45
  end
46
46
  end
47
47
 
48
- $stderr.puts "Release #{release.inspect}"
48
+ # $stderr.puts "Release #{release.inspect}"
49
49
  [ release, notes.join('') ]
50
50
  end
51
51
 
@@ -83,10 +83,10 @@ version_rb = "lib/#{PKG_NAME}/#{PKG_NAME}_version.rb"
83
83
  task :update_version do
84
84
  announce "Updating #{PKG_Name} version to #{PKG_VERSION}: #{version_rb}"
85
85
  open(version_rb, "w") do |f|
86
+ f.puts "module #{PKG_Name}"
86
87
  f.puts "# DO NOT EDIT"
87
88
  f.puts "# This file is auto-generated by build scripts."
88
89
  f.puts "# See: rake update_version"
89
- f.puts "module #{PKG_Name}"
90
90
  f.puts " #{PKG_Name}Version = '#{PKG_VERSION}'"
91
91
  f.puts "end"
92
92
  end
@@ -1,14 +1,21 @@
1
1
  = Multimethod Release History
2
2
 
3
+ == Release 0.2.0: 2006/11/29
4
+
5
+ * Fixed default parameter scoring.
6
+ * Added more documentation.
7
+ * TODO:
8
+ * Need ambigious method test cases.
9
+
3
10
  == Release 0.1.0: 2006/11/24
4
11
 
5
12
  * Multimethods can be added and removed.
6
13
  * Better internal design: signatures vs methods.
7
14
  * TODO
8
- * Fix how default parameters are scored.
15
+ * Fix how default parameters are scored.
9
16
 
10
17
  == Release 0.0.1: 2006/11/18
11
18
 
12
19
  * Initial Release
13
20
  * TODO
14
- * Fix how default parameters are scored.
21
+ * Fix how default parameters are scored.
@@ -1,3 +1,68 @@
1
+ # == Introduction
2
+ #
3
+ # The Multimethod package implements dispatch of messages to
4
+ # multiple methods based on argument types.
5
+ #
6
+ # Variadic methods and default values are supported.
7
+ #
8
+ # Methods can be added and removed at run-time.
9
+ #
10
+ # == Examples
11
+ #
12
+ # require 'multimethod'
13
+ #
14
+ # class A
15
+ # multimethod %q{
16
+ # def foo(x) # matches any argument type
17
+ # "#{x.inspect}"
18
+ # end
19
+ # }
20
+ #
21
+ # multimethod %q{
22
+ # def foo(Fixnum x) # matches any Fixnum
23
+ # "Fixnum #{x.inspect}"
24
+ # end
25
+ # }
26
+ #
27
+ # multimethod %q{
28
+ # def foo(Numeric x) # matches any Numeric
29
+ # "Numeric #{x.inspect}"
30
+ # end
31
+ # }
32
+ # end
33
+ #
34
+ # a = A.new
35
+ # puts a.foo(:symbol) # ==> ":symbol"
36
+ # puts a.foo(45) # ==> "Fixnum 45"
37
+ # puts a.foo(12.34) # ==> "Numeric 12.34"
38
+ #
39
+ # == Known Issues
40
+ #
41
+ # This library is not yet thread-safe, due to caching mechanisms
42
+ # used to increase performance. This will be fixed in a future release.
43
+ #
44
+ # == Home page
45
+ #
46
+ # * {Multimethod Home}[http://multimethod.rubyforge.org]
47
+ #
48
+ # == Credits
49
+ #
50
+ # Multimethod was developed by:
51
+ #
52
+ # * Kurt Stephens -- ruby-multimethod(at)umleta.com, sponsored by umleta.com
53
+ #
54
+ # == Contributors
55
+ #
56
+ # Maybe you?
57
+ #
58
+ # == See Also
59
+ #
60
+ # * http://en.wikipedia.org/wiki/Multimethod
61
+ # * http://rubyforge.org/projects/multi/
62
+ #
63
+ module Multimethod
64
+ end
65
+
1
66
  $:.unshift(File.dirname(__FILE__)) unless
2
67
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
68
 
@@ -7,3 +72,4 @@ require 'multimethod/method'
7
72
  require 'multimethod/signature'
8
73
  require 'multimethod/parameter'
9
74
  require 'multimethod/core_extensions'
75
+
@@ -1,5 +1,6 @@
1
1
  module Multimethod
2
2
 
3
+ # See Multimethod::ObjectExtension::ClassMethods
3
4
  module ObjectExtension
4
5
  def self.append_features(base) # :nodoc:
5
6
  # puts "append_features{#{base}}"
@@ -7,7 +8,25 @@ module Multimethod
7
8
  base.extend(ClassMethods)
8
9
  end
9
10
 
11
+
12
+ # This module is included into Object
13
+ # It is the "glue" for Multmethod.
10
14
  module ClassMethods
15
+ # Installs a new Multimethod Method using the multimethod syntax:
16
+ #
17
+ # class A
18
+ # multimethod q{
19
+ # def foo(x)
20
+ # ...
21
+ # end
22
+ # }
23
+ # multimethod q{
24
+ # def foo(A x)
25
+ # end
26
+ # }
27
+ # end
28
+ #
29
+ # Interfaces to Multimethod::Table.instance.
11
30
  def multimethod(body, file = nil, line = nil)
12
31
  unless file && line
13
32
  fileline = caller(1)[0]
@@ -24,12 +43,17 @@ module Multimethod
24
43
 
25
44
  ::Multimethod::Table.instance.install_method(self, body, file, line)
26
45
  end
27
- end
28
46
 
29
- def remove_multimethod(signature)
30
- ::Multimethod::Table.instance.remove_method(signature)
31
- end
47
+ # Removes a Multimethod using a signature:
48
+ #
49
+ # class A
50
+ # remove_multimethod "def foo(A x)"
51
+ # end
52
+ def remove_multimethod(signature)
53
+ ::Multimethod::Table.instance.remove_method(signature)
54
+ end
32
55
 
56
+ end # mixin
33
57
  end # class
34
58
  end # module
35
59
 
@@ -1,11 +1,29 @@
1
1
  module Multimethod
2
2
 
3
+ # Represents a Method implementation in a Multimethod.
4
+ #
5
+ # A Method is bound to a Module using a unique implementation name for the Multimethod.
6
+ #
7
+ # A Multimethod may have multiple implementation Methods.
8
+ #
9
+ # The Multimethod is responsible for determining the correct Method based on the
10
+ # relative scoring of the Method Signature.
11
+ #
3
12
  class Method
13
+ # The Method's Signature used for relative scoring of applicability to an argument list.
4
14
  attr_accessor :signature
15
+
16
+ # The Method's underlying method name.
17
+ # This method name is unique.
5
18
  attr_accessor :impl_name
6
19
 
20
+ # The Method's Multimethod.
7
21
  attr_accessor :multimethod
8
22
 
23
+ # Initialize a new Method.
24
+ #
25
+ # Method.new(impl_name, signature)
26
+ # Method.new(impl_name, mod, name, parameter_list)
9
27
  def initialize(impl_name, *args)
10
28
  if args.size == 1
11
29
  @signature = args[0]
@@ -23,19 +41,20 @@ module Multimethod
23
41
  end
24
42
 
25
43
 
44
+ # Returns true if this Method matches the Signature.
26
45
  def matches_signature(signature)
27
46
  @signature == signature
28
47
  end
29
48
 
30
49
 
50
+ # Remove the method implementation from the receiver Module.
31
51
  def remove_implementation
32
- # Remove the method implementation
33
52
  # $stderr.puts "Removing implementation for #{signature.to_s} => #{impl_name}"
34
53
  signature.mod.class_eval("remove_method #{impl_name.inspect}")
35
54
  end
36
55
 
37
56
 
38
- # For score sort
57
+ # Returns 0.
39
58
  def <=>(x)
40
59
  0
41
60
  end
@@ -47,40 +66,50 @@ module Multimethod
47
66
  end
48
67
 
49
68
 
50
- # Scoring this method.
69
+ # Score of this Method based on the argument types.
70
+ # The receiver type is the first element of args.
51
71
  def score(args)
52
72
  @signature.score(args)
53
73
  end
54
74
 
55
75
 
76
+ # Score this Method based on the argument types
77
+ # using a cache.
56
78
  def score_cached(args)
57
79
  @signature.score_cached(args)
58
80
  end
59
81
 
60
82
 
83
+ # Returns a string representation using the
84
+ # implementation name.
61
85
  def to_s(name = nil)
62
86
  name ||= @impl_name
63
87
  @signature.to_s(name)
64
88
  end
65
89
 
66
90
 
91
+ # Returns the "def foo(...)" string
92
+ # using the implementation name by default.
67
93
  def to_ruby_def(name = nil)
68
94
  name ||= @impl_name
69
95
  @signature.to_ruby_def(name)
70
96
  end
71
97
 
72
98
 
99
+ # Returns a ruby signature
100
+ # using the implementation name by default.
73
101
  def to_ruby_signature(name = nil)
74
102
  name ||= @impl_name
75
103
  @signature.to_ruby_signature(name)
76
104
  end
77
105
 
78
106
 
107
+ # Returns a string representing the Ruby parameters.
79
108
  def to_ruby_arg
80
109
  @signature.to_ruby_arg
81
110
  end
82
111
 
83
-
112
+ # Same as #to_s.
84
113
  def inspect
85
114
  to_s
86
115
  end
@@ -1,10 +1,23 @@
1
1
  module Multimethod
2
+ # Represents a Multimethod.
3
+ #
4
+ # A Multimethod has multiple implementations of a method based on the relative scoring
5
+ # of the Methods based on the argument types of the message.
6
+ #
7
+ # A Multimethod has a name.
8
+ #
2
9
  class Multimethod
3
10
 
11
+ # The Multimethod's name.
4
12
  attr_accessor :name
13
+
14
+ # A list of Method's that implement this Multimethod.
5
15
  attr_accessor :method
16
+
17
+ # The Multimethod::Table that owns this Multimethod.
6
18
  attr_accessor :table
7
19
 
20
+ # Initialize a new Multimethod.
8
21
  def initialize(name, *opts)
9
22
  raise NameError, "multimethod name not specified" unless name && name.to_s.size > 0
10
23
 
@@ -17,19 +30,22 @@ module Multimethod
17
30
  end
18
31
 
19
32
 
33
+ # Generates a unique symbol for a method name.
34
+ # Method implementations will use a unique name for the implementation method.
35
+ # For example, for a Multimethod named "foo", the Method name might be "_multimethod_12_foo".
20
36
  def gensym(name = nil)
21
37
  name ||= @name
22
38
  "_multimethod_#{@name_i = @name_i + 1}_#{name}"
23
39
  end
24
40
 
25
-
41
+ # Creates a new Method object bound to mod by name.
26
42
  def new_method(mod, name, *args)
27
43
  m = Method.new(gensym(name), mod, name, *args)
28
44
  add_method(m)
29
45
  m
30
46
  end
31
47
 
32
-
48
+ # Create a new Method object using the Signature.
33
49
  def new_method_from_signature(signature)
34
50
  m = Method.new(gensym(name), signature)
35
51
  add_method(m)
@@ -37,6 +53,7 @@ module Multimethod
37
53
  end
38
54
 
39
55
 
56
+ # Adds the new Method object to this Multimethod.
40
57
  def add_method(method)
41
58
  # THREAD CRITICAL BEGIN
42
59
  remove_method(method.signature)
@@ -47,18 +64,20 @@ module Multimethod
47
64
  end
48
65
 
49
66
 
67
+ # Returns true if this Multimethod matches the Signature.
50
68
  def matches_signature(signature)
51
69
  @name == signature.name
52
70
  end
53
71
 
54
72
 
73
+ # Returns a list of all Methods that match the Signature.
55
74
  def find_method(signature)
56
75
  m = @method.select{|x| x.matches_signature(signature)}
57
76
 
58
77
  m
59
78
  end
60
79
 
61
-
80
+ # Removes the method.
62
81
  def remove_method(x)
63
82
  case x
64
83
  when Signature
@@ -139,6 +158,8 @@ module Multimethod
139
158
  end
140
159
 
141
160
 
161
+ # Returns a sorted list of scores and Methods that
162
+ # match the argument types.
142
163
  def score_methods(meths, args)
143
164
  scores = meths.collect do |meth|
144
165
  score = meth.score_cached(args)
@@ -154,23 +175,14 @@ module Multimethod
154
175
  scores.compact!
155
176
  scores.sort!
156
177
 
157
- # $stderr.puts "score_methods(#{args.inspect}) => \n#{scores.inspect}"
178
+ # $stderr.puts %{ score_methods(#{args.inspect}) => \n#{scores.collect{|x| x.inspect}.join("\n")}}
158
179
 
159
180
  scores
160
181
  end
161
182
 
162
183
 
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
-
184
+ # Installs a dispatching method in the Module.
185
+ # This method will dispatch to the Multimethod for Method lookup and application.
174
186
  def install_dispatch(mod)
175
187
  # THREAD CRITICAL BEGIN
176
188
  unless @dispatch[mod]
@@ -187,6 +199,18 @@ end_eval
187
199
  end
188
200
 
189
201
 
202
+ # Removes the dispatching method in the Module.
203
+ def remove_dispatch(mod)
204
+ # THREAD CRITICAL BEGIN
205
+ if @dispatch[mod]
206
+ @dispatch[mod] = false
207
+ # $stderr.puts "Removing dispatch for #{mod.name}##{name}"
208
+ mod.class_eval("remove_method #{name.inspect}")
209
+ end
210
+ # THREAD CRITICAL END
211
+ end
212
+
213
+
190
214
  ##################################################
191
215
  # Support
192
216
  #
@@ -1,6 +1,6 @@
1
+ module Multimethod
1
2
  # DO NOT EDIT
2
3
  # This file is auto-generated by build scripts.
3
4
  # See: rake update_version
4
- module Multimethod
5
- MultimethodVersion = '0.1.0'
5
+ MultimethodVersion = '0.2.0'
6
6
  end
@@ -1,18 +1,51 @@
1
1
  module Multimethod
2
+
3
+ # Represents a Parameter in a Signature.
4
+ #
5
+ # A Parameter has a name, type and position.
6
+ #
7
+ # Parameters may also have a default value or may be a restarg, a parameter that
8
+ # collects all remaining arguments.
9
+ #
10
+ # Restarg parameters have a lower score than other arguments.
11
+ #
12
+ # Unlike Ruby parameters, Parameters are typed. Unspecified Parameter types default to Kernel.
13
+ #
2
14
  class Parameter
3
15
  include Comparable
4
16
 
5
- RESTARG_SCORE = 9999
17
+ # The score base used for all Parameters with defaults.
18
+ DEFAULT_SCORE_BASE = 200
19
+
20
+ # The score used for all Parameters with defaults and no argument.
21
+ DEFAULT_SCORE = DEFAULT_SCORE_BASE + 100
6
22
 
23
+ # The score used for all restarg Parameters.
24
+ RESTARG_SCORE = DEFAULT_SCORE + 100
25
+
26
+ # The Parameter name.
7
27
  attr_accessor :name
28
+
29
+ # The Parameter's offset in the Signature's parameter list.
30
+ # Parameter 0 is the implied "self" Parameter.
8
31
  attr_accessor :i
32
+
33
+ # The Paremeter's type, defaults to Kernel.
9
34
  attr_accessor :type
35
+
36
+ # The Parameter's default value expression.
10
37
  attr_accessor :default
38
+
39
+ # True if the Parameter is a restarg: e.g.: "*args"
11
40
  attr_accessor :restarg
12
41
 
42
+ # The Parameter's owning Signature.
13
43
  attr_accessor :signature
44
+
45
+ # Defines level of verbosity during processing.
14
46
  attr_accessor :verbose
15
47
 
48
+ # Initialize a new Parameter.
16
49
  def initialize(name = nil, type = nil, default = nil, restarg = false)
17
50
  # $stderr.puts "initialize(#{name.inspect}, #{type}, #{default.inspect}, #{restarg.inspect})"
18
51
  if name
@@ -44,7 +77,8 @@ module Multimethod
44
77
  @name = name
45
78
  end
46
79
 
47
-
80
+ # Compare two Parameters.
81
+ # Only type and restarg are significant.
48
82
  def <=>(p)
49
83
  x = @type <=> p.type
50
84
  x = ! @restarg == ! p.restarg ? 0 : 1 if x == 0
@@ -54,6 +88,7 @@ module Multimethod
54
88
  end
55
89
 
56
90
 
91
+ # Scan a string for a Parameter specification.
57
92
  def scan_string(str, need_names = true)
58
93
  str.sub!(/\A\s+/, '')
59
94
 
@@ -130,17 +165,37 @@ module Multimethod
130
165
  end
131
166
 
132
167
 
168
+ # Returns the score of this Parameter matching an argument type.
169
+ #
170
+ # The score is determined by the relative distance of the Parameter
171
+ # to the argument type. A lower distance means a tighter match
172
+ # of this Parameter.
173
+ #
174
+ # Parameters with restargs or unspecfied default arguments score lower, see RESTARG_SCORE, DEFAULT_SCORE.
133
175
  def score(arg)
134
- return RESTARG_SCORE if @restarg
135
- score = all_types(arg).index(type_object)
176
+ if @restarg
177
+ score = RESTARG_SCORE
178
+ elsif @default && ! arg
179
+ score = DEFAULT_SCORE
180
+ else
181
+ score = all_types(arg).index(type_object)
182
+ end
183
+
184
+ # $stderr.puts " score(#{signature.to_s}, #{to_s}, #{arg && arg.name}) => #{score}"
185
+
186
+ score
136
187
  end
137
188
 
138
189
 
139
- def all_types(arg)
140
- arg.ancestors
190
+ # Returns a list of all parent Modules of an argument type,
191
+ # including itself, in most-specialized
192
+ # to least-specialized order.
193
+ def all_types(arg_type)
194
+ arg_type.ancestors
141
195
  end
142
196
 
143
197
 
198
+ # Resolves type by name
144
199
  def type_object
145
200
  if @type.kind_of?(String)
146
201
  @type = Table.instance.name_to_object(@type,
@@ -152,16 +207,20 @@ module Multimethod
152
207
  end
153
208
 
154
209
 
210
+ # Returns a String representing this Parameter in a Signature string.
155
211
  def to_s
156
212
  "#{@type} #{to_ruby_arg}"
157
213
  end
158
214
 
159
215
 
216
+ # Return a String representing this Parameter as a Ruby method parameter.
160
217
  def to_ruby_arg
161
218
  "#{to_s_name}#{@default ? ' = ' + @default : ''}"
162
219
  end
163
220
 
164
221
 
222
+ # Return a String representing this Parameter's name.
223
+ # Restargs will be prefixed with '*'.
165
224
  def to_s_name
166
225
  (@restarg ? "*" : '') + (@name.to_s || "_arg_#{@i}")
167
226
  end
@@ -1,24 +1,51 @@
1
1
  module Multimethod
2
2
 
3
+ # Represents a method signature.
4
+ #
5
+ # A Signature has a bound Module, a name and a Parameter list.
6
+ #
7
+ # Each Parameter contributes to the scoring of the Method based
8
+ # on the message argument types, including the message receiver.
9
+ #
3
10
  class Signature
4
11
  include Comparable
5
12
 
6
- attr_accessor :mod # The Module the signature is bound to.
7
- attr_accessor :class_method # True if the signature is bound to the class.
8
- attr_accessor :name # The name of the method signature.
9
- attr_accessor :parameter # The parameters of the method, self included.
13
+ # The Module that the Signature is bound to.
14
+ attr_accessor :mod
10
15
 
11
- attr_accessor :min_args # The minimum # of arguments for this signature
12
- attr_accessor :max_args # The maximum # of arguments for this signature;
13
- # May be nil, if restargs
14
- attr_accessor :restarg # The "*args" parameter or nil
15
- attr_accessor :default # The first parameter with a default value.
16
+ # True if the signature is bound to the class.
17
+ attr_accessor :class_method
16
18
 
19
+ # The name of the method.
20
+ attr_accessor :name
21
+
22
+ # The list of Parameters, self is included at position 0.
23
+ attr_accessor :parameter
24
+
25
+ # The minimum # of arguments for this signature.
26
+ attr_accessor :min_args
27
+
28
+ # The maximum # of arguments for this signature.
29
+ # May be nil, if this Signature accepts restargs.
30
+ attr_accessor :max_args
31
+
32
+ # The "*args" parameter or nil.
33
+ attr_accessor :restarg
34
+
35
+ # An Array of all Parameters with a default value.
36
+ # Will be nil if there is not a Parameter with a default values.
37
+ attr_accessor :default
38
+
39
+ # The file where this Signature is specified.
17
40
  attr_accessor :file
41
+
42
+ # The line in the file where this Signature is specified.
18
43
  attr_accessor :line
19
44
 
45
+ # Defines level of verbosity during processing.
20
46
  attr_accessor :verbose
21
47
 
48
+ # Initialize a new Signature.
22
49
  def initialize(*opts)
23
50
  opts = Hash[*opts]
24
51
 
@@ -53,7 +80,7 @@ module Multimethod
53
80
  end
54
81
 
55
82
 
56
- # For sort
83
+ # Compares two Signature objects.
57
84
  def <=>(s)
58
85
  x = @name.to_s <=> s.name.to_s
59
86
  x = (! @class_method == ! s.class_method ? 0 : 1) if x == 0
@@ -63,6 +90,7 @@ module Multimethod
63
90
  end
64
91
 
65
92
 
93
+ # Returns the bound Module.
66
94
  def mod
67
95
  # THREAD CRITICAL BEGIN
68
96
  if @mod && @mod.kind_of?(String)
@@ -76,7 +104,7 @@ module Multimethod
76
104
  end
77
105
 
78
106
 
79
- # Scan
107
+ # Scan a string as a Signature, e.g.: "def foo(A a, x = true, *restargs)"
80
108
  def scan_string(str, need_names = true)
81
109
 
82
110
  str.sub!(/\A\s+/, '')
@@ -124,6 +152,9 @@ module Multimethod
124
152
  end
125
153
 
126
154
 
155
+ # Scan the parameter string of a Signature:
156
+ #
157
+ # "A a, x = true, *restargs"
127
158
  def scan_parameters_string(str, need_names = true)
128
159
  # @verbose = true
129
160
 
@@ -161,6 +192,10 @@ module Multimethod
161
192
  end
162
193
 
163
194
 
195
+ # Scan a programmatic Parameter list:
196
+ #
197
+ # [ A, :a, B, :b, :c, '*d' ]
198
+ #
164
199
  def scan_parameters(params)
165
200
  # Add self parameter at front.
166
201
  add_self
@@ -194,12 +229,13 @@ module Multimethod
194
229
  end
195
230
 
196
231
 
197
- # Add self parameter at front.
232
+ # Add the implicit "self" parameter at the front of the Parameter list.
198
233
  def add_self
199
234
  add_parameter(Parameter.new('self', mod)) if @parameter.empty?
200
235
  end
201
236
 
202
-
237
+
238
+ # Adds a new Parameter.
203
239
  def add_parameter(p)
204
240
  if p.restarg
205
241
  raise("Too many restargs") if @restarg
@@ -224,17 +260,12 @@ module Multimethod
224
260
  end
225
261
 
226
262
 
227
- def score_cached(args)
228
- unless x = @score[args]
229
- x = @score[args] =
230
- score(args)
231
- end
232
- x
233
- end
234
-
235
-
263
+ # Score of this Signature based on the argument types.
264
+ #
265
+ # The score is an Array of values that when sorted against
266
+ # other Signature scores will
267
+ # place the best matching Signature at the top of the list.
236
268
  def score(args)
237
-
238
269
  if @min_args > args.size
239
270
  # Not enough args
240
271
  score = nil
@@ -251,22 +282,35 @@ module Multimethod
251
282
  if @restarg || @default
252
283
  while (i = i + 1) < @parameter.size
253
284
  # $stderr.puts " Adding score i=#{i}"
254
- score << parameter_at(i).score(NilClass)
285
+ # nil means there is no argument for this parameter.
286
+ score << parameter_at(i).score(nil)
255
287
  end
256
288
  end
257
289
 
258
- # If any argument cannot match, avoid this method.
290
+ # If any argument cannot match, avoid this method entirely.
291
+ score.flatten!
259
292
  score = nil if score.index(nil)
260
293
  end
261
294
 
262
- # if true || @name =~ /_bar$/
263
- # $stderr.puts " Method: score #{self.to_s} #{args.inspect} => #{score.inspect}"
264
- # end
295
+ # $stderr.puts " score(#{to_s}, #{args.inspect} => #{score.inspect})"
265
296
 
266
297
  score
267
298
  end
268
299
 
269
300
 
301
+ # Score of this Signature using a cache.
302
+ def score_cached(args)
303
+ unless x = @score[args]
304
+ x = @score[args] =
305
+ score(args)
306
+ end
307
+ x
308
+ end
309
+
310
+
311
+ # Returns the Parameter at argument position i.
312
+ # If the Signature has a restarg, it will be used for
313
+ # argument postitions past the end of the Parameter list.
270
314
  def parameter_at(i)
271
315
  if i >= @parameter.size && @restarg
272
316
  @restarg
@@ -276,6 +320,7 @@ module Multimethod
276
320
  end
277
321
 
278
322
 
323
+ # Returns a String representing this Signature.
279
324
  def to_s(name = nil)
280
325
  name ||= @name || '_'
281
326
  p = @parameter.clone
@@ -284,18 +329,21 @@ module Multimethod
284
329
  end
285
330
 
286
331
 
332
+ # Returns a String representing this Signature's Parameters.
287
333
  def parameter_to_s(p = nil)
288
334
  p ||= @parameter
289
335
  p.collect{|x| x.to_s}.join(', ')
290
336
  end
291
337
 
292
338
 
339
+ # Returns a String representing this Signature's definition in Ruby syntax.
293
340
  def to_ruby_def(name = nil)
294
341
  name ||= @name || '_'
295
342
  "def #{name}(#{to_ruby_arg})"
296
343
  end
297
344
 
298
345
 
346
+ # Returns a String representing this Signature's definition in Ruby Doc syntax.
299
347
  def to_ruby_signature(name = nil)
300
348
  name ||= @name || '_'
301
349
  p = @parameter.clone
@@ -305,13 +353,14 @@ module Multimethod
305
353
  end
306
354
 
307
355
 
356
+ # Returns a String representing this Signature's definition parameters in Ruby syntax.
308
357
  def to_ruby_arg
309
358
  x = @parameter.clone
310
359
  x.shift
311
360
  x.collect{|x| x.to_ruby_arg}.join(', ')
312
361
  end
313
362
 
314
-
363
+ # Calls #to_s.
315
364
  def inspect
316
365
  to_s
317
366
  end
@@ -1,16 +1,25 @@
1
1
  module Multimethod
2
+ # Represents a Multimethod repository.
3
+ #
4
+ # There is typically only one instance.
5
+ #
6
+ # It provides the interface to the core extensions.
7
+ #
2
8
  class Table
3
9
 
4
10
  @@instance = nil
11
+ # Returns the current instance or creates a new one.
5
12
  def self.instance
6
13
  # THREAD CRITICAL BEGIN
7
14
  @@instance ||= self.new
8
15
  # TRREAD CRITICAL END
9
16
  end
10
17
 
11
- @@hook_var = :@@__multimethods
12
-
13
- attr_accessor :method
18
+
19
+ # A list of all Multimethod objects.
20
+ attr_accessor :multimethod
21
+
22
+ # Creates a new Table object.
14
23
  def initialize(*opts)
15
24
  @multimethod_by_name = { }
16
25
  @multimethod = [ ]
@@ -20,7 +29,20 @@ module Multimethod
20
29
  end
21
30
 
22
31
 
23
- @@match_def = /^\s*def\s+(\w+)([(](.*)[)])?/
32
+ # Installs a new Multimethod Method using the multimethod syntax:
33
+ #
34
+ # class A
35
+ # multimethod q{
36
+ # def foo(x)
37
+ # ...
38
+ # end
39
+ # }
40
+ # multimethod q{
41
+ # def foo(A x)
42
+ # end
43
+ # }
44
+ # end
45
+ #
24
46
  # Interface to Multimethod::Module mixin multimethod
25
47
  def install_method(mod, body, file = nil, line = nil)
26
48
  file ||= __FILE__
@@ -55,12 +77,16 @@ module Multimethod
55
77
  end
56
78
 
57
79
 
80
+ # Returns the Multimethods that matches a signature.
81
+ # The signature can be a String, Method or Signature object.
58
82
  def find_multimethod(x)
59
83
  case x
60
84
  when String
61
85
  signature = Signature.new(:string => x)
62
- when Method
86
+ when Method
63
87
  signature = x.signature
88
+ when Signature
89
+ signature = x
64
90
  end
65
91
 
66
92
  x = @multimethod.select{|mm| mm.matches_signature(signature)}
@@ -69,12 +95,17 @@ module Multimethod
69
95
  end
70
96
 
71
97
 
98
+ # Returns a list of all the Methods that match a signature.
99
+ #
100
+ # The signature can be a String, Method or Signature object.
72
101
  def find_method(x)
73
102
  case x
74
103
  when String
75
104
  signature = Signature.new(:string => x)
76
105
  when Method
77
106
  signature = x.signature
107
+ when Signature
108
+ signature = x
78
109
  end
79
110
 
80
111
  x = @multimethod.select{|mm| mm.matches_signature(signature)}
@@ -86,6 +117,11 @@ module Multimethod
86
117
  end
87
118
 
88
119
 
120
+ # Removed the Method that match a signature.
121
+ #
122
+ # The signature can be a String, Method or Signature object.
123
+ #
124
+ # Raises an error if more than one Method is found.
89
125
  def remove_method(signature)
90
126
  x = find_method(signature)
91
127
  raise("Found #{x.size} multimethods: #{x.inspect}") if x.size > 1
@@ -94,6 +130,9 @@ module Multimethod
94
130
  end
95
131
 
96
132
 
133
+ # Returns a Multimethod object for a method name.
134
+ #
135
+ # Will create a new Multimethod if needed.
97
136
  def lookup_multimethod(name)
98
137
  name = name.to_s
99
138
 
@@ -110,7 +149,7 @@ module Multimethod
110
149
  end
111
150
 
112
151
 
113
- # Interface to code generated by #install_dispatch.
152
+ # Dispatches to the appropriate Method based on name, receiver and arguments.
114
153
  def dispatch(name, rcvr, args)
115
154
  unless mm = @multimethod_by_name[name]
116
155
  raise NameError, 'No method for multmethod #{name}' unless mm
@@ -123,6 +162,7 @@ module Multimethod
123
162
  # Support
124
163
  #
125
164
 
165
+ # Returns the object for name, using the appropriate evaluation scope.
126
166
  def name_to_object(name, scope = nil, file = nil, line = nil)
127
167
  scope ||= Kernel
128
168
  # THREAD CRITICAL BEGIN
@@ -47,39 +47,54 @@ class C < Object
47
47
  include Comparable
48
48
  end
49
49
 
50
+
50
51
  class D < B
51
52
  # Variadic
52
53
  multimethod %q{
53
- def bar(x)
54
- x = "D#bar(x) : (#{x.class.name})"
54
+ def bbb(x)
55
+ x = "D#bbb(x) : (#{x.class.name})"
56
+ x
57
+ end
58
+ }
59
+
60
+ multimethod %q{
61
+ def bbb(*rest)
62
+ x = "D#bbb(*rest) : (#{rest.collect{|x| x.class}.inspect})"
63
+ x
64
+ end
65
+ }
66
+
67
+ multimethod %q{
68
+ def bbb(x, y)
69
+ x = "D#bbb(x, y) : (#{x.class.name}, #{y.class.name})"
55
70
  x
56
71
  end
57
72
  }
58
73
 
59
74
  multimethod %q{
60
- def bar(*rest)
61
- x = "D#bar(*rest) : (#{rest.inspect})"
75
+ def bbb(Fixnum x, String y)
76
+ x = "D#bbb(Fixnum x, String y) : (#{x.class.name}, #{y.class.name})"
62
77
  x
63
78
  end
64
79
  }
65
80
 
66
81
  multimethod %q{
67
- def bar(x, y)
68
- x = "D#bar(x, y) : (#{x.class.name}, #{y.class.name})"
82
+ def bbb(Fixnum x, Fixnum y = 1)
83
+ x = "D#bbb(Fixnum x, Fixnum y = 1) : (#{x.class.name}, #{y.class.name})"
69
84
  x
70
85
  end
71
86
  }
72
87
 
73
88
  multimethod %q{
74
- def bar(x, y, A a)
75
- x = "D#bar(x, y, A a) : (#{x.class.name}, #{y.class.name}, #{a.class.name})"
89
+ def bbb(x, String y, A a)
90
+ x = "D#bbb(x, String y, A a) : (#{x.class.name}, #{y.class.name}, #{a.class.name})"
76
91
  x
77
92
  end
78
93
  }
79
94
 
80
95
  multimethod %q{
81
- def bar(x, y, *rest)
82
- x = "D#bar(x, y, *rest) : (#{x.class.name}, #{y.class.name}, #{rest.inspect})"
96
+ def bbb(Fixnum x, y, *rest)
97
+ x = "D#bbb(Fixnum x, y, *rest) : (#{x.class.name}, #{y.class.name}, #{rest.collect{|x| x.class}.inspect})"
83
98
  x
84
99
  end
85
100
  }
@@ -125,20 +140,38 @@ module Multimethod
125
140
  a = A.new
126
141
  d = D.new
127
142
 
128
- assert_equal 'D#bar(*rest) : ([])',
129
- d.bar()
143
+ assert_not_nil bbb_mm = ::Multimethod::Table.instance.multimethod.select{|mm| mm.name == 'bbb'}
144
+ assert_equal 1, bbb_mm.size
145
+ assert_kind_of ::Multimethod::Multimethod, bbb_mm = bbb_mm[0]
146
+
147
+ assert_equal 7, bbb_mm.method.size
148
+
149
+ assert_equal 'D#bbb(*rest) : ([])',
150
+ d.bbb()
151
+
152
+ assert_equal 'D#bbb(x) : (Symbol)',
153
+ d.bbb(:x)
154
+
155
+ assert_equal 'D#bbb(Fixnum x, String y) : (Fixnum, String)' ,
156
+ d.bbb(1, 'a')
157
+
158
+ assert_equal 'D#bbb(Fixnum x, Fixnum y = 1) : (Fixnum, Fixnum)' ,
159
+ d.bbb(1, 2)
160
+
161
+ assert_equal 'D#bbb(x, y) : (Symbol, Symbol)' ,
162
+ d.bbb(:x, :y)
130
163
 
131
- assert_equal 'D#bar(x) : (Fixnum)',
132
- d.bar(1)
164
+ assert_equal 'D#bbb(*rest) : ([Symbol, D, D])' ,
165
+ d.bbb(:x, d, d)
133
166
 
134
- assert_equal 'D#bar(x, y) : (Fixnum, String)' ,
135
- d.bar(1, 'a')
167
+ assert_equal 'D#bbb(Fixnum x, y, *rest) : (Fixnum, String, [A])' ,
168
+ d.bbb(1, 'a', a)
136
169
 
137
- assert_equal 'D#bar(x, y, A a) : (Fixnum, String, A)' ,
138
- d.bar(1, 'a', a)
170
+ assert_equal 'D#bbb(x, String y, A a) : (String, String, A)' ,
171
+ d.bbb('a', 'b', a)
139
172
 
140
- assert_equal 'D#bar(x, y, *rest) : (Fixnum, String, [3])' ,
141
- d.bar(1, 'a', 3)
173
+ assert_equal 'D#bbb(Fixnum x, y, *rest) : (Fixnum, String, [Fixnum])' ,
174
+ d.bbb(1, 'a', 3)
142
175
  end
143
176
 
144
177
  end # class
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: multimethod
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
7
- date: 2006-11-24 00:00:00 -05:00
6
+ version: 0.2.0
7
+ date: 2006-11-29 00:00:00 -05:00
8
8
  summary: "Supports Multimethod dispatching. For more details, see: http://multimethod.rubyforge.org/files/lib/multimethod_rb.html http://multimethod.rubyforge.org/files/README.txt http://multimethod.rubyforge.org/"
9
9
  require_paths:
10
10
  - lib