module ActiveRecord::Aggregations::ClassMethods

def composed_of(part_id, options = {}, &block)


:converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
:constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
:mapping => %w(ip to_i),
:class_name => 'IPAddr',
composed_of :ip_address,
composed_of :gps_location, :allow_nil => true
composed_of :gps_location
composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| balance.to_money }
composed_of :temperature, :mapping => %w(reading celsius)
Option examples:

in the assignment and is only called if the new value is not an instance of :class_name.
called when a new value is assigned to the value object. The converter is passed the single value that is used
* :converter - A symbol specifying the name of a class method of :class_name or a Proc that is
The default is :new.
are defined in the :mapping option, as arguments and uses them to instantiate a :class_name object.
initialize the value object. The constructor is passed all of the mapped attributes, in the order that they
* :constructor - A symbol specifying the name of the constructor method or a Proc that is called to
This defaults to +false+.
attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all mapped attributes.
* :allow_nil - Specifies that the value object will not be instantiated when all mapped
attributes are sent to the value class constructor.
name the attribute in the value object. The order in which mappings are defined determine the order in which
is represented as an array where the first item is the name of the entity attribute and the second item is the
* :mapping - Specifies the mapping of entity attributes to attributes of the value object. Each mapping
if the real class name is CompanyAddress, you'll have to specify it with this option.
from the part id. So composed_of :address will by default be linked to the Address class, but
* :class_name - Specifies the class name of the association. Use it only if that name can't be inferred
Options are:

composed_of :address adds address and address=(new_address) methods.
Adds reader and writer methods for manipulating a value object:
def composed_of(part_id, options = {}, &block)
  options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
  name        = part_id.id2name
  class_name  = options[:class_name]  || name.camelize
  mapping     = options[:mapping]     || [ name, name ]
  mapping     = [ mapping ] unless mapping.first.is_a?(Array)
  allow_nil   = options[:allow_nil]   || false
  constructor = options[:constructor] || :new
  converter   = options[:converter]   || block
  ActiveSupport::Deprecation.warn('The conversion block has been deprecated, use the :converter option instead.', caller) if block_given?
  reader_method(name, class_name, mapping, allow_nil, constructor)
  writer_method(name, class_name, mapping, allow_nil, converter)
  create_reflection(:composed_of, part_id, options, self)
end

def reader_method(name, class_name, mapping, allow_nil, constructor)

def reader_method(name, class_name, mapping, allow_nil, constructor)
  module_eval do
    define_method(name) do |*args|
      force_reload = args.first || false
      if (instance_variable_get("@#{name}").nil? || force_reload) && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? })
        attrs = mapping.collect {|pair| read_attribute(pair.first)}
        object = case constructor
          when Symbol
            class_name.constantize.send(constructor, *attrs)
          when Proc, Method
            constructor.call(*attrs)
          else
            raise ArgumentError, 'Constructor must be a symbol denoting the constructor method to call or a Proc to be invoked.'
          end
        instance_variable_set("@#{name}", object)
      end
      instance_variable_get("@#{name}")
    end
  end
end

def writer_method(name, class_name, mapping, allow_nil, converter)

def writer_method(name, class_name, mapping, allow_nil, converter)
  module_eval do
    define_method("#{name}=") do |part|
      if part.nil? && allow_nil
        mapping.each { |pair| self[pair.first] = nil }
        instance_variable_set("@#{name}", nil)
      else
        unless part.is_a?(class_name.constantize) || converter.nil?
          part = case converter
            when Symbol
             class_name.constantize.send(converter, part)
            when Proc, Method
              converter.call(part)
            else
              raise ArgumentError, 'Converter must be a symbol denoting the converter method to call or a Proc to be invoked.'
            end
        end
        mapping.each { |pair| self[pair.first] = part.send(pair.last) }
        instance_variable_set("@#{name}", part.freeze)
      end
    end
  end
end