Forum: Ruby Declaring instance variables dynamically

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Alex S. (Guest)
on 2007-01-19 19:55
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?
Ryan D. (Guest)
on 2007-01-19 20:09
(Received via mailing list)
On Jan 19, 2007, at 9:55 AM, Alex Schearer wrote:

> 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
Tim P. (Guest)
on 2007-01-19 20:33
(Received via mailing list)
On 1/19/07, Ryan D. <removed_email_address@domain.invalid> wrote:
> > 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
>

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

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
unknown (Guest)
on 2007-01-19 21:45
(Received via mailing list)
On Jan 19, 2007, at 12:55 PM, Alex Schearer wrote:
> 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 W.
Alex S. (Guest)
on 2007-01-19 22:05
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.
unknown (Guest)
on 2007-01-19 22:43
(Received via mailing list)
On Jan 19, 2007, at 3:05 PM, Alex Schearer wrote:

> 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?


Gary W.
James G. (Guest)
on 2007-01-19 22:59
(Received via mailing list)
On Jan 19, 2007, at 2:42 PM, removed_email_address@domain.invalid wrote:

>> response.  I invariably find it more useful when more experienced
> I'll bet this has been done many times before.  Maybe in facets?
> Trans?

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

James Edward G. II
unknown (Guest)
on 2007-01-19 23:07
(Received via mailing list)
On Jan 19, 2007, at 3:58 PM, James Edward G. II wrote:
> It's called OpenStruct and it is included in Ruby's standard library.

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

Gary W.
Sam S. (Guest)
on 2007-01-20 00:50
(Received via mailing list)
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
This topic is locked and can not be replied to.