From: Zach Dennis Date: 2005-07-21T01:19:25+09:00 Subject: Re: Variable class (newb question) Shaun Fanning wrote: > I'm trying to figure out how to use Ruby to implement a strategy type > pattern that I used in PHP. Basically I took a set of class names passed in > as variables and instantiated the right class depending on the value of the > variable. It was roughly something like: > > Class SurveyQuestion > drawQuestion > storeResponse > reportResponse > ... > > Class SurveyQuestionMultiChoice extends SurveyQuestion > Class SurveyQuestionCheckBox extends SurveyQuestion > ....etc. One way using a base class and subclassing similar to your approach in PHP: class A def talk "talking" end end class B < A end defget_class_for_string( class_name ) eval( "#{class_name}.new" ) end obj = get_class_for_string( "B" ) obj.talk > > //build a list of question types based on what the user just submitted > $these_survey_questions = array('MultiChoice', 'MultiChoice', 'CheckBox') > > For each $these_survey_questions as $index=$question_type > $class_name = "SurveyQuestion".$question_type > $q = new $class_name() > $q.storeResponse($response_from_this_user) > > > So I'm struggling to figure out how this type of thing would typically be > done using Ruby. Or maybe it's a bad approach to this type of situation to > begin with so feel free to offer an alternative. The only thing I don't like about the above solution is potential insecurity if someone passes in " ; B" , the would execute. This could changed to be more secure if you were always using toplevel classes... def get_class_for_string( class_name ) eval("#{class_name}.new") if Object.constants.include?( class_name ) end Which the above code makes sure that the passed in class_name has been defined on the top-level Object otherwise it will return nil. This won't work for things like "MyModule::B" or "MyClass::InnerClass:B", although you could change it to work. And here is the modified version to make it work across the board: def get_class_for_string( class_name ) last_constant = Object class_name.split( /::/ ).each do |cons_str| if last_constant.constants.include?( cons_str ) last_constant = eval( "last_constant::#{cons_str}" ) else return nil end end eval("#{last_constant}.new") end Then you could do stuff like... get_class_for_string( "A" ) get_class_for_string( "M::C" ) get_class_for_string( "M::C::D::E::F" ) HTH, Zach