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
@@ -10,8 +10,10 @@ module Multimethod
10
10
 
11
11
  @@hook_var = :@@__multimethods
12
12
 
13
+ attr_accessor :method
13
14
  def initialize(*opts)
14
- @method = { }
15
+ @multimethod_by_name = { }
16
+ @multimethod = [ ]
15
17
 
16
18
  # Type name lookup cache
17
19
  @name_to_object = { }
@@ -21,33 +23,74 @@ module Multimethod
21
23
  @@match_def = /^\s*def\s+(\w+)([(](.*)[)])?/
22
24
  # Interface to Multimethod::Module mixin multimethod
23
25
  def install_method(mod, body, file = nil, line = nil)
24
- name = nil
25
- params = nil
26
-
27
- # Parse first line.
28
- if md = body.match(@@match_def)
29
- name = md[1]
30
- params = md[3] || ''
31
- else
32
- raise("Cannot determine name(params) from body")
33
- end
34
-
26
+ file ||= __FILE__
27
+ line ||= __LINE__
28
+ verbose = nil
29
+ #if body =~ /def bar\(x, y\)/
30
+ # verbose = 1
31
+ #end
32
+
33
+ # Parse the signature from the method body
34
+ signature = Signature.new
35
+ signature.mod = mod
36
+ signature.verbose = verbose
37
+ signature.file = file
38
+ signature.line = line
39
+ new_body = signature.scan_string(body.clone)
40
+
35
41
  # Get our Multimethod for this
36
- mm = lookup_multimethod(name)
42
+ mm = lookup_multimethod(signature.name)
37
43
  mm.install_dispatch(mod)
38
- m = mm.new_method(mod, params)
39
-
40
- # Evaluate our method.
41
- new_body = body.clone
42
- new_body.sub!(@@match_def, m.to_ruby)
44
+ m = mm.new_method_from_signature(signature)
45
+
46
+ # Replace the multimethod signature with a plain Ruby signature.
47
+ new_body = m.to_ruby_def + new_body
48
+
49
+ #if true || m.signature.restarg
50
+ # $stderr.puts "install_method(#{mod}) => #{m.to_ruby_signature}:\n#{new_body}"
51
+ #end
52
+
53
+ # Evaluate the new method body.
54
+ mod.module_eval(new_body, file, line)
55
+ end
56
+
57
+
58
+ def find_multimethod(x)
59
+ case x
60
+ when String
61
+ signature = Signature.new(:string => x)
62
+ when Method
63
+ signature = x.signature
64
+ end
65
+
66
+ x = @multimethod.select{|mm| mm.matches_signature(signature)}
67
+
68
+ x
69
+ end
70
+
43
71
 
44
- # if m.restarg
45
- # $stderr.puts "install_method(#{mod}) => #{m.to_ruby}:\n#{new_body}"
46
- # end
72
+ def find_method(x)
73
+ case x
74
+ when String
75
+ signature = Signature.new(:string => x)
76
+ when Method
77
+ signature = x.signature
78
+ end
79
+
80
+ x = @multimethod.select{|mm| mm.matches_signature(signature)}
81
+ # $stderr.puts "find_method(#{x}) => #{x.inspect}"
82
+ x = x.collect{|mm| mm.find_method(signature)}.flatten
47
83
 
48
- file ||= __FILE__; line ||= __LINE__
49
- m.file = file; m.line = line
50
- mod.module_eval(new_body, file || __FILE__, line || __LINE__)
84
+ # $stderr.puts "find_method(#{x}) => #{x.inspect}"
85
+ x
86
+ end
87
+
88
+
89
+ def remove_method(signature)
90
+ x = find_method(signature)
91
+ raise("Found #{x.size} multimethods: #{x.inspect}") if x.size > 1
92
+ x = x[0]
93
+ x.multimethod.remove_method(x)
51
94
  end
52
95
 
53
96
 
@@ -55,10 +98,11 @@ module Multimethod
55
98
  name = name.to_s
56
99
 
57
100
  # THREAD CRITICAL BEGIN
58
- unless mm = @method[name]
101
+ unless mm = @multimethod_by_name[name]
59
102
  mm = Multimethod.new(name)
60
103
  mm.table = self
61
- @method[name] = mm
104
+ @multimethod_by_name[name] = mm
105
+ @multimethod << mm
62
106
  end
