Override.rb

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

Holy cow!

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

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

[email protected] 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 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

[email protected] 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 methodremove_method’ called for C:Class
(NoMethodError)
from a.rb:61:in each' from a.rb:61:inoverride’
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

Hi –

On Fri, 29 Dec 2006, [email protected] wrote:

Whoops, it’s one of those messages that won’t quote back the text. Oh
well :slight_smile: 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 Thu, 28 Dec 2006, Pit C. wrote:

[email protected] 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 Thu, 28 Dec 2006, Devin M. wrote:

[email protected] 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 methodremove_method’ called for C:Class
(NoMethodError)
from a.rb:61:in each' from a.rb:61:inoverride’
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 Fri, 29 Dec 2006 [email protected] wrote:

Hi –

On Fri, 29 Dec 2006, [email protected] wrote:

Whoops, it’s one of those messages that won’t quote back the text. Oh
well :slight_smile: 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

[email protected] 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 Fri, 29 Dec 2006, Trans wrote:

[email protected] 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

[email protected] wrote:

On Thu, 28 Dec 2006, Devin M. wrote:

a.rb:63:in override': private methodremove_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 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! :wink:

lurks in the background).
indeed.

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

more later…

-a

[email protected] 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.