Friday, June 29, 2007

Open classes

When I want to change or add new behavior to existing class, static typed language give me only two options create new class that either extend existing class or delegate to existing class (in Java world some peoples do aspect oriented programming AOP, but it is little bit complicated and is external to the language).
Ruby give me an ability to modify existing class directly. A cross concern or aspect then can be added easily by modifying existing class.

ADDING NEW FEATURE TO EXISTING CLASS
class Object
   def blank?
      return true if nil?
      return true if respond_to?(:empty?) && empty?
      return false
   end
end
nil.blank? #=> true
''.blank?  #=>true
[].blank?  #=>true
{}.blank?  #=>true
'hello'.blank? #false
[:a].blank? #false
{:a=>1}.blank? #false
The above code define method blank? in class Object, which is root of all Ruby classes. This will result in well behaving instances of NilClass, String, Array, Hash.

DECORATION A METHOD OF EXISTING CLASS
To decorate a method of existing class is just easy as adding new one e.g.
require 'active_support'
class Hash
    alias original_symbolize_keys! symbolize_keys!

    def symbolize_keys!
        each_value do |value|
             value.symbolize_keys! if value.is_a?(Hash)
        end
        original_symbolize_keys!
    end
end
Rails ActiveSupport add method symbolize_keys!, above code decorate it to convert keys to symbol for a hash value if it is also hash.
When modifying an existing class, we shall make sure that the class is already loaded, otherwise we may get a strange behavior. This is specially important in rails, when classes are loaded dynamically using const_missing method. About how to handle this problem, there is good tip reopen with class/module eval on practical ruby blog. Other way to decorate method without using alias is available on replacing methods.

No comments: