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 :parent
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
on 2006-12-28 04:42
on 2006-12-28 05:06
Holy cow! Tell me, what do you want us to do with this, exactly? What are you challenging us to? Didn't get it. =/
on 2006-12-28 06:41
Ara wrote: > since the threads split all over the place i figured i'd start yet another. > can people break this? Interesting. I'll take a closer look at this tomorrow. In some ways it is similar to #instance_intercept which I've been working on: # = Instance Interception # # This code is in the spirit of class_extension, but performs instance # level method interception instead of class level method inheritance. class Module def instance_interception(&block) @instance_interception ||= Module.new do def self.append_features(mod) append_features_without_instance_interception( mod ) end end @instance_interception.module_eval(&block) if block_given? @instance_interception end private :instance_interception alias_method :append_features_without_instance_interception, :append_features # Append features def append_features( mod ) aspect = instance_interception aspect.__send__( :append_features_without_instance_interception, mod ) aspect.instance_methods.each do |meth| if mod.method_defined?( meth ) aspect.advise( mod, meth ) end end append_features_without_instance_interception( mod ) #if mod.instance_of? Module aspect.__send__( :append_features_without_instance_interception, mod.__send__(:instance_interception) ) #end end # Apply the around advice. def advise( mod, meth ) advice = instance_method( meth ) instance_target = mod.instance_method(meth) mod.__send__( :define_method, meth ) { |*args| #, &blk| target = instance_target.bind( self ) (class << target; self; end).class_eval { define_method( :super ){ call( *args ) } } advice.bind( self ).call( target, *args ) #, &blk ) } end # TODO make method_added hook more robust so not aas to clobber others. # If a method is added to the module/class that is advised. def method_added( meth ) return if @method_added_short if instance_interception.method_defined?( meth ) include instance_interception @method_added_short = true instance_interception.advise( self, meth ) @method_added_short = false end end end =begin test require 'test/unit' class TestModule < Test::Unit::TestCase module A def f ; "F" ; end def g ; "G" ; end instance_interception do def f( target, *args, &blk ) '{' + target.super + '}' end def g( target, *args, &blk ) '{' + target.super + '}' end end end class X def f ; super ; end include A def g ; super ; end end def test_1_01 x = X.new assert_equal( "{F}", x.f ) assert_equal( "{G}", x.g ) end end =end
on 2006-12-28 06:43
On Thu, 28 Dec 2006, [UTF-8] Paulo Köch wrote: > Holy cow! > > Tell me, what do you want us to do with this, exactly? What are you > challenging us to? Didn't get it. =/ > it is a candidate release which addresses both these threads: http://groups-beta.google.com/group/comp.lang.ruby/browse_frm/thread/a80cd271e6896aab/8f728cbad498d432?lnk=raot#8f728cbad498d432 http://groups-beta.google.com/group/comp.lang.ruby/browse_frm/thread/3f94b778d65045be/a25b7705ea5da80f#a25b7705ea5da80f afaikt it it the most flexible and complete solution. unless someone points out a flaw i'll gem it up tomorrow. regards. -a
on 2006-12-28 08:57
ara.t.howard@noaa.gov schrieb: > (... code to override and restore methods ...) Ara, though I find this very interesting I've not much time to figure it out myself, so could you please show us an example with #overriding and #restoring ? > can people break this? What do you mean by "break"? Regards, Pit
on 2006-12-28 15:01
ara.t.howard@noaa.gov wrote: > since the threads split all over the place i figured i'd start yet another. > can people break this? well... yes! i ran it, and got: "a" a.rb:63:in `override': private method `remove_method' called for C:Class (NoMethodError) from a.rb:61:in `each' from a.rb:61:in `override' from a.rb:102 then, once I fixed that: $ cat b.rb require 'a' class C c = new def M; 'a' end def m; 'A' end p c.M(), c.m override { def M; super + 'b' end; def m; super + 'B' end } p c.M(), c.m restore 'M' p c.M(), c.m restore 'm' p c.M(), c.m end $ ruby b.rb "a" "A" ./a.rb:32: warning: already initialized constant M "Ab" "AB" "A" "AB" "A" "A" This code's a lot longer than the last one you sent, so it's gonna take me more time to figure it out. Devin
on 2006-12-28 17:42
On Thu, 28 Dec 2006, Pit Capitain wrote: > ara.t.howard@noaa.gov schrieb: >> (... code to override and restore methods ...) > > Ara, though I find this very interesting I've not much time to figure it out > myself, so could you please show us an example with #overriding and > #restoring ? harp:~ > cat a.rb require 'override' class C c = new def m() 'a' end def n() 'x' end p c.m #=> a p c.n #=> x overriding{ def m() super << 'b' end def n() super << 'y' end } p c.m #=> ab p c.n #=> xy restoring{ m and n } p c.m #=> a p c.n #=> x end harp:~ > ruby a.rb "a" "x" "ab" "xy" "a" "x" i'll probably merge restore/restoring so the above will become restore{ m and n } >> can people break this? > > What do you mean by "break"? just finding bugs. regards. ps. latest version inlined below -a -- if you find yourself slandering anybody, first imagine that your mouth is filled with excrement. it will break you of the habit quickly enough. - the dalai lama file: override.rb class Module def singleton_class &b @singleton_class ||= (class << self; self; end) @singleton_class.module_eval &b if b @singleton_class end def overload :pattern => :method singleton_class{ module_eval <<-co } end end class C def self.new0 i p [i] end def self.new1 i, s p [i,s] end def self.new2 i, s, f p [i,s,f] end end
on 2006-12-28 18:00
Hi -- On Fri, 29 Dec 2006, ara.t.howard@noaa.gov wrote: Whoops, it's one of those messages that won't quote back the text. Oh well :-) All I wanted to ask was: do you think 'override' is the best name for this? The reason I ask is that "require 'override'" sounds like you're importing override ability into Ruby, without any indication of the enhancements that the package provides (i.e., the restoring ability). David
on 2006-12-28 18:02
On Thu, 28 Dec 2006, Devin Mullins wrote: > ara.t.howard@noaa.gov wrote: >> since the threads split all over the place i figured i'd start yet another. >> can people break this? > well... yes! i ran it, and got: > "a" > a.rb:63:in `override': private method `remove_method' called for C:Class > (NoMethodError) > from a.rb:61:in `each' > from a.rb:61:in `override' > from a.rb:102 how odd. which ruby version? > p c.M(), c.m > > $ ruby b.rb > "a" > "A" > ./a.rb:32: warning: already initialized constant M hmmm. i reworked to use a single constant table of methods. latest below and attached. -a -- if you find yourself slandering anybody, first imagine that your mouth is filled with excrement. it will break you of the habit quickly enough. - the dalai lama class Module module OverRiding class << self def new Module.new{ @added_methods = [] @parent = nil class << self attr :added_methods attr :parent def method_added m super if defined? super ensure added_methods << m.to_s end def delete_method m remove_method m added_methods.delete_at(added_methods.rindex(m.to_s)) end def add_parent m include(@parent = m) end def cache mname, m mname = mname.to_s const_set :METHODS____, {} unless const_defined? :METHODS____ const_get(:METHODS____)[ mname ] = m module_eval <<-code def #{ mname } *a, &b METHODS____[ '#{ mname }' ].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) remove_method mname end end m.add_parent @overriding include m @overriding = m end alias_method 'override', 'overriding' def restoring *_a, &_b if _b # barewords will restore methods 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; self } } o.instance_eval &_b end _a.flatten.compact.map do |mname| if defined? @overriding mname = mname.to_s @overriding.walking_up do |m| break( m.delete_method mname ) if m.added_methods.include?(mname) end end instance_method mname end end alias_method 'restore', 'restoring' 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
on 2006-12-28 18:06
On Fri, 29 Dec 2006 dblack@wobblini.net wrote: > Hi -- > > On Fri, 29 Dec 2006, ara.t.howard@noaa.gov wrote: > > Whoops, it's one of those messages that won't quote back the text. Oh > well :-) All I wanted to ask was: do you think 'override' is the best > name for this? The reason I ask is that "require 'override'" sounds > like you're importing override ability into Ruby, without any > indication of the enhancements that the package provides (i.e., the > restoring ability). i'm not attached to the name at all. suggestions? whatever you come up with should imply: "the following block allows one to redefine methods in a functional way: which is to say 'super' will be scoped in any method re-definition so as to refer to the 'current' method of the same name" the method can be used multiple time in a stack-like way with 'restore' affecting the inverse operation. i want to avoid any 'stack-like' names since the inability to un-include a module in ruby and the fact that this impl is module inclusion based would be misleading. the impl sets up a sort of linked list of included/supered modules to provide the call up stack for nested supers. regards. -a
on 2006-12-28 18:16
ara.t.howard@noaa.gov wrote: > since the threads split all over the place i figured i'd start yet another. > can people break this? > > [snip code] Aren't you leaving residual emtpy modules behind when you ermove methods? T.
on 2006-12-28 18:39
On Fri, 29 Dec 2006, Trans wrote: > > ara.t.howard@noaa.gov wrote: >> since the threads split all over the place i figured i'd start yet another. >> can people break this? >> >> [snip code] > > Aren't you leaving residual emtpy modules behind when you ermove > methods? not really. basically the first call to override sets up a master parent module with the orginal methods object -> [master] and re-definition takes place in a module between the object and this master, thus super works object -> [redef] -> [master] subsquent redefs chain like so, preserving super semantics object -> [redef 2] -> [redef 1] -> [master] note that the modules are created not once per method, but once per call to 'override'. therefore this class C def a() 'a' end def b() 'b' end def c() 'c' end override{ def a() super.upcase end def b() super.upcase end def c() super.upcase end } end creates exactly two modules: the master and one redef. now if we do this class C override{ def a() super.downcase end def b() super.downcase end def c() super.downcase end } end we've created just one more. something like object -> [redef 'downcase'] -> [redef 'upcase'] -> [master] now, say we do this class C restore{ a and b } end then __only__ the methods 'a' and 'b' from [redef 'downcase'] are removed. the 'c' method still lives there. so, while it is a true statement that __each__ call to 'override' introduces a new mixin module, it is not true that __every__ call to 'restore' leaves an extraneous module mixed in, though it's possible that a given call to 'restore' may indeed have that effect. make sense? -a
on 2006-12-29 03:50
ara.t.howard@noaa.gov wrote: > On Thu, 28 Dec 2006, Devin Mullins wrote: >> a.rb:63:in `override': private method `remove_method' called for > how odd. which ruby version? ruby 1.8.5 (2006-08-25) [i386-mswin32] (Yes, the "$" prompt in my last email was a lie. Mischevious!) > hmmm. i reworked to use a single constant table of methods. Will take a look. Looks to be cool-in-progress. Devin
on 2006-12-29 15:14
ara.t.howard@noaa.gov wrote: > > so, while it is a true statement that __each__ call to 'override' introduces a > new mixin module, it is not true that __every__ call to 'restore' leaves an > extraneous module mixed in, though it's possible that a given call to > 'restore' may indeed have that effect. > > make sense? sure, it makes sense, but you do have that possibility. and i agree it's not such a big deal. just pointing it out. i also point out that the idea of override.rb is but a step from the idea of cuts. in fact implementation of overide using cuts is little more than: def override &block Cut.new(self,&block) end and my hacked-up pure ruby implementation of cuts is very similar in design that of override.rb. moving slightly off-topic. have you ever thought about the possibility of every method being as if it were it's own module? class X module A def a; "a"; end end include A module B def b; "b"; end end include B end the ablity to dynamically manipulate behavior goes way up. class X module A2 def a; '{'+super+'}'; end end include A2 end of course so does the overhead. (and the dynamic inclusion problem still lurks in the background). t.
on 2006-12-29 16:58
On Fri, 29 Dec 2006, Trans wrote: > design that of override.rb. i confess cuts are a bit heavyweight for my tastes. however - i aggree that the effect and even the impl to some respects is very similar. > module B > def b; "b"; end > end > include B > > end yes. about 1 hour ago! ;-) > lurks in the background). indeed. no you've done it - my new project is in the works! more later.... -a
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.