Mixin using append_features and class<<self do not work together


(Philipp Meier) #1

Hello to all,

First, I’m new to ruby so please forgive if I read over the solution in
the ruby doc or pickaxe book.
I’d like to know if it’s possible to add a method to an object
instance at runtime? I tried sth. like instance_eval and “class <<
self” which works. But do I add a mixin at runtime?

I have a Mixin like this:

Module B
def B.append_features(aClass)
def aClass.foo
"foo"
end
end
end

o = Object.new
o.instance_eval<<-"end_eval"
include B
end_eval

But o.foo does not exist!?

Is there a way to use append_features together with class<<self ?

-billy.

···


Meisterbohne Söflinger Straße 100 Tel: +49-731-399 499-0
eLösungen 89077 Ulm Fax: +49-731-399 499-9


(Bob Calco) #2

Philip:

Here something conceptually related to what you are trying to do. This is a
teaser from Chapter 6 of the book Rich Kilmer, Dana Moore and I are writing,
namely, the Ruby Developer’s Handbook, for Sams (to be published in early
2003). The chapter covers Ruby’s idiomatic support for meta-programming in
considerable depth.

···

module Adaptable
def add_method( scope, name, args, code )
instance_eval <<-END
class << self
#{scope}
def #{name} ( #{args} )
begin
#{code}
rescue => err
puts err
end
end
end
END
end
def remove_method( name )
instance_eval "undef #{name}"
end
end

o = Object.new

class << o
include Adaptable # add Adaptable to this instance of the Object class
end

o.add_method :public, :fact, :n, <<-END
if n == 0 then
1
else
n * fact( n-1 )
end
END

o.fact( 5 ) #=> 120

o.remove_method :fact

o.fact( 5 ) #=> (NoMethodError)


This should anwer some of your questions, and open you eyes to more than a
few possibilities.

Enjoy!

– Bob Calco

-----Original Message-----
From: Philipp Meier [mailto:meier@meisterbohne.de]
Sent: Friday, June 21, 2002 2:35 PM
To: ruby-talk ML
Subject: Mixin using append_features and class<<self do not work
together

Hello to all,

First, I’m new to ruby so please forgive if I read over the solution in
the ruby doc or pickaxe book.
I’d like to know if it’s possible to add a method to an object
instance at runtime? I tried sth. like instance_eval and “class <<
self” which works. But do I add a mixin at runtime?

I have a Mixin like this:

Module B
def B.append_features(aClass)
def aClass.foo
"foo"
end
end
end

o = Object.new
o.instance_eval<<-"end_eval"
include B
end_eval

But o.foo does not exist!?

Is there a way to use append_features together with class<<self ?

-billy.

Meisterbohne Svflinger Stra_e 100 Tel: +49-731-399 499-0
eLvsungen 89077 Ulm Fax: +49-731-399 499-9


(David Alan Black) #3

Hello –

Hello to all,

First, I’m new to ruby so please forgive if I read over the solution in
the ruby doc or pickaxe book.

Welcome nuby!

I’d like to know if it’s possible to add a method to an object
instance at runtime? I tried sth. like instance_eval and “class <<
self” which works. But do I add a mixin at runtime?

I have a Mixin like this:

Module B
def B.append_features(aClass)
def aClass.foo
"foo"
end
end
end

o = Object.new
o.instance_eval<<-"end_eval"
include B
end_eval

But o.foo does not exist!?

Is there a way to use append_features together with class<<self ?

See Bob’s answer – but also, I’m wondering whether there might be
simpler ways to do what you want to do.

For example, simplest case of adding a method to an object:

o = Object.new
def o.foo
"foo"
end

p o.foo # “foo”

Also, if you want to include a module in an object’s metaclass:

module B
def foo
"foo"
end
end

o = Object.new
class << o
include B
end

p o.foo # “foo”

Do you actually need #append_features for what you want to do? (Not a
rhetorical question – it is at least as likely that I am missing the
point as that my simplifications are appropriate :slight_smile:

David

···

On Sat, 22 Jun 2002, Philipp Meier wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav


(Joel VanderWerf) #4

David Alan Black wrote:

Also, if you want to include a module in an object’s metaclass:

module B
def foo
"foo"
end
end

o = Object.new
class << o
include B
end

p o.foo # “foo”

Or use extend to get the same effect:

module B
def foo
"foo"
end
end

o = Object.new
o.extend B

p o.foo # “foo”


(Philipp Meier) #5

I have a Mixin like this:

Module B
def B.append_features(aClass)
def aClass.foo
"foo"
end
end
end

o = Object.new
o.instance_eval<<-"end_eval"
include B
end_eval

But o.foo does not exist!?

Is there a way to use append_features together with class<<self ?

See Bob’s answer – but also, I’m wondering whether there might be
simpler ways to do what you want to do.

[…]

Do you actually need #append_features for what you want to do? (Not a
rhetorical question – it is at least as likely that I am missing the
point as that my simplifications are appropriate :slight_smile:

To be honest I don’t remember the exact reason why I uses
append_features beside it was the only thing that worked out.

But I will try to give you a overview of the things I’m doing. As I did
not found any persistance framework for ruby I’m currently developing
one for my own. One of the most important features to me is that the
Object that should persisted can be easily declared. It came to my mind
to use a way like the attr_read function: attr_persist. This is declared
in a Mixim Module which simply declares a function that call a
Persistors write_attr or read_attr method:

Code extract:

module Persist

require ‘singleton’

class HashPersistor

include Singleton

include Enumerable

@allvalues = {}

def write(obj, attr, value)
  @allvalues = {} unless @allvalues != nil
  @allvalues[obj] = {} unless @allvalues.has_key? obj
  @allvalues[obj][attr] = value
end

def read(obj, attr)
  @allvalues[obj][attr]
end

def each
  @allvalues.keys
end

end

def Persist.append_features(includingClass)
super
def includingClass.persist(*ids)
for id in ids
ev=<<-"end_eval"
attr_accessor :persistor

  @@persistor = Persist::HashPersistor.instance 

  def #{id.id2name}(*args, &block)
    persistor.read(self, "#{id.id2name}")
  end

  def #{id.id2name}=(*args, &block)
    persistor.write(self, "#{id.id2name}", *args)
  end

    end_eval
  end

  ev+=<<-"end_eval"
def find_all
    persistor.each.to_a
  end
  end_eval

  module_eval ev
  end
end

end
end

I came to a problem when I’d implemented a dbiPersistor when I used to
restore an instance from the database. I need to declare all the
accessors to the persisted class fields at runtime. Sth. like the
following did not work:

o = Object.new
o.instance_eval<<-"env_eval"
class << self
include Persist
end
end_val

attrs.each(|attr| o.instance_eval(“persist :${attr}”));

I hope this gave you some insight in my motivation.

-billy.

···

On Sat, Jun 22, 2002 at 04:23:24AM +0900, David Alan Black wrote:


Meisterbohne Söflinger Straße 100 Tel: +49-731-399 499-0
eLösungen 89077 Ulm Fax: +49-731-399 499-9