简体   繁体   中英

Dynamic Variables to access class methods in Ruby

Working in Ruby, we have to use a 3rd party Framework, which has a class setup something like this:

class Foo 

    attr_accessor :bar

    def initialize() 
    end

end

class Poorly_Designed_Class

    attr_accessor :thing1
    attr_accessor :thing2
    attr_accessor :thing3
    attr_accessor :thing4
    attr_accessor :thing5

    # through :thing_n .. number defined at runtime

    def initialize()
        @thing1 = Foo.new
        @thing2 = Foo.new
        @thing3 = Foo.new
        @thing4 = Foo.new
        @thing5 = Foo.new
    end

end

I don't know how many "things" there are until run time. there could be 5 or their could be 50.

What I would like to do is something like:

pdc = Poorly_Designed_Class.new
for i in 0..numberOfThings do
    pdc."thing#{i}".bar = value[i]
end

The above doesn't work.

I've also tried accessing it via:

instance_variable_set("pdc.thing#{i}.bar",value)

I understand that the class should be using an array or hash. Unfortunately I can't do anything about how the class is designed and we have to use it.

Is what i'm trying to do even possible?

You could either try to call the getter (preferably, since it honors encapsulation):

pdc = PoorlyDesignedClass.new
1.upto(number_of_things.times do |i|
  pdc.public_send(:"thing#{i}").bar = value[i]
end

or get the instance variable (less preferred, since it breaks encapsulation):

pdc = PoorlyDesignedClass.new
1.upto(number_of_things) do |i|
  pdc.instance_variable_get(:"@thing#{i}").bar = value[i]
end

So, you were on the right track, there were just two problems with your code: instance variable names start with an @ sign, and . is not a legal character in an identifier.

You're using Object#instance_variable_set incorrectly. The first argument must be a string or a symbol representing the name of an instance variable including the @ prefix: eg "@thing{i}" . However you actually want to get the value of an instance variable and then send #bar= to it. That can be done with Object#instance_variable_get :

1.upto(numberOfThings) { |i| pdc.instance_variable_get("@thing#{i}").bar = value[i] }

That's a bit long and since attr_acessor :thingX defines getter methods, it's usually preferable to call them with Object#public_send instead of directly accessing the instance variable (a getter method might do something else than just returning a value):

1.upto(numberOfThings) { |i| pdc.public_send("thing#{i}").bar = value[i] }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM