override.rb

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 :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
 
P

Paulo Köch

Holy cow!

Tell me, what do you want us to do with this, exactly? What are you
challenging us to? Didn't get it. =/
 
T

Trans

Ara said:
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
 
A

ara.t.howard

Holy cow!

Tell me, what do you want us to do with this, exactly? What are you
challenging us to? Didn't get it. =3D/

it is a candidate release which addresses both these threads:

http://groups-beta.google.com/group/comp.lang.ruby/browse_frm/thread/a80=
cd271e6896aab/8f728cbad498d432?lnk=3Draot#8f728cbad498d432

http://groups-beta.google.com/group/comp.lang.ruby/browse_frm/thread/3f9=
4b778d65045be/a25b7705ea5da80f#a25b7705ea5da80f


afaikt it it the most flexible and complete solution. unless someone point=
s
out a flaw i'll gem it up tomorrow.

regards.

-a
--=20
if you find yourself slandering anybody, first imagine that your mouth is
filled with excrement. it will break you of the habit quickly enough. - th=
e
dalai lama
 
P

Pit Capitain

(... 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
 
D

Devin Mullins

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
 
D

dblack

Hi --

On Fri, 29 Dec 2006, (e-mail address removed) 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

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
A

ara.t.howard

---1400102478-551175305-1167325293=:5000
Content-Type: MULTIPART/MIXED; BOUNDARY="-1400102478-551175305-1167325293=:5000"

This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.

---1400102478-551175305-1167325293=:5000
Content-Type: TEXT/PLAIN; charset=US-ASCII; format=flowed

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?
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

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
---1400102478-551175305-1167325293=:5000
Content-Type: TEXT/PLAIN; charset=US-ASCII; name=override.rb
Content-Transfer-Encoding: BASE64
Content-ID: <[email protected]>
Content-Description:
Content-Disposition: attachment; filename=override.rb

Y2xhc3MgTW9kdWxlDQogIG1vZHVsZSBPdmVyUmlkaW5nDQogICAgY2xhc3Mg
PDwgc2VsZg0KICAgICAgZGVmIG5ldw0KICAgICAgICBNb2R1bGUubmV3eyAN
CiAgICAgICAgICBAYWRkZWRfbWV0aG9kcyA9IFtdIA0KICAgICAgICAgIEBw
YXJlbnQgPSBuaWwNCg0KICAgICAgICAgIGNsYXNzIDw8IHNlbGYNCiAgICAg
ICAgICAgIGF0dHIgOmFkZGVkX21ldGhvZHMNCiAgICAgICAgICAgIGF0dHIg
OnBhcmVudA0KDQogICAgICAgICAgICBkZWYgbWV0aG9kX2FkZGVkIG0NCiAg
ICAgICAgICAgICAgc3VwZXIgaWYgZGVmaW5lZD8gc3VwZXINCiAgICAgICAg
ICAgIGVuc3VyZQ0KICAgICAgICAgICAgICBhZGRlZF9tZXRob2RzIDw8IG0u
dG9fcw0KICAgICAgICAgICAgZW5kDQoNCiAgICAgICAgICAgIGRlZiBkZWxl
dGVfbWV0aG9kIG0NCiAgICAgICAgICAgICAgcmVtb3ZlX21ldGhvZCBtDQog
ICAgICAgICAgICAgIGFkZGVkX21ldGhvZHMuZGVsZXRlX2F0KGFkZGVkX21l
dGhvZHMucmluZGV4KG0udG9fcykpDQogICAgICAgICAgICBlbmQNCg0KICAg
ICAgICAgICAgZGVmIGFkZF9wYXJlbnQgbQ0KICAgICAgICAgICAgICBpbmNs
dWRlKEBwYXJlbnQgPSBtKQ0KICAgICAgICAgICAgZW5kDQoNCiAgICAgICAg
ICAgIGRlZiBjYWNoZSBtbmFtZSwgbQ0KICAgICAgICAgICAgICBtbmFtZSA9
IG1uYW1lLnRvX3MNCiAgICAgICAgICAgICAgY29uc3Rfc2V0IDpNRVRIT0RT
X19fXywge30gdW5sZXNzIGNvbnN0X2RlZmluZWQ/IDpNRVRIT0RTX19fXw0K
ICAgICAgICAgICAgICBjb25zdF9nZXQoOk1FVEhPRFNfX19fKVsgbW5hbWUg
XSA9IG0NCiAgICAgICAgICAgICAgbW9kdWxlX2V2YWwgPDwtY29kZQ0KICAg
ICAgICAgICAgICAgIGRlZiAjeyBtbmFtZSB9ICphLCAmYg0KICAgICAgICAg
ICAgICAgICAgTUVUSE9EU19fX19bICcjeyBtbmFtZSB9JyBdLmJpbmQoc2Vs
ZikuY2FsbCAqYSwgJmINCiAgICAgICAgICAgICAgICBlbmQNCiAgICAgICAg
ICAgICAgY29kZQ0KICAgICAgICAgICAgZW5kDQoNCiAgICAgICAgICAgIGRl
ZiB3YWxraW5nX3VwDQogICAgICAgICAgICAgIG0gPSBzZWxmDQogICAgICAg
ICAgICAgIGxvb3B7DQogICAgICAgICAgICAgICAgeWllbGQgbQ0KICAgICAg
ICAgICAgICAgIGJyZWFrIHVubGVzcygobT1tLnBhcmVudCkgcmVzY3VlIG5p
bCkNCiAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZW5kDQogICAgICAg
ICAgZW5kDQogICAgICAgIH0NCiAgICAgIGVuZA0KICAgIGVuZA0KICBlbmQN
Cg0KICBkZWYgb3ZlcnJpZGluZyAmYg0KICAgIHRoaXMgPSBzZWxmDQogICAg
bSA9IE92ZXJSaWRpbmcubmV3DQogICAgbS5tb2R1bGVfZXZhbCAmYg0KDQog
ICAgdW5sZXNzIGRlZmluZWQ/IEBvdmVycmlkaW5nIA0KICAgICAgQG92ZXJy
aWRpbmcgPSBPdmVyUmlkaW5nLm5ldw0KICAgICAgbS5hZGRlZF9tZXRob2Rz
LmVhY2ggZG8gfG1uYW1lfA0KICAgICAgICBAb3ZlcnJpZGluZy5jYWNoZSBt
bmFtZSwgdGhpcy5pbnN0YW5jZV9tZXRob2QobW5hbWUpDQogICAgICAgIHJl
bW92ZV9tZXRob2QgbW5hbWUNCiAgICAgIGVuZA0KICAgIGVuZA0KDQogICAg
bS5hZGRfcGFyZW50IEBvdmVycmlkaW5nDQogICAgaW5jbHVkZSBtIA0KICAg
IEBvdmVycmlkaW5nID0gbQ0KICBlbmQNCiAgYWxpYXNfbWV0aG9kICdvdmVy
cmlkZScsICdvdmVycmlkaW5nJw0KDQogIGRlZiByZXN0b3JpbmcgKl9hLCAm
X2INCiAgICBpZiBfYiAjIGJhcmV3b3JkcyB3aWxsIHJlc3RvcmUgbWV0aG9k
cw0KICAgICAgbyA9IE9iamVjdC5uZXcNCiAgICAgIHNjID0gY2xhc3MgPDwg
bzsgc2VsZjsgZW5kDQogICAgICBzYy5tb2R1bGVfZXZhbHsNCiAgICAgICAg
aW5zdGFuY2VfbWV0aG9kcy5lYWNoe3xtfCByZW1vdmVfbWV0aG9kIG0gdW5s
ZXNzIG1bL15fXy9dIHJlc2N1ZSBuaWx9DQogICAgICAgIGRlZmluZV9tZXRo
b2QoJ21ldGhvZF9taXNzaW5nJyl7fF9fbSwgKl9fYXwgX2EgPDwgX19tOyBz
ZWxmIH0NCiAgICAgIH0NCiAgICAgIG8uaW5zdGFuY2VfZXZhbCAmX2INCiAg
ICBlbmQNCiAgICBfYS5mbGF0dGVuLmNvbXBhY3QubWFwIGRvIHxtbmFtZXwN
CiAgICAgIGlmIGRlZmluZWQ/IEBvdmVycmlkaW5nDQogICAgICAgIG1uYW1l
ID0gbW5hbWUudG9fcw0KICAgICAgICBAb3ZlcnJpZGluZy53YWxraW5nX3Vw
IGRvIHxtfA0KICAgICAgICAgIGJyZWFrKCBtLmRlbGV0ZV9tZXRob2QgbW5h
bWUgKSBpZiBtLmFkZGVkX21ldGhvZHMuaW5jbHVkZT8obW5hbWUpDQogICAg
ICAgIGVuZA0KICAgICAgZW5kDQogICAgICBpbnN0YW5jZV9tZXRob2QgbW5h
bWUNCiAgICBlbmQNCiAgZW5kDQogIGFsaWFzX21ldGhvZCAncmVzdG9yZScs
ICdyZXN0b3JpbmcnDQplbmQNCg0KDQoNCg0KaWYgJDAgPT0gX19GSUxFX18N
Cg0KICBjbGFzcyBDDQogICAgYyA9IG5ldw0KDQogICAgZGVmIG0oKSAnYScg
ZW5kDQogICAgcCBjLm0gIz0+ICdhJw0KDQogICAgb3ZlcnJpZGV7IGRlZiBt
KCkgc3VwZXIgKyAnYicgZW5kIH0NCiAgICBwIGMubSAjPT4gJ2FiJw0KDQog
ICAgb3ZlcnJpZGV7IGRlZiBtKCkgc3VwZXIgKyAnYycgZW5kIH0NCiAgICBw
IGMubSAjPT4gJ2FiYycNCg0KICAgIG92ZXJyaWRleyBkZWYgbSgpIHN1cGVy
ICsgJ2QnIGVuZCB9DQogICAgcCBjLm0gIz0+ICdhYmNkJw0KDQogICAgcmVz
dG9yZSAnbScNCiAgICBwIGMubSAjPT4gJ2FiYycNCg0KICAgIHJlc3RvcmUg
J20nDQogICAgcCBjLm0gIz0+ICdhYicNCg0KICAgIHJlc3RvcmUgJ20nDQog
ICAgcCBjLm0gIz0+ICdhJw0KICBlbmQNCg0KZW5kDQo=

---1400102478-551175305-1167325293=:5000--
---1400102478-551175305-1167325293=:5000--
 
A

ara.t.howard

Hi --

On Fri, 29 Dec 2006, (e-mail address removed) 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
 
T

Trans

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.
 
A

ara.t.howard

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
 
D

Devin Mullins

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
 
T

Trans

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.
 
A

ara.t.howard

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.

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.
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

yes. about 1 hour ago! ;-)
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).

indeed.

no you've done it - my new project is in the works!

more later....

-a
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top