1

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.

10
  • Never used a local variable inside a class like this. What are you trying to do? Commented Feb 10, 2015 at 14:06
  • 1
    What is a static variable? Commented Feb 10, 2015 at 14:07
  • I am trying to refactor some variables which act as an enum. Commented Feb 10, 2015 at 14:10
  • 3
    A::a is something completely different than what you have. What you have in your post are local variables. Local variables go out of scope as soon as the class definition ends, they cannot be accessed anywhere else. What you have in your comment is a method call: you are calling method a on class A. That's a totally different story, and much easier to do. Commented Feb 10, 2015 at 14:26
  • 1
    Maybe a constant would be more appropriate, i.e. A::A, A::B and A::C Commented Feb 10, 2015 at 14:27

3 Answers 3

4

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
Sign up to request clarification or add additional context in comments.

Comments

3

Constants are defined like this (they have to start with an uppercase letter):

class A
  A = 1
  B = 2
  C = 3
end

A::A #=> 1
A::B #=> 2
A::C #=> 3

To define them dynamically, use const_set:

class A
  %w(A B C).each.with_index(1) { |c, i| const_set(c, i) }
end

1 Comment

This is what I was after. Thank you!
1

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

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.