Dynamic method access from an Object

Hi Everyone, I have a requirement where the method name of an Object will be generated and accessed dynamically in code. When I tried to access it showed me "undefined method". Am attaching a sample code of what i want to accomplish:

@user_information = UserInformation.new @table_fields = UserInformation.find_by_sql("DESC user_informations") for table_field in @table_fields     temp = table_field.Field # considering Field is `user_name`     @user_information.temp = "vasanth"     # referring to @user_information.user_name = "vasanth" end

In the above example i will get an error like "undefined method `temp`"

Can anyone tell me how to do this in Ruby on Rails? (I need something equivalent to "eval()" function present in Java Script)

Thanks in Advance. Regards, VASANTH

Guo Yangguang-2 wrote:

Hi Everyone, I have a requirement where the method name of an Object will be generated and accessed dynamically in code. When I tried to access it showed me "undefined method". Am attaching a sample code of what i want to accomplish:

@user_information = UserInformation.new @table_fields = UserInformation.find_by_sql("DESC user_informations") for table_field in @table_fields     temp = table_field.Field # considering Field is `user_name`     @user_information.temp = "vasanth"     # referring to @user_information.user_name = "vasanth" end

In the above example i will get an error like "undefined method `temp`"

Can anyone tell me how to do this in Ruby on Rails? (I need something equivalent to "eval()" function present in Java Script)

instance_variable_set is one method you should investigate for doing dynamic setters. You can find it sort of undocumented here: http://www.ruby-doc.org/core/classes/Object.src/M000381.html. But seriously, Google turns up lots on this topic.

Your code would then read:

for table_field in @table_fields     @user_information.instance_variable_set("@#{table_field}", "vasanth")     # referring to @user_information.send("@#{table_field}") # => "vasanth" end

Vasanthakumar Csk wrote:

I have a requirement where the method name of an Object will be generated and accessed dynamically in code. When I tried to access it showed me "undefined method". Am attaching a sample code of what i want to accomplish:

As well as Steve's suggestion for this case, there is a general method available in Ruby for sending messages to invoke methods where the method name is in a string. The method #send does this:

http://www.ruby-doc.org/core/classes/Object.html#M000334

However:

@user_information = UserInformation.new @table_fields = UserInformation.find_by_sql("DESC user_informations")

Try to avoid embedding SQL in your code if possible. You'll make it more readable and more portable (especially if you decide to change your database at some point). The above will return an array of column properties which is not what you're after anyway. To get the column names for a model, use:

@table_fields = UserInformation.column_names

for table_field in @table_fields     temp = table_field.Field # considering Field is `user_name`     @user_information.temp = "vasanth"     # referring to @user_information.user_name = "vasanth" end

If you are using a standard Rails model, then one of your columns will be called "id" and the above will try to overwrite this (numerical) value with a string. Not a good idea.

This code is also very model oriented but gives the impression you are wanting to put this into a controller. Whenever you find yourself doing model logic like this, re-think your strategy. This sort of stuff should be in the model. So, if you really want the model to be able to set all attributes (except "id"!) to a particular string, then in your model:

def set_to_string new_string   attributes.keys.each do | attr |     self.send "#{attr}=", new_string   end end

This form means that any setters you've overridden for any reason will be used and using #attributes to get the names automatically excludes "id".

This is, of course, dangerous. It assumes that all your attributes are string values (does the model have "created_at" and "updated_at"?) and that if you ever add columns, you'll want them to get the same treatment.

Thanks a lot Steve and Bush. All the informations that you gave was new and very useful to me. Here is a bit more explanation of what i am trying to do:

This code is also very model oriented but gives the impression you are wanting to put this into a controller. Whenever you find yourself doing model logic like this, re-think your strategy. This sort of stuff should be in the model. So, if you really want the model to be able to set all attributes (except "id"!) to a particular string, then in your model:

I understand that my code is model oriented, so its in model only. Anyway thanks again for your suggestion.

This is, of course, dangerous. It assumes that all your attributes are string values (does the model have "created_at" and "updated_at"?) and that if you ever add columns, you'll want them to get the same treatment.

Sorry that i couldn't explain you the real scenario or give the actual code for you to analyse. But the case is like this, I have a "table A" with columns a1,a2,a3,a4 & a5. Now, there will be a procedure (user action) which will change only one/two column(s) in that "table A" eg: a3. The need of this is to generate a report based on changed value. Also i should not over-write the original value of a3 in "table A".

Instead, i want to modify that object dynamically at run-time for further calculations. However, i dont know which column that user is going to change (till run-time).

So i have to write IF condition for all 5 columns of "table A". But this will get tedious because i have more than 20 tables with 40 columns each. Hope you understood my problem. As you said, the user wont/cant modify "id" or "created_at" or "updated_at" etc.

Hope you can help me in minimizing the code further, if any methods available.

Thanks in Advance. Regards, VASANTH

Vasanthakumar Csk wrote:

Instead, i want to modify that object dynamically at run-time for further calculations. However, i dont know which column that user is going to change (till run-time).

In your model:

def set_values columns, values   (0...columns.length).each do |i|     self.send "#{columns[i]}=", values[i]   end end

Then call this on the object you want to change with two arrays, one the set of columns the user wants to change and the other with the values. If the values will always be the same, then this reduces to:

def set_values columns, new_value   columns.each do | attr |     seld.send "#{attr}=", new_value   end end

You should be able to fit any of these to your particular situation.

Hi Mark, Thanks a lot for your help. Its working fine. Learnt few new things in ROR because of you, thanks once again for that.

Regards, Vasanth