From: Stefano Crocco Date: 2008-03-16T04:21:15+09:00 Subject: Re: How to iterate through instance attribute names (attr_accessor) On Saturday 15 March 2008, me wrote: > Say, I have a class: > > class Event > attr_accessor :a, > > :b, > :c, > :d > > def initialize() > end > > def validate > # validate all attributes at once here > end > > protected > > def validate_a > # a can be any number from 1-10 if b = 'Y' and c is blank > end > > def validate_b > # b can be 'Y' or 'N' > end > > def validate_c > end > > def validate_d > end > > end > > And in method validate, I'd like to iterate through all the attributes > and run their respective validating method. Is there a way for me to > be able to iterate through my list of attr_accessor symbols and > reference their names to know which method to call? > > Thanks, > > -Al You must understand that all attr_accessor does is create a couple of methods: a setter and a getter. For example attr_accessor :a creates the two methods a and a=, which are more or less equivalent to the following hand-written methods: def a @a end def a= value @a = value end Once defined, the methods created by attr_accessor can't be distinguished from the other instance methods of the class. This means that you can't iterate on the methods themselves. What you can do is iterate on the instance variables methods created this way refer to. For example, your 'validate' method could be something like: def validate instance_variables.each{|v| send "validate_#{v.sub('@','')}"} end instance_variables is a method which returns an array with the names of all the instance variables of the object. The method send, instead, calls the method with the name passed as its first argument (in this case, 'validate_a', 'validate_b' and so on. v.sub is needed because the names of the instance variables contain the initial '@'). The code above works provided there's a validate_ method for each instance variable. If this is not the case, you can enclose the call to send in a begin/rescue block: def validate instance_variables.each do |v| begin send "validate_#{v.sub('@','')}" rescue NoMethodError end end end Of course, you can also keep an array of the names of those instance variables for which a validation method exists and use the following code: def validate [:a, :b, :c, :d].each{|v| send "validate_#{v}"} end I hope this helps Stefano