63
107
  # THREAD CRITICAL END
64
108
 
@@ -68,7 +112,7 @@ module Multimethod
68
112
 
69
113
  # Interface to code generated by #install_dispatch.
70
114
  def dispatch(name, rcvr, args)
71
- unless mm = @method[name]
115
+ unless mm = @multimethod_by_name[name]
72
116
  raise NameError, 'No method for multmethod #{name}' unless mm
73
117
  end
74
118
  mm.dispatch(rcvr, args)
@@ -79,15 +123,16 @@ module Multimethod
79
123
  # Support
80
124
  #
81
125
 
82
- def name_to_object(name, scope = nil, method = nil)
126
+ def name_to_object(name, scope = nil, file = nil, line = nil)
83
127
  scope ||= Kernel
84
- # THREAD CRITICAL
128
+ # THREAD CRITICAL BEGIN
85
129
  unless x = (@name_to_object[scope] ||= { })[name]
86
130
  # $stderr.puts " name_to_object(#{name.inspect}) in #{scope}"
87
131
  x =
88
132
  @name_to_object[scope][name] =
89
- scope.module_eval(name, method ? method.file : __FILE__, method ? method.line : __LINE__)
133
+ scope.module_eval(name, file || __FILE__, line || __LINE__)
90
134
  end
135
+ # THREAD CRITICAL END
91
136
 
92
137
  x
93
138
  end
@@ -0,0 +1,99 @@
1
+ require 'test_base'
2
+
3
+
4
+ class AddRemoveA < Object
5
+ end
6
+
7
+ module Multimethod
8
+
9
+ class AddRemoveTest < TestBase
10
+
11
+ def setup
12
+ super
13
+
14
+ AddRemoveA.class_eval {
15
+ multimethod %q{
16
+ def asdfwert(x)
17
+ x = "A#asdfwert(x) : (#{x.class.name})"
18
+ x
19
+ end
20
+ }
21
+
22
+ multimethod %q{
23
+ def asdfwert(AddRemoveA x)
24
+ x = "A#asdfwert(A x) : (#{x.class.name})"
25
+ x
26
+ end
27
+ }
28
+
29
+ }
30
+
31
+ end
32
+
33
+
34
+ def test_find_multimethod
35
+ assert_not_nil mm = Table.instance.find_multimethod("AddRemoveA#asdfwert(x)")
36
+ assert 1, mm.size
37
+ mm = mm[0]
38
+ assert_not_nil mm
39
+ assert_equal 'asdfwert', mm.name
40
+
41
+ mm
42
+ end
43
+
44
+
45
+ def test_remove
46
+ # Check method counts
47
+ assert_method_count(2)
48
+
49
+ # Remove asdfwert(AddRemoveA x)
50
+ assert_not_nil m = Table.instance.find_method("AddRemoveA#asdfwert(AddRemoveA x)")
51
+ assert 1, m.size
52
+ m = m[0]
53
+ assert_not_nil m
54
+ assert_not_nil mm = m.multimethod
55
+ assert_equal 'AddRemoveA#asdfwert(AddRemoveA x)', m.signature.to_s
56
+ assert_equal AddRemoveA, m.signature.mod
57
+ assert_equal AddRemoveA, m.signature.parameter[1].type_object
58
+
59
+ AddRemoveA.remove_multimethod(m)
60
+
61
+ assert_not_nil m = Table.instance.find_method("AddRemoveA#asdfwert(AddRemoveA x)")
62
+ assert 0, m.size
63
+
64
+ assert_method_count(1)
65
+
66
+ # Remove asdfwert(x)
67
+ assert_not_nil m = Table.instance.find_method("AddRemoveA#asdfwert(x)")
68
+ assert 1, m.size
69
+ m = m[0]
70
+ assert_not_nil m
71
+ assert_not_nil mm = m.multimethod
72
+ assert_equal 'AddRemoveA#asdfwert(Kernel x)', m.signature.to_s
73
+ assert_equal AddRemoveA, m.signature.mod
74
+ assert_equal Kernel, m.signature.parameter[1].type_object
75
+
76
+ AddRemoveA.remove_multimethod(m)
77
+
78
+ assert_not_nil m = Table.instance.find_method("AddRemoveA#asdfwert(x)")
79
+ assert 0, m.size
80
+
81
+ assert_method_count(1, 0)
82
+ end
83
+
84
+
85
+ def assert_method_count(impl, dispatch = 1)
86
+ mm = test_find_multimethod
87
+
88
+ methods = AddRemoveA.instance_methods(false).select{|x| x =~ /\d_asdfwert$/}
89
+ assert impl, methods.size
90
+ assert impl, mm.method.size
91
+
92
+ methods = AddRemoveA.instance_methods(false).select{|x| x = 'asdfwert'}
93
+ assert dispatch, methods.size
94
+ end
95
+
96
+
97
+ end # class
98
+
99
+ end # module
@@ -13,74 +13,9 @@ module Multimethod
13
13
  super
