Reputation: 17900
I have the following class:
class A
X = %w(a b c)
end
How can I create a list of static variables for class A
using the values in X
so that I will end up with the same thing as:
class A
a = 1
b = 2
c = 3
end
I found out that methods can be defined with define_method
, but I don't quite have a clue on how to do this for variables.
Upvotes: 1
Views: 442
Reputation: 355
With some meta programming madness you could do something like
class A
class << self
def A.X(args)
puts "Creating methods"
args.each do |arg|
define_singleton_method(arg) {
if class_variable_defined? "@@#{arg}"
class_variable_get("@@#{arg}")
else
nil
end
}
define_singleton_method("#{arg}=") { |val|
class_variable_set("@@#{arg}", val)
}
end
end
end
end
Would allow you to do like this
class A
X %w(a b c)
A.a = 1
A.b = 2
A.c = 3
end
Need to prefix with A.a, A.b and so on because of some ruby syntax thing
Upvotes: 1
Reputation: 369458
Unfortunately, what you want is AFAIK impossible. You simply cannot introduce local variables into a scope dynamically. You might be tempted to do something like this:
class A
X = %w(a b c)
end
class A
bnd = binding
X.each.with_index(1) do |var, i|
bnd.local_variable_set(var, i)
end
end
But that won't work. When you use local_variable_set
to create a new variable, that variable will only exist within the Binding
object, not in the scope from which the Binding
was created.
class A
bnd = binding
X.each.with_index(1) do |var, i|
eval("#{var} = #{i}", bnd)
end
end
This won't work either. In fact, it is simply equivalent to the first.
Okay, so you think: "let's not use a Binding
, then, just plain eval
":
class A
X.each.with_index(1) do |var, i|
eval("#{var} = #{i}")
end
end
Nope, this doesn't work either. The local variables still only get created inside the eval
, but they don't leak outside. (They did leak in 1.8 and older, but they don't anymore in 1.9+.) And in fact, even if they did get created, they would get created in the scope of the block. So, you would have to use for
/in
instead.
Also note that even if you managed to inject local variables into the scope, they are still local variables: you cannot access them from anywhere else. (Unless you somehow manage to get hold of the Binding
and call local_variable_get
.)
It would make much more sense to make them methods, which is much easier:
class A
X.each.with_index(1) do |var, i|
define_singleton_method(:var) do i end
end
end
Now, they can also be accessed from outside the scope:
A.a # => 1
or using the somewhat obscure method calling syntax using the scope resolution operator:
A::a # => 1
Of course, since they always return the same value anyway, why not make them constants?
class A
X.each.with_index(1) do |var, i|
const_set(var.upcase, i)
end
end
Upvotes: 4