Value Objects implementation in Ruby

Hi,

I am reading currently one design pattern called the value object. To
understand it I am reading a blog post
Value Objects Explained with Ruby — SitePoint, as it
contains a Ruby example.

The problem is the author of the post said, that, whenever you will try
to
change the attribute of a value object, you should create a new value
object.
Otherwise you will break the rule of value object . But we know in
Ruby, the
setter method always return the value it sets.

#!/usr/bin/env ruby

class Money
attr_reader :amount, :currency

def initialize(amount, currency)
@amount = amount
@currency = currency
end

def amount=(other_amount)
Money.new(other_amount, currency)
end

def ==(other_money)
self.class == other_money.class && amount == other_money.amount &&
currency == other_money.currency
end

alias :eql? :==

def hash
[amount, currency].hash
end
end

usd1 = Money.new(10, ‘usd’)
usd2 = Money.new(10, ‘usd’)

usd1.eql?(usd2) # => true
usd1 == usd2 # => true

usd = Money.new(10, ‘USD’)
p usd.inspect

other_usd = (usd.amount = 20)

p usd.inspect
p other_usd.inspect

>> “#<Money:0xa12f354 @amount=10, @currency="USD">”

>> “#<Money:0xa12f354 @amount=10, @currency="USD">”

>> “20”

Look the output of other_usd.inspect which returns “20”, as per the
author it
should return a new Money object.

My question is how then we implement/correct this flaw, which author
missed to
mention ?

Regards,
Arup R.

Debugging is twice as hard as writing the code in the first place.
Therefore,
if you write the code as cleverly as possible, you are, by definition,
not
smart enough to debug it.

–Brian Kernighan

On Aug 31, 2014, at 22:43, Arup R. [email protected]
wrote:

The problem is the author of the post said, that, whenever you will try to
change the attribute of a value object, you should create a new value object.
Otherwise you will break the rule of value object . But we know in Ruby, the
setter method always return the value it sets.

Id refrain from putting setters on immutable value objects. With the
Money object that has both a currency and amount, dont modify the
amount, instead add a Numeric or Money instance to it to get a new Money
object.

m = Money.new 100, USD

m + 50 #=> #<Money 150 USD>

m + Money.new(90, USD) #=> #<Money 190 USD>

m + Money.new(100, CND) # raises Money::NonMatchingCurrencyError

On Mon, Sep 1, 2014 at 4:43 AM, Arup R.
[email protected] wrote:

I am reading currently one design pattern called the value object. To
understand it I am reading a blog post
Value Objects Explained with Ruby — SitePoint, as it contains a Ruby
example.

The problem is the author of the post said, that, whenever you will try to
change the attribute of a value object, you should create a new value object.
Otherwise you will break the rule of value object . But we know in Ruby, the
setter method always return the value it sets.

My question is how then we implement/correct this flaw, which author missed to
mention ?

Do not use an assignment method then. :slight_smile: As Bryce mentioned one
approach is to provide proper mathematical constructs. I wrote about
this once:
http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html

For changing the currency you probably need an explicit conversion
method, e.g.

def to_currency(target_currency, conversion_factor = 1)
self.class.new[amount * conversion_factor, target_currency]
end

Btw. to make the approach more robust you can freeze the instance at
the end of #initialize.

Kind regards

robert