Dynamically added methods

Y

Yann Klis

Hi all,

I'm pretty new to ruby so I will ask some dumb questions, sorry if it's
not the right place to ask :)

I'd like to add some methods dynamically to one of my object.
I understand that with "method_missing" I can "emulate" this behaviour.

However, imagine I have an hash table. The key would be the attributes
of my object, and the keys their value. How could I add dynamically
accessors for those attributes/keys ?

Thanks in advance,

++

yk
 
A

Austin Ziegler

Hi all,
=20
I'm pretty new to ruby so I will ask some dumb questions, sorry if it's
not the right place to ask :)
=20
I'd like to add some methods dynamically to one of my object.
I understand that with "method_missing" I can "emulate" this behaviour.
=20
However, imagine I have an hash table. The key would be the attributes
of my object, and the keys their value. How could I add dynamically
accessors for those attributes/keys ?

Use OpenStruct.

require 'ostruct'
os =3D OpenStruct.new
os.foo =3D "hello"
os.bar =3D "world"
puts "#{os.foo}, #{os.bar}"

-austin
--=20
Austin Ziegler * (e-mail address removed)
* Alternate: (e-mail address removed)
 
S

Shalev NessAiver

To clarify - you want to be able to do something like this:

class YourObject

def initialize
@myhash = {'key1' => 'value1', 'key2' => 'value2', 'key3' =>
'value3'}
end

end

YourObject.new.key1 # Returns 'value1'
YourObject.new.key2 # Returns 'value2'



.... like that?

-Shalev
 
Y

Yann Klis

In fact, I'd like to mimic the behaviour of ActiveRecord.

If I have something like this:

class Base

def initialize(attributes)
#do something clever with attributes
end

end

class MyObject < Base
end

I'd like to be able to do that:

object =3D MyObject.new({"attribute1" =3D> "value1", "attribute2" =3D>
"value2"})
val =3D object.attribute1

Thanks,

yk

Le vendredi 12 ao=C3=BBt 2005 =C3=A0 05:29 +0900, Shalev NessAiver a =C3=A9=
crit :
 
Z

Zach Dennis

Yann said:
In fact, I'd like to mimic the behaviour of ActiveRecord.

If I have something like this:

class Base

def initialize(attributes)
#do something clever with attributes
end

end

class MyObject < Base
end

I'd like to be able to do that:

object = MyObject.new({"attribute1" => "value1", "attribute2" =>
"value2"})
val = object.attribute1

To create methods on the class itself so all instances of that class get
the method you can do:

class A
def initialize( args=nil )
if args.is_a? Hash
args.each { |key,val| self.class.send( :define_method, key, proc
{ val } ) }
end
end
end

a = A.new( :zzz => "testing" )
puts a.zzz
b = A.new
puts b.zzz

To create methods on the instance you have created only you can do:

class A
def initialize( args=nil )
if args.is_a? Hash
s = class << self; self; end
args.each { |key,val| s.send( :define_method, key, proc { val } ) }
end
end
end

a = A.new( :zzz => "testing" )
puts a.zzz
b = A.new
puts b.zzz #this will throw an error,because method zzz doesn't exist on
this instance

Note, that I think the second example above is bad design. We are
relying on an instance of A to give the class A extra methods for all
instances. We should instead rely on a singleton method for class A to
handle this task. (keep reading)

The above two solutions don't take into consideration if a method
already exists though, so you could easily overwrite an existing method.
Another way to do this is by overriding the method missing for your
class which won't override existing methods. The below will handle
things for instances only, like the first example:

class A
def initialize( args={} )
@args = args if args.is_a? Hash
end

def method_missing( method )
@args[method] || super
end
end

puts A.new( :a => "z" ) # prints out "z"
A.new.a #throws an error because :a wasn't passed in to this object


And you can alter that to add methods for the class also so all
instances get the methods, although above in the second example I think
we used bad designed. So here we fix it by using a singleton method
'add_attributes':

class A
def self.add_attributes( attributes )
@@attributes = attributes
end

def initialize( args={} )
@args = args if args.is_a? Hash
end

def method_missing( method )
@@attributes[method] || super
end
end

A.add_attributes( :attr1 => "attribute 1", :attr2 => "attribute 2" )
puts A.new.attr1
puts A.new.attr2

I hope this helps!

Zach
 
R

Robert Klemme

Yann said:
In fact, I'd like to mimic the behaviour of ActiveRecord.

If I have something like this:

class Base

def initialize(attributes)
#do something clever with attributes
end

end

class MyObject < Base
end

I'd like to be able to do that:

object = MyObject.new({"attribute1" => "value1", "attribute2" =>
"value2"})
val = object.attribute1

As others have pointed out already, use OpenStruct:

09:51:10 [robert.klemme]: irbs?> "value2")
=> "value2"

If you need inheritance, you can do

class MyObject < OpenStruct
end

That's it.

If you need additional initialization, you'll have to do

class MyObject < OpenStruct
def initialize(attrs)
super(attrs)
# do more init
end
end

Kind regards

robert
 
Z

Zach Dennis

Robert said:
As others have pointed out already, use OpenStruct:

OpenStruct uses the method_missing magic as pointed out in my last post,
so you know now how it does what it does. Refer to ostruct.rb for more
implementation details. =)

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

Forum statistics

Threads
474,176
Messages
2,570,947
Members
47,499
Latest member
DewittK739

Latest Threads

Top