14
14
  end
15
15
 
16
- def test_scan_parameter
17
- assert_not_nil m1 = Method.new(Object, :m, [ A, :a, B, :b, :c, '*d' ])
18
16
 
19
- assert_equal 5, m1.parameter.size
20
-
21
- i = -1
22
-
23
- i = i + 1
24
- assert_equal :self, m1.parameter[i].name
25
- assert_equal Object, m1.parameter[i].type
26
- assert ! m1.parameter[i].restarg
27
-
28
- i = i + 1
29
- assert_equal :a, m1.parameter[i].name
30
- assert_equal A, m1.parameter[i].type
31
- assert ! m1.parameter[i].restarg
32
-
33
- i = i + 1
34
- assert_equal :b, m1.parameter[i].name
35
- assert_equal B, m1.parameter[i].type
36
- assert ! m1.parameter[i].restarg
37
-
38
- i = i + 1
39
- assert_equal :c, m1.parameter[i].name
40
- assert_equal Kernel, m1.parameter[i].type
41
- assert ! m1.parameter[i].restarg
42
-
43
- i = i + 1
44
- assert_equal :d, m1.parameter[i].name
45
- assert_equal Kernel, m1.parameter[i].type
46
- assert m1.parameter[i].restarg
47
-
48
- m1
49
- end
50
-
51
- def test_scan_parameter_string
52
- assert_not_nil m1 = Method.new(Object, :m, 'A a, B b, c = nil, *d')
53
-
54
- assert_equal 5, m1.parameter.size
55
-
56
- i = -1
57
-
58
- i = i + 1
59
- assert_equal :self, m1.parameter[i].name
60
- assert_equal Object, m1.parameter[i].type
61
- assert ! m1.parameter[i].restarg
62
-
63
- i = i + 1
64
- assert_equal :a, m1.parameter[i].name
65
- assert_equal 'A', m1.parameter[i].type
66
- assert ! m1.parameter[i].restarg
67
-
68
- i = i + 1
69
- assert_equal :b, m1.parameter[i].name
70
- assert_equal 'B', m1.parameter[i].type
71
- assert ! m1.parameter[i].restarg
72
-
73
- i = i + 1
74
- assert_equal :c, m1.parameter[i].name
75
- assert_equal Kernel, m1.parameter[i].type
76
- assert ! m1.parameter[i].restarg
77
-
78
- i = i + 1
79
- assert_equal :d, m1.parameter[i].name
80
- assert_equal Kernel, m1.parameter[i].type
81
- assert m1.parameter[i].restarg
82
-
83
- m1
17
+ def test_basic
18
+ true
84
19
  end
85
20
 
86
21
  end # class
@@ -10,10 +10,12 @@ module Multimethod
10
10
  class D < B; end
11
11
  class E < A; end
12
12
 
13
+
13
14
  def setup
14
15
  super
15
16
  end
16
17
 
18
+
17
19
  def test_score
18
20
  # Make some argument lists.
19
21
  types = [ A, B, C, D, E ]
@@ -36,26 +38,26 @@ module Multimethod
36
38
 
37
39
  rs = Parameter::RESTARG_SCORE
38
40
 
39
- assert_not_nil m1 = Method.new(Object, :m, [ A, :x, A, :y ])
40
- assert_equal 3, m1.min_args
41
- assert_equal 3, m1.max_args
41
+ assert_not_nil m1 = Method.new(:m_1, Object, :m, [ A, :x, A, :y ])
42
+ assert_equal 3, m1.signature.min_args
43
+ assert_equal 3, m1.signature.max_args
42
44
 
