Wednesday, May 4, 2011

Juggling Ruby Class Names


In a recent task, we had generated several models that share some common behavior. However, inheritance was not an option as the names of attributes were different for each class.

In such a case, you need to provide functionality for each class separately. Much of the code will be duplicated in methods pertaining to each class, which runs against the DRY principle.

In Ruby, a class called "Object" comes to the rescue. Using this class, we can retrieve a class (to be used exactly as a class) given its name (as a string):

@klass = Object.const_get("class_name")

You can create a new instance of the class:

@instance = @klass.send "new"

You can then assign values to attributes by their  name (string):

@instance.attributes={attribute_symbol => attribute_value}

You can also invoke a method directly on the class such as "find" or "all":

@klass.send "find", parameter_1, parameter_2, ...

In the following snippet, we call a method from a module to create a link between any object and a tagging object. We can use this in our controllers several times, passing relevant parameters to each:

tag_object( 'SubjectTag', :subject_id, :tag_id, 312, 105)

The code implementing this function can be understood knowing the above hints:

def tag_object(tag_class, id_symbol, tag_symbol, id, tag)
      
    @klass = Object.const_get(tag_class)
      
    if (@klass.send "find", :all, :conditions => {id_symbol => id, tag_symbol => tag}).length > 0 then
        return false   
    end   # if
    new_tag = @klass.send "new"
    new_tag.attributes={id_symbol => id, tag_symbol => tag}
    return new_tag.save
end

Hoping this is useful.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.