How to use validation with aggregation (composed_of)?


#1

At the risk of being banned for posting the same question twice, I
thought I’d try once more with a question for the title rather than a
statement (on the basis that perhaps questions get answered and
statements ignored :wink: )

ActiveRecord supports composed_of for value objects which is
fantastic but one thing that it doesn’t seem to support (or at least
I am unable to find any documentation for) validation of the value
objects.

For example, given the following:

class Message < ActiveRecord::Base
composed_of :sender, :class_name => ‘EmailAddress’
composed_of :recipient, :class_name => ‘EmailAddress’

end

class EmailAddress
attr_reader :name, :address
def initialize(name, address)
@name, @address = name, address
end

end

How can I best take advantage of Rails’ validation, given that I’d
like to only specify the validation rules for EmailAddress once. The
validation rules might look something like:

validates_presence_of :name, :address
validates_format_of :email, :with => ADDRESS_FORMAT

I’m guessing the obvious answer is to create an email_addresses table
and use belongs_to but that seems (to me at least) a somewhat heave-
handed approach.

Regards,

Simon


Simon Harris
RedHill Consulting, Pty. Ltd.
12/55-67 Batman Street
West Melbourne VIC Australia 3003
http://www.redhillconsulting.com.au
mob: +61 417 505 611
yahoo/msn/skype: haruki_zaemon
gmail: haruki.zaemon
icq: 20461518


#2

Simon Harris wrote:

ActiveRecord supports composed_of for value objects which is fantastic
but one thing that it doesn’t seem to support (or at least I am unable
to find any documentation for) validation of the value objects.

Here’s an attempt at a solution. Any better way?

class Message < ActiveRecord::Base
composed_of :sender, :class_name => ‘EmailAddress’,
:mapping => [[:sender_name], [:sender_address]]

composed_of :recipient, :class_name => ‘EmailAddress’,
:mapping => [[:recipient_name], [:recipient_address]]

def validate
[‘sender’, ‘recipient’].each do |attr|
obj = self.send(attr)
[‘name’, ‘address’].each do |field|
msg = obj.send(“valid_#{field}?”)
# choose one of the following
errors.add_to_base( “#{attr}'s #{field} #{msg}” ) unless
msg.blank?
errors.add( “#{attr}_#{field}”, msg ) unless msg.blank?
end
end
super
end
end

class EmailAddress
attr_reader :name, :address

def initialize( name, address )
@name, @address = name, address
end

def valid_address?
@valid_address ||=
check_valid?( @address ) ? ‘’ : ‘is not a valid email address’
end

def valid_name?
@valid_name ||=
if @name.blank?
‘cannot be blank’
elsif @name =~ /[A-Za-Z][A-Za-z -]*/
‘’
else
‘contains invalid characters’
end
end
end


We develop, watch us RoR, in numbers too big to ignore.