Declaring instance variables dynamically

Coming from PHP I want to take a class say,

class User
    @name
end

And call it:
u = User.new

And then add a second class variable previously not declared:
u.mood = "happy"

PHP being what it is, it let you declare public class variables in that
manner. Is there a way to acheive the same in Ruby?

···

--
Posted via http://www.ruby-forum.com/.

ri OpenStruct

···

On Jan 19, 2007, at 9:55 AM, Alex Schearer wrote:

Coming from PHP I want to take a class say,

class User
    @name
end

And call it:
u = User.new

And then add a second class variable previously not declared:
u.mood = "happy"

PHP being what it is, it let you declare public class variables in that
manner. Is there a way to acheive the same in Ruby?

It is a bit hard to sort out what you are really after because your
example and description don't really match at all with Ruby concepts.

In your example, @name is an instance variable associated with
the object User, which is an instance of Class. You aren't
actually declaring that variable but simply referencing it. In Ruby
instance variables come into existence when they are referenced, they
do not have to be declared ahead of time. In any case, using @name
within a class block like this doesn't imply anything about
variables associated with instances of User. @name is an instance
variable associated with the class itself, which in many other
languages would be called a 'class variable' but not in Ruby. In Ruby,
'class instance variable' is a very different thing than
'class variable'.

You then create an new instance of User and suggest that
you would like to be able to call 'u.mood = "happy"', which would
typically be an assignment to the instance variable @mood associated
with 'u'. This @mood is very different than @name and neither of
them are Ruby 'class variables', which are different beasts altogether.

If what you are really trying to do is have instances of User with
two attributes (instance variables) of @name and @mood, then you want
something like:

class User
   attr_accessor :name
   attr_accessor :mood
end

allowing you to do things like:

u = User.new
u.name = 'bob' # modifies @name associated with u
u.mood = 'happy' # modifies @mood associated with u

There are no class variables involved in this situation nor are there
any class instance variables.

If you don't even want to bother with the attr_accessor calls you can
then use the OpenStruct class as described in the other posts. Note,
OpenStruct simulates setter and getter methods for arbitrary attributes
but it doesn't actually store the data in instance variables. Instead
it uses an internal hash table:

require 'ostruct'
user = OpenStruct.new
user.name = "bob"
user.mood = "happy"
p user.instance_variables # ["@table"]
p user.instance_variable_get('@table') # {:name=>"bob", :mood=>"happy"}

Gary Wright

···

On Jan 19, 2007, at 12:55 PM, Alex Schearer wrote:

Coming from PHP I want to take a class say,

class User
    @name
end

And call it:
u = User.new

And then add a second class variable previously not declared:
u.mood = "happy"

PHP being what it is, it let you declare public class variables in that
manner. Is there a way to acheive the same in Ruby?

Thanks for the feedback. I am in fact trying to do what openstrtuct
allows despite any confusing language I might have employed. I was
curious whether there was any way to do so without employing a hash
table -- that isn't to say that PHP doesn't employ a hash table with its
objects. Now I know, and let me just also say thank you for the lengthy
response. I invariably find it more useful when more experienced
members of the community explain/tell me something than when they point
me to a reference document, though that is still more useful than no
response at all.

···

--
Posted via http://www.ruby-forum.com/.

http://www.ruby-doc.org/stdlib/libdoc/ostruct/rdoc/classes/OpenStruct.html

To elucidate a little more, OpenStruct is a great class that does what
you want ...

u = OpenStuct.new
u.name = 'bob'
u.mood = 'surly'

p u.name
p u.mood

u.freeze
u.age = 59 #=> raises TypeError

Blessings,
TwP

···

On 1/19/07, Ryan Davis <ryand-ruby@zenspider.com> wrote:

On Jan 19, 2007, at 9:55 AM, Alex Schearer wrote:

> Coming from PHP I want to take a class say,
>
> class User
> @name
> end
>
> And call it:
> u = User.new
>
> And then add a second class variable previously not declared:
> u.mood = "happy"
>
> PHP being what it is, it let you declare public class variables in
> that
> manner. Is there a way to acheive the same in Ruby?

ri OpenStruct

Well, it is certainly possible to roll your own class that
uses instance variables instead of the hash table. You can use
method_missing to capture calls to object.x and object.x= and then
set or get the matching instance variables.

I'll bet this has been done many times before. Maybe in facets? Trans?

Gary Wright

···

On Jan 19, 2007, at 3:05 PM, Alex Schearer wrote:

Thanks for the feedback. I am in fact trying to do what openstrtuct
allows despite any confusing language I might have employed. I was
curious whether there was any way to do so without employing a hash
table -- that isn't to say that PHP doesn't employ a hash table with its
objects. Now I know, and let me just also say thank you for the lengthy
response. I invariably find it more useful when more experienced
members of the community explain/tell me something than when they point
me to a reference document, though that is still more useful than no
response at all.

Alex Schearer wrote:

I was curious whether there was any way to do so without employing a
hash table

Here you go:

class User
  attr_accessor :name
end

=> nil

me = User.new
me.name = 'Sam'

=> 'Sam'

you = User.new
you.name = 'Alex'

=> 'Alex'

you.mood = :happy

=> NoMethodError...

you.class.send(:attr_accessor, :mood)
you.mood = :happy

=> :happy

me.mood = :potato

=> :potato

So you can extend classes at run-time any number of ways, this is just
one of them. You can make it more magical with method_missing and a
combination of instance_variable_get and instance_variable_set too:

module Magic
  def method_missing(sym, *args)
    name = sym.to_s
    if name[-1,1] == '='
      instance_variable_set("@#{name[0, name.size - 1]}", *args)
    else
      instance_variable_get("@#{name}")
    end
  end
end

=> nil

class Person
  include Magic
end

=> nil

p = Person.new
p.name = 'Alex'

=> 'Alex'

p.mood = :peppy

=> :peppy

puts p.name

=> Alex

It's called OpenStruct and it is included in Ruby's standard library.

James Edward Gray II

···

On Jan 19, 2007, at 2:42 PM, gwtmp01@mac.com wrote:

On Jan 19, 2007, at 3:05 PM, Alex Schearer wrote:

Thanks for the feedback. I am in fact trying to do what openstrtuct
allows despite any confusing language I might have employed. I was
curious whether there was any way to do so without employing a hash
table -- that isn't to say that PHP doesn't employ a hash table with its
objects. Now I know, and let me just also say thank you for the lengthy
response. I invariably find it more useful when more experienced
members of the community explain/tell me something than when they point
me to a reference document, though that is still more useful than no
response at all.

Well, it is certainly possible to roll your own class that
uses instance variables instead of the hash table. You can use
method_missing to capture calls to object.x and object.x= and then
set or get the matching instance variables.

I'll bet this has been done many times before. Maybe in facets? Trans?

The original poster wanted the setters and getters mapped to instance
variables. OpenStruct maps them to key/value pairs in an internal hash.

Gary Wright

···

On Jan 19, 2007, at 3:58 PM, James Edward Gray II wrote:

It's called OpenStruct and it is included in Ruby's standard library.