forked from garybernhardt/base
/
base.rb
93 lines (80 loc) · 2.55 KB
/
base.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class Base
def initialize *args, &block
super *args, &block
end
def self.const_missing name
name = name.to_s
all_modules.each do |mod|
mod.constants.each do |constant|
return mod.const_get(constant) if constant == name
end
end
super
end
def self.all_modules
modules = []
ObjectSpace.each_object(Module) do |mod|
modules << mod if should_extract_from?(mod)
end
modules << Kernel
modules
end
def self.should_extract_from?(mod)
return false if (mod < Base || mod == Base || mod.is_a?(Base))
return mod.is_a?(Module) && mod != Kernel
end
def self.method_missing name, *args, &block
call_method(self, name, args, block)
end
def method_missing name, *args, &block
self.class.call_method(self, name, args, block)
end
def self.call_method(object, name, args, block)
name_string = name.to_s
all_modules.each do |mod|
if mod.respond_to?(name)
return mod.send name, *args, &block
elsif mod.instance_methods.include?(name_string)
return call_instance_method(mod, name, args, block)
end
end
# 1. The world is all that is the case.
# 2. We failed to find a method to call.
# 2.1. So we need to call method_missing.
# 2.1.1. We can't just super it because we're not in method_missing.
# 2.1.1.1. We're not in method_missing because there are two of them
# (self and instance) that need to share this code.
# 2.1.1.2. We need to call the method that would be called if we said
# "super" in the object's method_missing.
# 2.1.1.2.1. Which is its class's superclass's method_missing method
# object.
Object.instance_method(:method_missing).bind(object).call(name, *args, &block)
end
def self.call_instance_method(mod, name, args, block)
if mod.is_a? Class
klass = Class.new(mod)
else
klass = Class.new { include mod }
end
return klass.new.send name, *args, &block
end
def self.methods
(giant_method_list_including_object(self) + super).uniq
end
def methods
(self.class.giant_method_list_including_object(self) + super).uniq
end
# INHERIT ALL THE METHODS!
def self.giant_method_list_including_object(object)
methods = []
all_modules.each_with_index do |m, i|
# Don't recurse into other Base objects' "methods" method
if m.is_a?(Base) || m < Base || m == Base
[]
else
methods += m.methods + m.instance_methods
end
end
methods
end
end