43
- assert_not_nil m2 = Method.new(Object, :m, [ B, :x, A, :y ])
44
- assert_equal 3, m2.min_args
45
- assert_equal 3, m2.max_args
45
+ assert_not_nil m2 = Method.new(:m_2, Object, :m, [ B, :x, A, :y ])
46
+ assert_equal 3, m2.signature.min_args
47
+ assert_equal 3, m2.signature.max_args
46
48
 
47
- assert_not_nil m3 = Method.new(Object, :m, [ C, :x, '*rest' ])
48
- assert_equal 2, m3.min_args
49
- assert_equal nil, m3.max_args
50
- assert_not_nil m3.restarg
49
+ assert_not_nil m3 = Method.new(:m_3, Object, :m, [ C, :x, '*rest' ])
50
+ assert_equal 2, m3.signature.min_args
51
+ assert_equal nil, m3.signature.max_args
52
+ assert_not_nil m3.signature.restarg
51
53
 
52
- assert_not_nil m4 = Method.new(Object, :m, [ D, :x, :y ])
53
- assert_equal 3, m4.min_args
54
- assert_equal 3, m4.max_args
54
+ assert_not_nil m4 = Method.new(:m_4, Object, :m, [ D, :x, :y ])
55
+ assert_equal 3, m4.signature.min_args
56
+ assert_equal 3, m4.signature.max_args
55
57
 
56
- assert_not_nil m5 = Method.new(Object, :m, [ E, :y, :y ])
57
- assert_equal 3, m4.min_args
58
- assert_equal 3, m4.max_args
58
+ assert_not_nil m5 = Method.new(:m_5, Object, :m, [ E, :y, :y ])
59
+ assert_equal 3, m4.signature.min_args
60
+ assert_equal 3, m4.signature.max_args
59
61
 
60
62
  # 5 == 1.class.ancestors.index(Object)
61
63
  assert_equal [ 5, 0, 0 ] , m1.score(c[:a_a])
