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.
51680328b7b84f611614e893ab02cf0c?d=identicon&s=25 Alex Schearer (pirateking)
on 2007-01-19 18: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?
5a837592409354297424994e8d62f722?d=identicon&s=25 Ryan Davis (Guest)
on 2007-01-19 19: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
4d5b5dd4e263d780a5dfe7ac8b8ac98c?d=identicon&s=25 Tim Pease (Guest)
on 2007-01-19 19:33
(Received via mailing list)
On 1/19/07, Ryan Davis <ryand-ruby@zenspider.com> 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
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 unknown (Guest)
on 2007-01-19 20: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 Wright
51680328b7b84f611614e893ab02cf0c?d=identicon&s=25 Alex Schearer (pirateking)
on 2007-01-19 21: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.
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 unknown (Guest)
on 2007-01-19 21: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 Wright
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-01-19 21:59
(Received via mailing list)
On Jan 19, 2007, at 2:42 PM, gwtmp01@mac.com 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 Gray II
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 unknown (Guest)
on 2007-01-19 22:07
(Received via mailing list)
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.

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
41c597a48c80e37ba68d1adc7095ea0e?d=identicon&s=25 Sam Smoot (Guest)
on 2007-01-19 23: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.