Injecting methods from one class into another.

G

George Moschovitis

Note that EvilRuby has a less strict version of UnboundMethod#bind
wtf is EvilRuby ?!?! :)

-g.
 
G

George Moschovitis

Florian,

THANK you for this intelligent reply, this is what I was looking for.
Perhaps another poster was correct, 'you can do everything in Ruby' :)
best regards,
George.
 
R

Robert Klemme

George Moschovitis said:
Robert,

thank you for taking the time to answer my post.

However, I dont think that this is a usefull example. One could use a
simple
obj.class.class_eval("..") to achieve the same.

Dont you feel that allowing bind to attach methods only to objects that
already have this method (ie, obj.kind_of(class) == true) is strange?

Probably. But you can have several versions of a method with this:

class Foo
def bar;"old" end
end

f = Foo.new
puts f.bar -> "old"
m = f.method:)bar).unbind
puts m.bind(f).call -> "old"

class Foo
def bar;"new" end
end

puts f.bar -> "new"
puts m.bind(f).call -> "old"


Kind regards

robert
 
R

Rob Lally

George said:
Of course it has. Have a look at the following example:

class C1
attr_accessor :a

def a1
puts @a
end

def a2
puts 'hello'
end
end

class C2
attr_accessor :a
end

So for method C1#a1 the class C2 'quacks like a duck' (ie, it has the
attribute @a)

Your object doesn't quack like a duck. If it did it would have a method 'a1'. Your object has some of the same internal
organs as a duck, which is both a gruesome image and not the same idea as duck typing. Duck typing is about interface
not implementation. The implementation should be irrelevant ... only the interface matters.

R.
 
C

Csaba Henk

class Object
def cast(target_class)
copy = case target_class
when Class; target_class.allocate
when Module; dup.extend(target_class)
else target_class.class.allocate
end

instance_variables.each do |var|
copy.instance_variable_set(var, instance_variable_get(var))
end

copy
end
end

Here's a bit more thorough implementation which lets you do:

o = Object.new
o.cuckoo_bind Array, :size
o.size
=> 0

It also assumes you know what you are doing, ie. it relies on the
methods of the object which adopts the method.

I had to use metaclasses much more intensively than I wanted to (which
was "not at all"), for reasons not totally understood by me. (Just try
to strip them off, you'll see.) It can be modified to bind several
methods of the same class, but I won't do that now. Tested w/ 1.8.2.

####################
class Object
def metaclass
class << self
self
end
end
end

class Object
def all_methods
a=[]
%w(public protected private).each {|x| a += send(x+"_methods") }
a
end
def cuckoo_bind klass, methname
methname = methname.to_s
# We could ask for the (Unbound)Method directly as an argument
# But then I can't see a better way to extract klass, methname than
# meth.inspect[/: ([^#]*)#([^>]*)/]
# klass = eval $1; methname = $2
# and that's too hacky. It's a flaw in the API, imho.
klass.instance_methods.include? methname or
raise TypeError, "victim is unuseable"
cuckoo = case klass
when Class; cuckoo = klass.allocate
when Module; cuckoo = Object.new; cuckoo.extend klass
end
# first we teach cuckoo what is easy to teach: our instance
# variables
instance_variables.each{|v|
cuckoo.instance_variable_set v, instance_variable_get(v)
}
class << cuckoo
def self.fetch data
@fetched_data = data
end
end
# cuckoo, we own you!
warning_triggerers = %w(__id__ initialize __send__)
bare_minimum = %w(fetch undef_method metaclass
instance_variable_get) + [methname] + warning_triggerers
(all_methods - bare_minimum).each{ |m|
cuckoo.metaclass.fetch m
class << cuckoo
begin
undef_method @fetched_data
rescue NameError
end
end
}
cuckoo.metaclass.fetch self
# fly the cuckoo
def cuckoo.method_missing *args, &bl
metaclass.instance_variable_get:)@fetched_data).send *args, &bl
end
# we also have to learn making good use of cuckoo
# it's hard to import local names to the metaclass' namespace
class << self
def self.fetch data
@fetched_data = data
end
end
@zoo ||= {}
@zoo[methname] = cuckoo
metaclass.fetch :methname => methname, :cuckoo => cuckoo
class << self
# define_method's block can't have block argument, hence the eval hack
eval "def #{@fetched_data[:methname]}(*args, &bl)
@zoo['#{@fetched_data[:methname]}'].#{@fetched_data[:methname]}(*args,&bl)
end"
end
method methname
end
end
####################
 