@@ -0,0 +1,190 @@
1
+ require 'test_base'
2
+
3
+ module Multimethod
4
+
5
+ class SignatureTest < TestBase
6
+
7
+ class A < Object; end
8
+ class B < A; end
9
+ class C < Object; end
10
+ class D < B; end
11
+
12
+
13
+ def setup
14
+ super
15
+ end
16
+
17
+
18
+ def test_scan_parameter
19
+ assert_not_nil m1 = Signature.new(:mod => Object,
20
+ :name => :m,
21
+ :parameter => [ A, :a, B, :b, :c, '*d' ])
22
+
23
+ assert_equal 5, m1.parameter.size
24
+
25
+ i = -1
26
+
27
+ i = i + 1
28
+ assert_equal :self, m1.parameter[i].name
29
+ assert_equal Object, m1.parameter[i].type
30
+ assert ! m1.parameter[i].restarg
31
+ assert ! m1.parameter[i].default
32
+
33
+ i = i + 1
34
+ assert_equal :a, m1.parameter[i].name
35
+ assert_equal A, m1.parameter[i].type
36
+ assert ! m1.parameter[i].restarg
37
+ assert ! m1.parameter[i].default
38
+
39
+ i = i + 1
40
+ assert_equal :b, m1.parameter[i].name
41
+ assert_equal B, m1.parameter[i].type
42
+ assert ! m1.parameter[i].restarg
43
+ assert ! m1.parameter[i].default
44
+
45
+ i = i + 1
46
+ assert_equal :c, m1.parameter[i].name
47
+ assert_equal Kernel, m1.parameter[i].type
48
+ assert ! m1.parameter[i].restarg
49
+ assert ! m1.parameter[i].default
50
+
51
+ i = i + 1
52
+ assert_equal :d, m1.parameter[i].name
53
+ assert_equal Kernel, m1.parameter[i].type
54
+ assert m1.parameter[i].restarg
55
+ assert ! m1.parameter[i].default
56
+
57
+ m1
58
+ end
59
+
60
+
61
+ def assert_signature(m1)
62
+ assert_equal 5, m1.parameter.size
63
+
64
+ i = -1
65
+
66
+ i = i + 1
67
+ assert_equal :self, m1.parameter[i].name
68
+ assert_equal Object, m1.parameter[i].type
69
+ assert ! m1.parameter[i].restarg
70
+ assert ! m1.parameter[i].default
71
+
72
+ i = i + 1
73
+ assert_equal :a, m1.parameter[i].name
74
+ assert_equal 'A', m1.parameter[i].type
75
+ assert ! m1.parameter[i].restarg
76
+ assert ! m1.parameter[i].default
77
+
78
+ i = i + 1
79
+ assert_equal :b, m1.parameter[i].name
80
+ assert_equal 'B', m1.parameter[i].type
81
+ assert ! m1.parameter[i].restarg
82
+
83
+ i = i + 1
84
+ assert_equal :c, m1.parameter[i].name
85
+ assert_equal Kernel, m1.parameter[i].type
86
+ assert ! m1.parameter[i].restarg
87
+ assert_equal 'nil', m1.parameter[i].default
88
+
89
+ i = i + 1
90
+ assert_equal :d, m1.parameter[i].name
91
+ assert_equal Kernel, m1.parameter[i].type
92
+ assert m1.parameter[i].restarg
93
+ assert ! m1.parameter[i].default
94
+
95
+ m1
96
+ end
97
+
98
+
99
+ def test_scan_parameter_string
100
+ assert_not_nil m1 = Signature.new(:mod => Object, :name => :m, :parameter => 'A a, B b, c = nil, *d')
101
+ assert_signature m1
102
+ assert ! m1.class_method
103
+ end
104
+
105
+
106
+ def test_scan_string
107
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
108
+
109
+ assert_signature m1
110
+ assert ! m1.class_method
111
+ end
112
+
113
+
114
+ def test_scan_string_2
115
+ assert_not_nil m1 = Signature.new(:string => 'Object def m(A a, B b, c = nil, *d)')
116
+
117
+ assert_signature m1
118
+ assert ! m1.class_method
119
+ end
120
+
121
+
122
+ def test_scan_string_class_method
123
+ assert_not_nil m1 = Signature.new(:string => 'Object.m(A a, B b, c = nil, *d)')
124
+
125
+ assert_signature m1
126
+ assert m1.class_method
127
+ end
128
+
129
+
130
+ def test_scan_string_class_method_2
131
+ assert_not_nil m1 = Signature.new(:string => 'Object def self.m(A a, B b, c = nil, *d)')
132
+
133
+ assert_signature m1
134
+ assert m1.class_method
135
+ end
136
+
137
+
138
+ def test_scan_string_balanced_parens
139
+ assert_not_nil m1 = Signature.new(:string => 'Object.m(A a, B b = call_method(foo(bar, "45,67"), \',\'), c = nil, *d)')
140
+
141
+ assert_signature m1
142
+ assert m1.class_method
143
+ assert_equal :b, m1.parameter[2].name
144
+ assert m1.parameter[2].default
145
+ assert_equal 'call_method(foo(bar, "45,67"), \',\')', m1.parameter[2].default
146
+ end
147
+
148
+ def test_scan_default_at_end
149
+ assert_not_nil m1 = Signature.new(:string => 'Object.m(A a, B b = call_method(foo(bar, "45,67"), \',\'), c = nil)')
150
+
151
+ assert m1.class_method
152
+ assert_equal :c, m1.parameter[3].name
153
+ assert m1.parameter[3].default
154
+ assert_equal 'nil', m1.parameter[3].default
155
+ end
156
+
157
+
158
+ def test_cmp
159
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
160
+ assert_not_nil m2 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
161
+
162
+ assert m1 == m2
163
+
164
+
165
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
166
+ assert_not_nil m2 = Signature.new(:string => 'Object#x(A a, B b, c = nil, *d)')
167
+
168
+ assert m1 != m2
169
+
170
+
171
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
172
+ assert_not_nil m2 = Signature.new(:string => 'Object#m(A q, B x, c = nil, *args)')
173
+
174
+ assert m1 == m2
175
+
176
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
177
+ assert_not_nil m2 = Signature.new(:string => 'Object#m(A q, B x, c = nil, d)')
178
+
179
+ assert m1 != m2
180
+
181
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
182
+ assert_not_nil m2 = Signature.new(:string => 'Object#m(A q, B x, c = nil)')
183
+
184
+ assert m1 != m2
185
+ end
186
+
187
+ end # class
188
+
189
+ end # module
190
+