[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

override.rb

Ara.T.Howard

12/28/2006 3:41:00 AM

13 Answers

Paulo Köch

12/28/2006 4:06:00 AM

0

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 12/28/06, ara.t.howard@noaa.gov <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?
>
>
> 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
> --
> 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
>
>

Trans

12/28/2006 5:41:00 AM

0

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


Ara.T.Howard

12/28/2006 5:42:00 AM

0

Pit Capitain

12/28/2006 7:57:00 AM

0

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

Devin Mullins

12/28/2006 2:01:00 PM

0

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

dblack

12/28/2006 4:59:00 PM

0

Ara.T.Howard

12/28/2006 5:02:00 PM

0

Ara.T.Howard

12/28/2006 5:06:00 PM

0

Trans

12/28/2006 5:15:00 PM

0


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.


Ara.T.Howard

12/28/2006 5:38:00 PM

0