A
ara.t.howard
since the threads split all over the place i figured i'd start yet another.
can people break this?
harp:~ > cat a.rb
class Module
module OverRiding
class << self
def new
Module.new{
@added_methods = []
@parent = nil
class << self
attr :added_methods
attr arent
def method_added m
super if defined? super
ensure
added_methods << m.to_s
end
def remove_method m
super if defined? super
ensure
added_methods.delete_at(added_methods.rindex(m.to_s)) rescue nil
end
def add_parent m
include(@parent = m)
end
def cache mname, m
mname = mname.to_s
const = mname.gsub(%r/[!]/,'__bang__').gsub(%r/[?]/,'__what__').upcase
const_set const, m
module_eval <<-code
def #{ mname } *a, &b
#{ const }.bind(self).call *a, &b
end
code
end
def walking_up
m = self
loop{
yield m
break unless((m=m.parent) rescue nil)
}
end
end
}
end
end
end
def overriding &b
this = self
m = OverRiding.new
m.module_eval &b
unless defined? @overriding
@overriding = OverRiding.new
m.added_methods.each do |mname|
@overriding.cache mname, this.instance_method(mname)
this.remove_method mname
end
end
m.add_parent @overriding
include m
@overriding = m
end
alias_method 'override', 'overriding'
def restore mname
if defined? @overriding
mname = mname.to_s
@overriding.walking_up do |m|
break( m.remove_method mname ) if m.added_methods.include?(mname)
end
end
instance_method mname
end
def restoring *a, &b
o = Object.new
sc = class << o; self; end
sc.module_eval{
instance_methods.each{|m| remove_method m unless m[/^__/] rescue nil}
define_method('method_missing'){|m, *a| a << m }
}
o.instance_eval &b
a.flatten.compact.each{|m| restore m}
end
end
if $0 == __FILE__
class C
c = new
def m() 'a' end
p c.m #=> 'a'
override{ def m() super + 'b' end }
p c.m #=> 'ab'
override{ def m() super + 'c' end }
p c.m #=> 'abc'
override{ def m() super + 'd' end }
p c.m #=> 'abcd'
restore 'm'
p c.m #=> 'abc'
restore 'm'
p c.m #=> 'ab'
restore 'm'
p c.m #=> 'a'
end
end
harp:~ > ruby a.rb
"a"
"ab"
"abc"
"abcd"
"abc"
"ab"
"a"
-a
can people break this?
harp:~ > cat a.rb
class Module
module OverRiding
class << self
def new
Module.new{
@added_methods = []
@parent = nil
class << self
attr :added_methods
attr arent
def method_added m
super if defined? super
ensure
added_methods << m.to_s
end
def remove_method m
super if defined? super
ensure
added_methods.delete_at(added_methods.rindex(m.to_s)) rescue nil
end
def add_parent m
include(@parent = m)
end
def cache mname, m
mname = mname.to_s
const = mname.gsub(%r/[!]/,'__bang__').gsub(%r/[?]/,'__what__').upcase
const_set const, m
module_eval <<-code
def #{ mname } *a, &b
#{ const }.bind(self).call *a, &b
end
code
end
def walking_up
m = self
loop{
yield m
break unless((m=m.parent) rescue nil)
}
end
end
}
end
end
end
def overriding &b
this = self
m = OverRiding.new
m.module_eval &b
unless defined? @overriding
@overriding = OverRiding.new
m.added_methods.each do |mname|
@overriding.cache mname, this.instance_method(mname)
this.remove_method mname
end
end
m.add_parent @overriding
include m
@overriding = m
end
alias_method 'override', 'overriding'
def restore mname
if defined? @overriding
mname = mname.to_s
@overriding.walking_up do |m|
break( m.remove_method mname ) if m.added_methods.include?(mname)
end
end
instance_method mname
end
def restoring *a, &b
o = Object.new
sc = class << o; self; end
sc.module_eval{
instance_methods.each{|m| remove_method m unless m[/^__/] rescue nil}
define_method('method_missing'){|m, *a| a << m }
}
o.instance_eval &b
a.flatten.compact.each{|m| restore m}
end
end
if $0 == __FILE__
class C
c = new
def m() 'a' end
p c.m #=> 'a'
override{ def m() super + 'b' end }
p c.m #=> 'ab'
override{ def m() super + 'c' end }
p c.m #=> 'abc'
override{ def m() super + 'd' end }
p c.m #=> 'abcd'
restore 'm'
p c.m #=> 'abc'
restore 'm'
p c.m #=> 'ab'
restore 'm'
p c.m #=> 'a'
end
end
harp:~ > ruby a.rb
"a"
"ab"
"abc"
"abcd"
"abc"
"ab"
"a"
-a