7

Is it possible to create attribute dynamicaly in the Struct instance?

class Person < Struct.new(:name)
end

p = Person.new("Bilbo")
p[:surname] = "Jenkins" # does not work
1
  • @joelparkerhenderson Thanks for answer, but this not what I need. I gave the vote. Commented Jan 6, 2015 at 3:12

3 Answers 3

3

You could use an OpenStruct:

require 'ostruct'

p = OpenStruct.new(name: "Bilbo")
p[:surname] = "Jenkins"

p.surname # => "Jenkins"
Sign up to request clarification or add additional context in comments.

1 Comment

I got instance of Struct, and can't use OpenStruct.
1

You can define new methods on your Person class by doing this:

Person.send(:define_method, :surname){@surname}
Person.send(:define_method, :surname=){|x|@surname=x}

I prefer define_method instead of instance_eval because I try to omit eval when possible.

2 Comments

In this case size, values, members methods will not work properly.
The size, values, and members will continue to work properly because they return information about the struct members; the new method is adding an instance variable, which is not a struct member, so doesn't affect size, values, and members.
1

You can subclass Struct, in which case you will be creating two classes, but it is more common to just create one:

Person = Struct.new(:name)

Person.instance_methods(false)
  #=> [:name, :name=]

p = Person.new("Bilbo")
  #=> #<struct Person name="Bilbo">

Does `p` have an instance variable `@name` whose value is `"Bilbo"`?

p.instance_variables
  #=> []

No, it does not. Rather, it has "members":

p.members
  #=> [:name]

Can name be treated as an instance variable with the accessors provided by Struct?

p.name
  #=> "Bilbo"
p.name = "cat"
p.name
  #=> "cat"

Yes! That's because the members of a Struct instance are stored in array that you are not intended to access directly, only through the accessors.

Can we add Struct members dynamically? I don't know the answer to that, but methods are not provide to do that easily. Instead, just add instance variables and, optionally, accessors.

We can add an instance variable and set its value with:

p.instance_variable_set('@surname', 'Jenkins')
  #=> "Jenkins"

p.instance_variables
  #=> [:@surname]

and retrieve its value with:

p.instance_variable_get('@surname')
  #=> "Jenkins"

If you wish to create accessors for that variable, this is one way:

p.class.instance_eval do
  attr_accessor :surname
end

p.surname
  #=> "Jenkins"
p.surname = 'cat'
  #=> "cat"
p.surname
  #=> "cat"

p.class.instance_methods(false)
  #=> [:name, :name=, :surname, :surname=]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.