call-me 0.0.1
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/lib/call-me/memoize.rb +109 -0
- data/lib/call-me/named.rb +242 -0
- data/lib/call-me/overload.rb +76 -0
- data/lib/call-me/pattern-matching.rb +43 -0
- metadata +81 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'refining'
|
12
|
+
|
13
|
+
class Module
|
14
|
+
refine_method :method_added, :prefix => '__memoize' do |name|
|
15
|
+
next if name == 'temporary method for refining'
|
16
|
+
|
17
|
+
memoize(name) if @__to_memoize__
|
18
|
+
|
19
|
+
__memoize_method_added(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
refine_method :singleton_method_added, :prefix => '__memoize' do |name|
|
23
|
+
next if name == 'temporary method for refining'
|
24
|
+
|
25
|
+
singleton_memoize(name) if @__to_singleton_memoize__
|
26
|
+
|
27
|
+
__memoize_singleton_method_added(name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Object
|
32
|
+
def is_memoized? (name)
|
33
|
+
respond_to? "__memoize_#{name}"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Memoize the method +name+.
|
37
|
+
def memoize (name = nil)
|
38
|
+
return if @__to_memoize__ = !name
|
39
|
+
|
40
|
+
to_call = "__memoize_#{name}"
|
41
|
+
|
42
|
+
begin; if instance_method(name).arity == 0
|
43
|
+
refine_method name, :prefix => '__memoize' do
|
44
|
+
(memoize_cache[name][nil] ||= [__send__(to_call)])[0]
|
45
|
+
end
|
46
|
+
|
47
|
+
return
|
48
|
+
end; rescue; end
|
49
|
+
|
50
|
+
refine_method name, :prefix => '__memoize' do |*args|
|
51
|
+
if tmp = memoize_cache[name][args]
|
52
|
+
tmp
|
53
|
+
else
|
54
|
+
memoize_cache[name][__memoize_try_to_clone__(args)] = [__send__(*([to_call] + args))]
|
55
|
+
end[0]
|
56
|
+
end
|
57
|
+
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
# Memoize the singleton method +name+.
|
62
|
+
def singleton_memoize (name = nil)
|
63
|
+
return if @__to_singleton_memoize__ = !name
|
64
|
+
|
65
|
+
to_call = "__memoize_#{name}"
|
66
|
+
|
67
|
+
begin; if method(name).arity == 0
|
68
|
+
refine_singleton_method name, :prefix => '__memoize' do
|
69
|
+
(memoize_cache[name][nil] ||= [__send__(to_call)])[0]
|
70
|
+
end
|
71
|
+
|
72
|
+
return
|
73
|
+
end; rescue; end
|
74
|
+
|
75
|
+
refine_singleton_method name, :prefix => '__memoize' do |*args, &block|
|
76
|
+
if tmp = memoize_cache[name][args]
|
77
|
+
tmp
|
78
|
+
else
|
79
|
+
memoize_cache[name][__memoize_try_to_clone__(args)] = [__send__(*([to_call] + args))]
|
80
|
+
end[0]
|
81
|
+
end
|
82
|
+
|
83
|
+
nil
|
84
|
+
end; alias singleton_memoize singleton_memoize
|
85
|
+
|
86
|
+
# Clear the memoize cache completely or only for the method +name+
|
87
|
+
def memoize_clear (name = nil)
|
88
|
+
if name
|
89
|
+
memoize_cache.delete(name.to_sym)
|
90
|
+
else
|
91
|
+
memoize_cache.clear
|
92
|
+
end
|
93
|
+
end; alias memoize_clear memoize_clear
|
94
|
+
|
95
|
+
# Get the memoization cache
|
96
|
+
def memoize_cache
|
97
|
+
@__memoize_cache__ ||= Hash.new { |h, k| h[k] = {} }
|
98
|
+
end; alias memoize_cache memoize_cache
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def __memoize_try_to_clone__ (value) # :nodoc:
|
103
|
+
begin
|
104
|
+
Marshal.load(Marshal.dump(value))
|
105
|
+
rescue Exception
|
106
|
+
value
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'refining'
|
12
|
+
|
13
|
+
class Named
|
14
|
+
@warn = true
|
15
|
+
|
16
|
+
def self.warn (value)
|
17
|
+
@warn = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.warn?
|
21
|
+
@warn
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize (name)
|
25
|
+
@name = name.to_sym
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_sym
|
29
|
+
@name
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.normalize (*args)
|
33
|
+
options = Hash[
|
34
|
+
:optional => [],
|
35
|
+
:alias => {},
|
36
|
+
:rest => []
|
37
|
+
].merge(args.last.is_a?(Hash) ? args.pop : {})
|
38
|
+
|
39
|
+
method = args.shift.to_sym
|
40
|
+
names = args
|
41
|
+
|
42
|
+
options[:optional] = Hash[if options[:optional].is_a?(Range)
|
43
|
+
names[options[:optional]]
|
44
|
+
elsif options[:optional].is_a?(Hash)
|
45
|
+
[options[:optional]]
|
46
|
+
else
|
47
|
+
options[:optional]
|
48
|
+
end.map {|opt|
|
49
|
+
if opt.is_a?(Hash)
|
50
|
+
opt.to_a
|
51
|
+
else
|
52
|
+
[[opt, nil]]
|
53
|
+
end
|
54
|
+
}.flatten(1)]
|
55
|
+
|
56
|
+
return method, names, options
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.arguments (names, options, *args)
|
60
|
+
return args if (args.length != 1 || !args.first.is_a?(Hash)) || (options[:rest] && !args.last.is_a?(Hash))
|
61
|
+
|
62
|
+
parameters = args.pop
|
63
|
+
rest = args
|
64
|
+
args = []
|
65
|
+
|
66
|
+
# fix alias parameters
|
67
|
+
parameters.dup.each {|name, value|
|
68
|
+
if options[:alias].has_key?(name)
|
69
|
+
parameters[options[:alias][name]] = value
|
70
|
+
parameters.delete(name)
|
71
|
+
elsif name.is_a?(Integer) && !parameters[names[name - 1]].is_a?(Integer)
|
72
|
+
parameters[names[name - 1]] = value
|
73
|
+
parameters.delete(name)
|
74
|
+
end
|
75
|
+
}
|
76
|
+
|
77
|
+
# check if there are unknown parameters
|
78
|
+
parameters.keys.each {|parameter|
|
79
|
+
raise ArgumentError, "#{parameter} is an unknown parameter" unless names.member?(parameter)
|
80
|
+
}
|
81
|
+
|
82
|
+
# check for missing required parameters
|
83
|
+
(names - parameters.keys - options[:optional].keys).tap {|required|
|
84
|
+
raise ArgumentError, "the following required parameters are missing: #{required.join(', ')}" unless required.empty?
|
85
|
+
} unless options[:optional] == true
|
86
|
+
|
87
|
+
all_optional_after = names.length - names.reverse.take_while {|name|
|
88
|
+
options[:optional].has_key?(name) && !parameters.has_key?(name)
|
89
|
+
}.length
|
90
|
+
|
91
|
+
# fill the arguments array
|
92
|
+
# TODO: try to not add nil for the last optional parameters
|
93
|
+
names.each_with_index {|name, index|
|
94
|
+
if parameters.has_key?(name)
|
95
|
+
if options[:rest].member?(name)
|
96
|
+
args.push(*parameters[name])
|
97
|
+
else
|
98
|
+
args << parameters[name]
|
99
|
+
end
|
100
|
+
else
|
101
|
+
if index < all_optional_after
|
102
|
+
warn 'keep in mind that optionals between two arguments will have nil as value' if Named.warn?
|
103
|
+
end
|
104
|
+
|
105
|
+
if options[:optional][name].nil? && index >= all_optional_after
|
106
|
+
break
|
107
|
+
end
|
108
|
+
|
109
|
+
args << options[:optional][name]
|
110
|
+
end
|
111
|
+
}
|
112
|
+
|
113
|
+
args
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.definition (method)
|
117
|
+
names = []
|
118
|
+
options = { :rest => [], :optional => [] }
|
119
|
+
|
120
|
+
if method.respond_to? :parameters
|
121
|
+
method.parameters.map {|how, name|
|
122
|
+
if name
|
123
|
+
names << name
|
124
|
+
|
125
|
+
options[:optional] << name if how == :opt
|
126
|
+
options[:rest] << name if how == :rest
|
127
|
+
else
|
128
|
+
names << rand.to_s
|
129
|
+
options[:rest] << names.last
|
130
|
+
end
|
131
|
+
}
|
132
|
+
else
|
133
|
+
if method.arity > 0
|
134
|
+
names.push(*(1 .. method.arity))
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
[names, options]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
module Kernel
|
143
|
+
def always_named!
|
144
|
+
@always_named = true
|
145
|
+
end
|
146
|
+
|
147
|
+
def always_named?
|
148
|
+
@always_named
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class Module
|
153
|
+
refine_method :method_added, prefix: '__named' do |name|
|
154
|
+
next if name == 'temporary method for refining'
|
155
|
+
|
156
|
+
@__named_last_method__ = name
|
157
|
+
|
158
|
+
if @__to_namedify__
|
159
|
+
named(Named.new(@__named_last_method__), *@__to_namedify__)
|
160
|
+
elsif always_named?
|
161
|
+
namedc(nil)
|
162
|
+
end
|
163
|
+
|
164
|
+
__named_method_added(name)
|
165
|
+
end
|
166
|
+
|
167
|
+
refine_method :singleton_method_added, prefix: '__named' do |name|
|
168
|
+
next if name == 'temporary method for refining'
|
169
|
+
|
170
|
+
@__singleton_named_last_method__ = name
|
171
|
+
|
172
|
+
if @__to_singleton_namedify__
|
173
|
+
singleton_named(Named.new(@__singleton_named_last_method__), *@__to_singleton_namedify__)
|
174
|
+
elsif always_named?
|
175
|
+
singleton_named(nil)
|
176
|
+
end
|
177
|
+
|
178
|
+
__named_singleton_method_added(name)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class Object
|
183
|
+
def named (*args)
|
184
|
+
raise ArgumentError, 'you have to pass at least one argument' if args.length == 0
|
185
|
+
|
186
|
+
if args.first.nil?
|
187
|
+
if @__named_last_method__
|
188
|
+
names, options = Named.definition(instance_method(@__named_last_method__))
|
189
|
+
|
190
|
+
named(Named.new(@__named_last_method__), *(names + [options]))
|
191
|
+
end; true
|
192
|
+
elsif !args.first.is_a?(Named)
|
193
|
+
@__to_namedify__ = args
|
194
|
+
end and return
|
195
|
+
|
196
|
+
@__to_namedify__ = false
|
197
|
+
|
198
|
+
method, names, options = Named.normalize(*args)
|
199
|
+
|
200
|
+
instance_method(method).tap {|m|
|
201
|
+
raise ArgumentError, 'method arity mismatch' if m.arity > 0 && m.arity != names.length
|
202
|
+
}
|
203
|
+
|
204
|
+
to_call = "__named_#{method}"
|
205
|
+
|
206
|
+
refine_method method, :prefix => '__named' do |*args, &block|
|
207
|
+
__send__ *([to_call] + Named.arguments(names, options, *args)), &block
|
208
|
+
end
|
209
|
+
|
210
|
+
nil
|
211
|
+
end
|
212
|
+
|
213
|
+
def singleton_named (*args)
|
214
|
+
raise ArgumentError, 'you have to pass at least one argument' if args.length == 0
|
215
|
+
|
216
|
+
if args.first.nil?
|
217
|
+
if @__singleton_named_last_method__
|
218
|
+
names, options = Named.definition(method(@__singleton_named_last_method__))
|
219
|
+
|
220
|
+
singleton_named(Named.new(@__singleton_named_last_method__), *(names + [options]))
|
221
|
+
end; true
|
222
|
+
elsif !args.first.is_a?(Named)
|
223
|
+
@__to_singleton_namedify__ = args
|
224
|
+
end and return
|
225
|
+
|
226
|
+
@__to_singleton_namedify__ = false
|
227
|
+
|
228
|
+
method, names, options = Named.normalize(*args)
|
229
|
+
|
230
|
+
method(method).tap {|m|
|
231
|
+
raise ArgumentError, 'method arity mismatch' if m.arity > 0 && m.arity != names.length
|
232
|
+
}
|
233
|
+
|
234
|
+
to_call = "__named_#{method}"
|
235
|
+
|
236
|
+
refine_singleton_method method, :prefix => '__named' do |*args, &block|
|
237
|
+
__send__ *([to_call] + Named.arguments(names, options, *args)), &block
|
238
|
+
end
|
239
|
+
|
240
|
+
nil
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'refining'
|
12
|
+
|
13
|
+
class Module
|
14
|
+
private
|
15
|
+
|
16
|
+
def __overload (name)
|
17
|
+
overloaded = (__overloaded__[name] ||= {})
|
18
|
+
overloaded[@__to_overload__ || :default] = instance_method(name)
|
19
|
+
|
20
|
+
remove_instance_variable :@__to_overload__
|
21
|
+
|
22
|
+
define_method name do |*args, &block|
|
23
|
+
overloaded.each {|signature, body|
|
24
|
+
next if signature == :default || signature.each_with_index.any? {|klass, index|
|
25
|
+
!args[index].is_a?(klass)
|
26
|
+
}
|
27
|
+
|
28
|
+
return body.bind(self).call(*args, &block)
|
29
|
+
}
|
30
|
+
|
31
|
+
return overloaded[:default].bind(self).call(*args, &block) if overloaded[:default]
|
32
|
+
|
33
|
+
raise ArgumentError, "the arguments don't match any signature"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def __overloaded__
|
38
|
+
@__overloaded__ ||= {}
|
39
|
+
end
|
40
|
+
|
41
|
+
public
|
42
|
+
|
43
|
+
def is_overloaded? (name)
|
44
|
+
__overloaded__.has_key?(name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def def_signature (*sign)
|
48
|
+
@__to_overload__ = sign
|
49
|
+
end
|
50
|
+
|
51
|
+
def define_overloadable (name, default = nil, matchers)
|
52
|
+
define_method name do |*args|
|
53
|
+
matchers.each {|signature, body|
|
54
|
+
return instance_exec *args, &body if (signature.is_a?(Array) ? signature : [signature]).each_with_index.all? {|klass, index|
|
55
|
+
args[index].is_a?(klass)
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
return instance_exec *args, &default if default
|
60
|
+
|
61
|
+
raise ArgumentError, "the arguments don't match any signature"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
refine_method :method_added, :prefix => '__overload' do |name|
|
66
|
+
next if name == 'temporary method for refining'
|
67
|
+
|
68
|
+
if !@__overloading__ && (@__to_overload__ || is_overloaded?(name))
|
69
|
+
@__overloading__ = true
|
70
|
+
__overload(name)
|
71
|
+
remove_instance_variable :@__overloading__
|
72
|
+
end
|
73
|
+
|
74
|
+
__overload_method_added(name)
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
class Module
|
12
|
+
private
|
13
|
+
|
14
|
+
public
|
15
|
+
|
16
|
+
def define_pattern_matched (name, default = nil, matchers)
|
17
|
+
define_method name do |*args|
|
18
|
+
matchers.each {|signature, body|
|
19
|
+
if signature.is_a?(Proc)
|
20
|
+
return instance_exec *args, &body if signature.call(*args)
|
21
|
+
else
|
22
|
+
return instance_exec *args, &body if (signature.is_a?(Array) ? signature : [signature]) == args
|
23
|
+
end
|
24
|
+
}
|
25
|
+
|
26
|
+
return instance_exec *args, &default if default
|
27
|
+
|
28
|
+
raise ArgumentError, "non-exhaustive patterns"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
refine_method :method_added, :prefix => '__pattern_match' do |name|
|
33
|
+
next if name == 'temporary method for refining'
|
34
|
+
|
35
|
+
if !@__pattern_matching__ && (@__to_pattern_match__ || is_pattern_matched?(name))
|
36
|
+
@__pattern_matching__ = true
|
37
|
+
__pattern_match(name)
|
38
|
+
remove_instance_variable :@__pattern_matching__
|
39
|
+
end
|
40
|
+
|
41
|
+
__pattern_match_method_added(name)
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: call-me
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- meh.
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: refining
|
16
|
+
requirement: &9935540 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *9935540
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &9934600 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *9934600
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &9933640 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *9933640
|
47
|
+
description:
|
48
|
+
email: meh@paranoici.org
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- lib/call-me/pattern-matching.rb
|
54
|
+
- lib/call-me/named.rb
|
55
|
+
- lib/call-me/overload.rb
|
56
|
+
- lib/call-me/memoize.rb
|
57
|
+
homepage: http://github.com/meh/ruby-call-me
|
58
|
+
licenses: []
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.8.10
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: Various calling things, overload, pattern matching, memoization and such.
|
81
|
+
test_files: []
|