Creating a dynamic attribute in a Struct instance

Is it possible to create a dynamic attribute in an instance Struct?

class Person < Struct.new(:name)
end

p = Person.new("Bilbo")
p[:surname] = "Jenkins" # does not work
+4
source share
3 answers

You can use OpenStruct:

require 'ostruct'

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

p.surname # => "Jenkins"
+1
source

You can define new methods in your class Personby doing the following:

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

I prefer define_methodinstead instance_evalbecause I try to omit evalwhen possible.

+1
source

Struct, , :

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
  #=> []

, . , "":

p.members
  #=> [:name]

name , Struct?

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

Yes! This is because instance members Structare stored in an array that you are not intended to access directly, only through accessors.

Can we add members Structdynamically? I do not know the answer to this question, but the methods do not allow to do this easily. Instead, just add instance variables and possibly accessors.

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

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

p.instance_variables
  #=> [:@surname]

and get its value with:

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

If you want to create accessors for this 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=]
0
source

Source: https://habr.com/ru/post/1569386/


All Articles