G

Gavin Kistner

I do not think what you want has anything to do with 'duck typing'.
'duck typing' only means that you can send a message to any object and
if the object has a corresponding method it gets executed regardless
of object type. It does not urge you to abandon the "normal" way of
method definition for a class.

FWIW, in Javascript you can invoke a method defined anywhere with any
scope that you wish. For example:

Object.prototype.toString = function(){
return '['+this.constructor.name+' "'+this.name+'"]';
}

function Bird( inName ){
this.name = inName;
this.wings = 2;
}
Bird.prototype.fly = function(){
if ( this.wings )
{
this.altitude += 100;
alert( 'Look ma, ' + this + ' is flying!' );
}
}

function Pig( inName ){
this.name = inName;
}

var robin = new Bird( 'Tweeters' );
robin.fly(); // Look ma, [Bird "Tweeters"] is flying!

var super_pig = new Pig( 'Babe' );
super_pig.wings = 17;
robin.fly.call( super_pig ); // Look ma, [Pig "Babe"] is flying!
Bird.prototype.fly.call( super_pig ); // Look ma, [Pig "Babe"] is
flying!


Perhaps it's the difference between a strongly-typed dynamic language,
and a loosely-typed dynamic language. Perhaps it has no place in Ruby.
But on occasion, I've enjoyed this flexibility in Javascript.

What makes Ruby not amenable to running arbitrary (but predefined) code
against an arbitrary scope, and deciding only during the execution of
each statement if the instance variables exist or not?
 
Z

Zach Dennis

Gavin said:
I do not think what you want has anything to do with 'duck typing'.
'duck typing' only means that you can send a message to any object and
if the object has a corresponding method it gets executed regardless
of object type. It does not urge you to abandon the "normal" way of
method definition for a class.


FWIW, in Javascript you can invoke a method defined anywhere with any
scope that you wish. For example:

Object.prototype.toString = function(){
return '['+this.constructor.name+' "'+this.name+'"]';
}

function Bird( inName ){
this.name = inName;
this.wings = 2;
}
Bird.prototype.fly = function(){
if ( this.wings )
{
this.altitude += 100;
alert( 'Look ma, ' + this + ' is flying!' );
}
}

function Pig( inName ){
this.name = inName;
}

var robin = new Bird( 'Tweeters' );
robin.fly(); // Look ma, [Bird "Tweeters"] is flying!

var super_pig = new Pig( 'Babe' );
super_pig.wings = 17;
robin.fly.call( super_pig ); // Look ma, [Pig "Babe"] is flying!
Bird.prototype.fly.call( super_pig ); // Look ma, [Pig "Babe"] is flying!



def method_missing( arg1, arg2=nil )
if arg1.to_s =~ /([^=]*)=/
instance_eval "@#{$1}= #{arg2};def #{$1}; @#{$1}; end"
else
super
end
end

class Bird
attr_accessor :wings
def initialize( name )
@name = name
@wings = 2
end

def self.fly( arg=nil )
obj = arg
if obj.wings
obj.instance_eval "@altitude ||=0"
obj.instance_eval "@altitude += 100"
puts "Look ma, #{obj.instance_eval '@name' } is flying!"
end
end

def fly( arg=self )
Bird.fly( arg )
end
end

class Pig
def initialize( name )
@name = name
end
end

robin = Bird.new( "Tweeters" )
robin.fly

super_pig = Pig.new( 'Babe' )
super_pig.wings = 17
robin.fly( super_pig )
Bird.fly( super_pig )


You can still achieve the same outcome, just not using the ECMA language
syntax. JavaScript isn't the only prototype-based langauge out there.
Flash ActionScript is much like this as well (and I believe nicer then JS).

Not having to mess with things like:
Object.prototype.method.call
Object.__prototype__.method.call
is a nice way to use class or module methods IMO, it's just much cleaner
looking:
Bird.prototype.fly.call( super_pig )
Bird.fly( super_pig )

The nice thing about Ruby is that you DONT have to succumb to the
behavior where new attributes will be created on the fly. If you want
that functionality override method missing. Perhaps not every single
Object out there needs this ability, but if you feel you need it
somewhere then it's simple, easy and quick to add.

Zach
 

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,995
Messages
2,570,228
Members
46,818
Latest member
SapanaCarpetStudio

Latest Threads

Top