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
|
# File 'lib/called_uncalled.rb', line 7
def self.included(base)
base.define_singleton_method(:called_methods) do |*sources|
sources.push(base) if sources.empty?
sources = sources.flatten.map { |source| source.is_a?(base) ? [source.class, source.singleton_class] : source }.flatten
base.class_variable_get(:@@called_methods).select { |m| sources.include?(:all) || sources.include?(m.owner) }.map(&:name)
end
base.define_singleton_method(:uncalled_methods) do |*sources|
sources.push(base) if sources.empty?
sources = sources.flatten.map { |source| source.is_a?(base) ? [source.class, source.singleton_class] : source }.flatten
base.class_variable_get(:@@uncalled_methods).select { |m| sources.include?(:all) || sources.include?(m.owner) }.map(&:name)
end
observe = ->(base, klass, mthd) do
method_name = mthd.name
klass.class_variable_get(:@@uncalled_methods) << mthd
base.define_method(method_name) do |*args, **kwargs, &block|
newly_called = klass.class_variable_get(:@@uncalled_methods).find { |x| x.name == method_name }
if newly_called
klass.class_variable_get(:@@uncalled_methods).delete(newly_called)
klass.class_variable_get(:@@called_methods).push(newly_called)
end
super(*args, **kwargs, &block)
end
end
singleton_method_wrapper = Module.new
method_wrapper = Module.new do
base.class_variable_set(:@@called_methods, [])
base.class_variable_set(:@@uncalled_methods, [])
method_names = base.instance_methods
method_names.each { |method_name|
mthd = base.instance_method(method_name)
observe.call(self, base, mthd)
}
define_method(:singleton_method_added) do |method_name|
mthd = singleton_method(method_name)
singleton_method_wrapper.module_eval { observe.call(self, base, mthd) }
singleton_class.prepend(singleton_method_wrapper) unless singleton_class < singleton_method_wrapper
newly_called = base.class_variable_get(:@@uncalled_methods).find { |x| x.name == :singleton_method_added }
if newly_called
base.class_variable_get(:@@uncalled_methods).delete(newly_called)
base.class_variable_get(:@@called_methods).push(newly_called)
end
super(method_name)
end
end
method_added_wrapper = Module.new do
define_method(:method_added) do |method_name|
mthd = base.instance_method(method_name)
method_wrapper.module_eval { observe.call(self, base, mthd) }
super(method_name)
end
end
base.singleton_class.prepend(method_added_wrapper)
base.prepend(method_wrapper)
end
|