Reputation: 1232
I'm new to Ruby and confused about how I can make a method in Class
similar to :attr_accessor
, in that it adds methods to a user class, but so these added methods have access to a pre-initialized instance variable. It's difficult for me to explain so here is a greatly simplified sample of my efforts:
class Class
def super_accessor_wow(attr_name)
attr_name = attr_name.to_s
new_var_name = "@crazy_var_name"
instance_variable_set(new_var_name, ["hi", "everyone"])
module_eval(%Q/
def super_#{attr_name}()
return @#{attr_name}
end
def super_#{attr_name}=(value)
@#{attr_name} = value
end
def greetings
return #{new_var_name}
end
/)
end
end
This is how I'm trying to use the new method on Class
to modify my own class:
class Foo
super_accessor_wow(:bar)
end
foo1 = Foo.new()
foo1.super_bar = 1000
puts foo1.super_bar
puts foo1.greetings.inspect
The first puts prints '1000'
The second puts prints 'nil'
, so my instance_variable_set
call in super_accessor_wow
seemingly has no effect.
I expected that the second puts would print '['hi', 'everyone']'
by the way. All of this code is contained in a single Ruby file.
Upvotes: 0
Views: 135
Reputation:
Your instance_variable_set
is called when you call super_accessor_wow
during the class definition. No instance of the class exists yet. You create an instance of the class when you call new
. You could add your @crazy_var_name
initialization to the constructor, or you could define it in the greetings
method:
Put the default in a class variable, and initialize the instance variable in the constructor (be aware that this creates a constructor for your class, and if you then create your own constructor, it will override this one):
class Class
def super_accessor_wow(attr_name)
attr_name = attr_name.to_s
new_var_name = "@crazy_var_name"
new_var_name_default = "@#{new_var_name}"
module_eval(%Q/
#{new_var_name_default} = ["hi", "everyone"]
def initialize()
#{new_var_name} = #{new_var_name_default}
end
def super_#{attr_name}()
return @#{attr_name}
end
def super_#{attr_name}=(value)
@#{attr_name} = value
end
def greetings
return #{new_var_name}
end
/)
end
end
class Foo
super_accessor_wow(:bar)
end
foo1 = Foo.new()
foo1.super_bar = 1000
puts foo1.super_bar
puts foo1.greetings.inspect
puts Foo.class_variable_get('@@crazy_var_name').inspect
puts foo1.instance_variable_get('@crazy_var_name').inspect
Outputs:
1000
["hi", "everyone"]
["hi", "everyone"]
["hi", "everyone"]
Define it in the greetings
method:
class Class
def super_accessor_wow(attr_name)
attr_name = attr_name.to_s
new_var_name = "@crazy_var_name"
module_eval(%Q/
def super_#{attr_name}()
return @#{attr_name}
end
def super_#{attr_name}=(value)
@#{attr_name} = value
end
def greetings
#{new_var_name} = ["hi", "everyone"] unless #{new_var_name}
return #{new_var_name}
end
/)
end
end
class Foo
super_accessor_wow(:bar)
end
foo1 = Foo.new()
foo1.super_bar = 1000
puts foo1.super_bar
puts foo1.greetings.inspect
Outputs
1000
["hi", "everyone"]
Upvotes: 1
Reputation: 2038
As noted in the comment, instance_variable_set takes a symbol, not a string, so we'll fix that up first.
instance_variable_set(new_var_name.to_sym, ["hi", "everyone"])
But the big issue is that instance_variable_set isn't being called by an instance of Foo, it's being called by the Foo class itself. So, an instance variable is being set, but not on what you expected.
Foo.instance_variable_get(:@crazy_var_name).inspect
# ["hi", "everyone"]
Upvotes: 0