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

Adds reader and writer methods for manipulating a value object: composed_of :address adds address and address=(new_address) methods.

Options are:

  • :class_name - specify the class name of the association. Use it only if that name can’t be inferred from the part id. So composed_of :address will by default be linked to the Address class, but if the real class name is CompanyAddress, you’ll have to specify it with this option.
  • :mapping - specifies a number of mapping arrays (attribute, parameter) that bind an attribute name to a constructor parameter on the value class.
  • :allow_nil - specifies that the aggregate object will not be instantiated when all mapped attributes are nil. Setting the aggregate class to nil has the effect of writing nil to all mapped attributes. This defaults to false.

An optional block can be passed to convert the argument that is passed to the writer method into an instance of :class_name. The block will only be called if the argument is not already an instance of :class_name.

Option examples:

  composed_of :temperature, :mapping => %w(reading celsius)
  composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) {|balance| balance.to_money }
  composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
  composed_of :gps_location
  composed_of :gps_location, :allow_nil => true
Show source
Register or log in to add new notes.
September 26, 2008
4 thanks

Example of composed_of composition class implementation

If we have following code in model:

composed_of :temperature, :mapping => %w(celsius)

Then our composition class can be this:

class Temperature
  def initialize(celsius)
    @celsius = celsius

  # This method is called by ActiveRecord, when record is saved.
  # Result of this method will be stored in table in "celsius" field,
  # and later when the record is loaded again, this will go to 
  # our Temperature#new constructor.
  def celsius

  # This is example of method that we can add to make this composition useful.
  def farenheit 
    @celsius * 9/5 + 32
July 15, 2008
1 thank

Mapping Order

Something I always forget is the order in which each mapping should be specified.

The first item is the attribute name in the ActiveRecord model, and the second is the name of the attribute in the ValueObject (the writer uses it to read from the VO).

Furthermore, the order in which mapping pairs are specified should be the same as the order the attributes are specified in the ValueObject’s initialize method (for the reader to be able to instantiate a VO with the record’s values).