-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwrap.rb
More file actions
111 lines (95 loc) · 2.32 KB
/
wrap.rb
File metadata and controls
111 lines (95 loc) · 2.32 KB
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#07/08/10
# Make an object display another object's public interface - "favor composition
# over inheritance"
class Class
# First attempt, doesn't work if wrapped objs change
def wrap1(*classes)
classes.each do |clsname|
cls = eval clsname.to_s
obj = cls.new
@wrapped_objs ||= {}
@wrapped_objs[clsname] = obj
to_add = obj.methods.keep_if do |metname|
m = obj.method metname
m.owner == obj.class
end
to_add.each do |met|
# This is the problem, if a wrapped obj is removed, how to
# undefine this method? I kind of don't like 'undef'
define_method met do |*params|
obj.send met, *params
end
end
define_method :initialize do
self.class.wrapped_objs.each do |clsname, obj|
instance_variable_set "@obj_#{clsname.downcase}", obj
end
end
end
end
attr_reader :wrapped_classes
def wrap(*classes)
classes.each do |clsname|
# is it safe to use eval here, in this way?
cls = eval clsname.to_s
obj = cls.new
attr_reader :wrapped_objs
@wrapped_classes ||= []
@wrapped_classes.push clsname
define_method :method_missing do |metname, *args|
return_value = nil
@wrapped_objs.each do |obj|
begin
return_value = obj.send metname, *args
rescue NoMethodError => e
next
else
break
end
end
if return_value
return_value
else
raise NoMethodError
end
end
#FIXME for classes that define their own :initialize
define_method :initialize do
@wrapped_objs = self.class.wrapped_classes.collect do |clsname|
#same here, safe to use eval?
cls = eval clsname.to_s
cls.new
end
end
end
end
private :wrap
end
if __FILE__ == $0
# testing
class A
def meta(a, b)
"In meta: #{a} #{b}"
end
end
class B
wrap :A
end
class D
def metd
"In metd"
end
end
class C
wrap :B
wrap :D
end
b = B.new
p "b.meta 1,2 : #{b.meta 1, 2}"
p "b.kind_of? A : #{b.kind_of? A}"
p "b.wrapped_objs : #{b.wrapped_objs}"
c = C.new
p "c.meta 3,4 : #{c.meta 3,4}"
p "c.metd : #{c.metd}"
p "c.wrapped_objs : #{c.wrapped_objs}"
end