Dynamically added methods

(Yann Klis) #1

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 :slight_smile:

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

(Austin Ziegler) #2

Use OpenStruct.

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

-austin

路路路

On 8/11/05, Yann Klis <strass@strasslab.net> wrote:

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 :slight_smile:

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 ?

--
Austin Ziegler * halostatue@gmail.com
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽* Alternate: austin@halostatue.ca

(Shalev NessAiver) #3

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

路路路

On Aug 11, 2005, at 1:48 PM, Yann Klis wrote:

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 :slight_smile:

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

(Yann Klis) #4

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

Thanks,

yk

路路路

Le vendredi 12 ao没t 2005 脿 05:29 +0900, Shalev NessAiver a 茅crit :

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

On Aug 11, 2005, at 1:48 PM, Yann Klis wrote:

> 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 :slight_smile:
>
> 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
>
>
>

(Zach Dennis) #5

Yann Klis wrote:

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

(Robert) #6

Yann Klis wrote:

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

require 'ostruct'

=> true

object = OpenStruct.new( "attribute1" => "value1", "attribute2" =>

?> "value2")
=> <OpenStruct attribute1="value1" attribute2="value2">

object.attribute1

=> "value1"

object.attribute2

=> "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

(Zach Dennis) #7

Robert Klemme wrote:

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