loverload 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/README.md +51 -1
- data/lib/loverload/method.rb +22 -31
- data/lib/loverload/version.rb +1 -1
- data/lib/loverload.rb +14 -4
- data/spec/loverload/loverload_spec.rb +183 -0
- metadata +22 -16
data/README.md
CHANGED
@@ -137,8 +137,58 @@ dummy.before_save(proc{|this| is proc}) #=> "Puke rainbow and leprechaun"
|
|
137
137
|
dummy.before_save('string') #=> "Puke rainbow, leprechaun, and gold"
|
138
138
|
```
|
139
139
|
|
140
|
+
It also support __functor__ 'guard' arguments
|
141
|
+
``` ruby
|
142
|
+
class Dummy
|
143
|
+
include Loverload
|
144
|
+
|
145
|
+
def_overload :hello do
|
146
|
+
with_params ->(num){ num.odd? } do |num|
|
147
|
+
"Odd"
|
148
|
+
end
|
149
|
+
|
150
|
+
with_params ->(num){ num.even? } do |num|
|
151
|
+
"Even"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
dummy = Dummy.new
|
157
|
+
dummy.hello(1) #=> 'Odd'
|
158
|
+
dummy.hello(2) #=> 'Even'
|
159
|
+
dummy.hello(100) #> 'Even'
|
160
|
+
```
|
161
|
+
|
162
|
+
It also support __functor__ fibonnaci with recursive method, but kinda reversed on method definition.
|
163
|
+
``` ruby
|
164
|
+
class Dummy
|
165
|
+
include Loverload
|
166
|
+
|
167
|
+
def_overload :fibonnaci do
|
168
|
+
with_params 1 do |n|
|
169
|
+
1
|
170
|
+
end
|
171
|
+
|
172
|
+
with_params 0 do |n|
|
173
|
+
0
|
174
|
+
end
|
175
|
+
|
176
|
+
with_params Integer do |n|
|
177
|
+
fibonnaci(n - 1) + fibonnaci(n - 2)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
dummy = Dummy.new
|
183
|
+
|
184
|
+
[*0..10].map(&dummy.method(:fibonnaci)) #=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
185
|
+
```
|
186
|
+
|
140
187
|
## TODO
|
141
|
-
-
|
188
|
+
- Cache?
|
189
|
+
|
190
|
+
## NOTES
|
191
|
+
Couple weeks ago, while searching for the same library, I found yanked gem called [overload](http://rubygems.org/gems/overload) from 2009, it's such an coincidence that we used similar implementation, inside it's readme, the author mentioned [functor](https://github.com/waves/functor), similar gem with different approach. Then I decided to change __loverload__ implementation using __functor__ approach, an simple but expensive approach.
|
142
192
|
|
143
193
|
## Contributing
|
144
194
|
|
data/lib/loverload/method.rb
CHANGED
@@ -1,49 +1,40 @@
|
|
1
1
|
module Loverload
|
2
2
|
class Method
|
3
3
|
def initialize klass, method_name, &with_params_block
|
4
|
-
@klass
|
5
|
-
@
|
4
|
+
@klass, @method_name = klass, method_name
|
5
|
+
@klass.instance_variable_set :@__dictionary__, {} unless
|
6
|
+
@klass.instance_variable_defined? :@__dictionary__
|
7
|
+
|
6
8
|
instance_eval(&with_params_block)
|
7
9
|
end
|
8
10
|
|
9
11
|
def with_params *pars, &block
|
10
|
-
|
11
|
-
method_name = "__name_#{ default }_#{ type_signature(pars) }"
|
12
|
-
method_alias = "__alias_#{ default }_#{ alias_signature(pars) }"
|
13
|
-
|
14
|
-
@klass.define_method method_name do |*args|
|
15
|
-
instance_exec(*args, &block)
|
16
|
-
end
|
17
|
-
|
18
|
-
@klass.send :alias_method, method_alias, method_name
|
19
|
-
@klass.send :alias_method, default, method_name
|
20
|
-
|
21
|
-
[default, method_name, method_alias].each{ |m| @klass.send :private, m }
|
12
|
+
dictionary[method_signature(block.arity, *pars)] = block
|
22
13
|
end
|
23
14
|
|
24
15
|
def overload instance, *args
|
25
|
-
|
26
|
-
method_name = "__name_#{ default }_#{ type_signature(args.map(&:class)) }"
|
27
|
-
method_alias = "__alias_#{ default }_#{ alias_signature(args.map(&:class)) }"
|
28
|
-
|
29
|
-
if instance.respond_to? method_name, true
|
30
|
-
instance.send method_name, *args
|
31
|
-
elsif instance.respond_to? method_alias, true
|
32
|
-
instance.send method_alias, *args
|
33
|
-
elsif instance.respond_to? default, true
|
34
|
-
instance.send default, *args
|
35
|
-
else
|
36
|
-
raise NoMethodError, "Undefined method '#{ @method_name }' for #{ type_signature(args.map(&:class)) }"
|
37
|
-
end
|
16
|
+
instance.instance_exec *args, &find(*args)
|
38
17
|
end
|
39
18
|
|
40
19
|
private
|
41
|
-
def
|
42
|
-
|
20
|
+
def find *args
|
21
|
+
dictionary.find do |signature, _|
|
22
|
+
match? signature, method_signature(args.size, *args)
|
23
|
+
end.tap{ |arr| raise NoMethodError unless arr }.last
|
24
|
+
end
|
25
|
+
|
26
|
+
def match? signature, args
|
27
|
+
signature.zip(args).all? do |type, arg|
|
28
|
+
type === arg
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def dictionary
|
33
|
+
@klass.instance_variable_get(:@__dictionary__)
|
43
34
|
end
|
44
35
|
|
45
|
-
def
|
46
|
-
|
36
|
+
def method_signature size, *meta
|
37
|
+
[*@method_name, size, *meta]
|
47
38
|
end
|
48
39
|
end
|
49
40
|
end
|
data/lib/loverload/version.rb
CHANGED
data/lib/loverload.rb
CHANGED
@@ -2,16 +2,26 @@ require "loverload/version"
|
|
2
2
|
require "loverload/method"
|
3
3
|
|
4
4
|
module Loverload
|
5
|
+
NULL = Object.new
|
6
|
+
|
5
7
|
def self.included(base)
|
6
8
|
base.extend(ClassMethods)
|
7
9
|
end
|
8
10
|
|
9
11
|
module ClassMethods
|
10
|
-
def def_overload method_name, &with_params_block
|
11
|
-
|
12
|
+
def def_overload method_name_or_class, method_name = NULL, &with_params_block
|
13
|
+
if method_name_or_class.is_a?(Class) && method_name != NULL
|
14
|
+
method = Method.new(method_name_or_class, method_name, &with_params_block)
|
15
|
+
|
16
|
+
define_singleton_method method_name do |*args|
|
17
|
+
method.overload(method_name_or_class, *args)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
method = Method.new(self, method_name_or_class, &with_params_block)
|
12
21
|
|
13
|
-
|
14
|
-
|
22
|
+
define_method method_name_or_class do |*args|
|
23
|
+
method.overload(self, *args)
|
24
|
+
end
|
15
25
|
end
|
16
26
|
end
|
17
27
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'loverload'
|
2
2
|
|
3
3
|
describe Loverload do
|
4
|
+
after { Dummy.send :remove_instance_variable, :@__dictionary__ }
|
5
|
+
|
4
6
|
it "makes your code more magical" do
|
5
7
|
class Dummy
|
6
8
|
include Loverload
|
@@ -26,6 +28,22 @@ describe Loverload do
|
|
26
28
|
dummy.hello('Teja', 21).should eql 'Hello Teja Age 21'
|
27
29
|
end
|
28
30
|
|
31
|
+
it "raise no method error" do
|
32
|
+
class Dummy
|
33
|
+
include Loverload
|
34
|
+
|
35
|
+
def_overload :hello do
|
36
|
+
with_params Fixnum do |n|
|
37
|
+
"Hello"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
dummy = Dummy.new
|
43
|
+
dummy.hello(1).should eql 'Hello'
|
44
|
+
expect{ dummy.hello('Teja') }.to raise_error
|
45
|
+
end
|
46
|
+
|
29
47
|
it "makes your code even more magical" do
|
30
48
|
class Dummy
|
31
49
|
include Loverload
|
@@ -165,4 +183,169 @@ describe Loverload do
|
|
165
183
|
dummy.method_2.should eql 'method_2'
|
166
184
|
dummy.method_2(1).should eql 'method_2 with arg'
|
167
185
|
end
|
186
|
+
|
187
|
+
it "can overload class methods" do
|
188
|
+
class Dummy
|
189
|
+
include Loverload
|
190
|
+
|
191
|
+
def_overload self, :hello do
|
192
|
+
with_params do
|
193
|
+
"Hello Nobody"
|
194
|
+
end
|
195
|
+
|
196
|
+
with_params do |name|
|
197
|
+
"Hello #{ name }"
|
198
|
+
end
|
199
|
+
|
200
|
+
with_params do |name, age|
|
201
|
+
"Hello #{ name } Age #{ age }"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
Dummy.hello.should eql 'Hello Nobody'
|
207
|
+
Dummy.hello('Teja').should eql 'Hello Teja'
|
208
|
+
Dummy.hello('Teja', 21).should eql 'Hello Teja Age 21'
|
209
|
+
end
|
210
|
+
|
211
|
+
it "can call another class methods" do
|
212
|
+
class Dummy
|
213
|
+
include Loverload
|
214
|
+
|
215
|
+
def self.hello_nobody
|
216
|
+
"Hello Nobody"
|
217
|
+
end
|
218
|
+
|
219
|
+
def_overload self, :hello do
|
220
|
+
with_params do
|
221
|
+
hello_nobody
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
Dummy.hello.should eql 'Hello Nobody'
|
227
|
+
end
|
228
|
+
|
229
|
+
it "can use immediate value" do
|
230
|
+
class Dummy
|
231
|
+
include Loverload
|
232
|
+
|
233
|
+
def_overload :hello do
|
234
|
+
with_params 1 do |num|
|
235
|
+
"One"
|
236
|
+
end
|
237
|
+
|
238
|
+
with_params 2 do |num|
|
239
|
+
"Two"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
dummy = Dummy.new
|
245
|
+
dummy.hello(1).should eql 'One'
|
246
|
+
dummy.hello(2).should eql 'Two'
|
247
|
+
end
|
248
|
+
|
249
|
+
it "works with inheritance" do
|
250
|
+
class Dummy
|
251
|
+
include Loverload
|
252
|
+
|
253
|
+
def_overload :hello do
|
254
|
+
with_params 1 do |num|
|
255
|
+
"One"
|
256
|
+
end
|
257
|
+
|
258
|
+
with_params 2 do |num|
|
259
|
+
"Two"
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class Dummy2 < Dummy
|
265
|
+
end
|
266
|
+
|
267
|
+
dummy = Dummy2.new
|
268
|
+
dummy.hello(1).should eql 'One'
|
269
|
+
dummy.hello(2).should eql 'Two'
|
270
|
+
end
|
271
|
+
|
272
|
+
it "works with inheritance and overrides" do
|
273
|
+
class Dummy
|
274
|
+
include Loverload
|
275
|
+
|
276
|
+
def_overload :hello do
|
277
|
+
with_params 1 do |num|
|
278
|
+
"One"
|
279
|
+
end
|
280
|
+
|
281
|
+
with_params 2 do |num|
|
282
|
+
"Two"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
class Dummy2 < Dummy
|
288
|
+
def_overload :hello do
|
289
|
+
with_params 1 do |num|
|
290
|
+
"1"
|
291
|
+
end
|
292
|
+
|
293
|
+
with_params 2 do |num|
|
294
|
+
"2"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
dummy = Dummy2.new
|
300
|
+
dummy.hello(1).should eql '1'
|
301
|
+
dummy.hello(2).should eql '2'
|
302
|
+
end
|
303
|
+
|
304
|
+
it "has `guard` signature method like functor" do
|
305
|
+
class Dummy
|
306
|
+
include Loverload
|
307
|
+
|
308
|
+
def_overload :hello do
|
309
|
+
with_params ->(num){ num.odd? } do |num|
|
310
|
+
"Odd"
|
311
|
+
end
|
312
|
+
|
313
|
+
with_params ->(num){ num.even? } do |num|
|
314
|
+
"Even"
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
dummy = Dummy.new
|
320
|
+
dummy.hello(1).should eql 'Odd'
|
321
|
+
dummy.hello(2).should eql 'Even'
|
322
|
+
dummy.hello(100).should eql 'Even'
|
323
|
+
end
|
324
|
+
|
325
|
+
it "can do fibonnaci like functor" do
|
326
|
+
# f.given( Integer ) { | n | f.call( n - 1 ) + f.call( n - 2 ) }
|
327
|
+
# f.given( 0 ) { |x| 0 }
|
328
|
+
# f.given( 1 ) { |x| 1 }
|
329
|
+
|
330
|
+
class Dummy
|
331
|
+
include Loverload
|
332
|
+
|
333
|
+
def_overload :fibonnaci do
|
334
|
+
with_params 1 do |n|
|
335
|
+
1
|
336
|
+
end
|
337
|
+
|
338
|
+
with_params 0 do |n|
|
339
|
+
0
|
340
|
+
end
|
341
|
+
|
342
|
+
with_params Integer do |n|
|
343
|
+
fibonnaci(n - 1) + fibonnaci(n - 2)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
dummy = Dummy.new
|
349
|
+
[*0..10].map(&dummy.method(:fibonnaci)).should eql [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
350
|
+
end
|
168
351
|
end
|
metadata
CHANGED
@@ -2,79 +2,79 @@
|
|
2
2
|
name: loverload
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0
|
5
|
+
version: 0.1.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Teja Sophista V.R.
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-07-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
prerelease: false
|
16
|
-
type: :development
|
17
16
|
name: bundler
|
17
|
+
type: :development
|
18
18
|
version_requirements: !ruby/object:Gem::Requirement
|
19
|
-
none: false
|
20
19
|
requirements:
|
21
20
|
- - ~>
|
22
21
|
- !ruby/object:Gem::Version
|
23
22
|
version: '1.3'
|
24
|
-
requirement: !ruby/object:Gem::Requirement
|
25
23
|
none: false
|
24
|
+
requirement: !ruby/object:Gem::Requirement
|
26
25
|
requirements:
|
27
26
|
- - ~>
|
28
27
|
- !ruby/object:Gem::Version
|
29
28
|
version: '1.3'
|
29
|
+
none: false
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
prerelease: false
|
32
|
-
type: :development
|
33
32
|
name: rake
|
33
|
+
type: :development
|
34
34
|
version_requirements: !ruby/object:Gem::Requirement
|
35
|
-
none: false
|
36
35
|
requirements:
|
37
36
|
- - ! '>='
|
38
37
|
- !ruby/object:Gem::Version
|
39
38
|
version: '0'
|
40
|
-
requirement: !ruby/object:Gem::Requirement
|
41
39
|
none: false
|
40
|
+
requirement: !ruby/object:Gem::Requirement
|
42
41
|
requirements:
|
43
42
|
- - ! '>='
|
44
43
|
- !ruby/object:Gem::Version
|
45
44
|
version: '0'
|
45
|
+
none: false
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
prerelease: false
|
48
|
-
type: :development
|
49
48
|
name: rspec
|
49
|
+
type: :development
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
none: false
|
52
51
|
requirements:
|
53
52
|
- - ! '>='
|
54
53
|
- !ruby/object:Gem::Version
|
55
54
|
version: '0'
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
57
55
|
none: false
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
58
57
|
requirements:
|
59
58
|
- - ! '>='
|
60
59
|
- !ruby/object:Gem::Version
|
61
60
|
version: '0'
|
61
|
+
none: false
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
63
|
prerelease: false
|
64
|
-
type: :development
|
65
64
|
name: debugger
|
65
|
+
type: :development
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
67
|
-
none: false
|
68
67
|
requirements:
|
69
68
|
- - ! '>='
|
70
69
|
- !ruby/object:Gem::Version
|
71
70
|
version: '0'
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
71
|
none: false
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
74
73
|
requirements:
|
75
74
|
- - ! '>='
|
76
75
|
- !ruby/object:Gem::Version
|
77
76
|
version: '0'
|
77
|
+
none: false
|
78
78
|
description: DSL for building method overloading in Ruby more magical
|
79
79
|
email:
|
80
80
|
- tejanium@yahoo.com
|
@@ -100,17 +100,23 @@ rdoc_options: []
|
|
100
100
|
require_paths:
|
101
101
|
- lib
|
102
102
|
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
-
none: false
|
104
103
|
requirements:
|
105
104
|
- - ! '>='
|
106
105
|
- !ruby/object:Gem::Version
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
hash: 2984216650687558397
|
107
109
|
version: '0'
|
108
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
110
|
none: false
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
112
|
requirements:
|
111
113
|
- - ! '>='
|
112
114
|
- !ruby/object:Gem::Version
|
115
|
+
segments:
|
116
|
+
- 0
|
117
|
+
hash: 2984216650687558397
|
113
118
|
version: '0'
|
119
|
+
none: false
|
114
120
|
requirements: []
|
115
121
|
rubyforge_project:
|
116
122
|
rubygems_version: 1.8.25
|