The main purpose of a value object is answer meaningful questions for our application.
class Temperature
include Comparable
def initialize(temperature)
@temperature = Float(temperature) # in case of strings, hashes, arrays we should explicitly .freeze them
end
def cold?
temperature < COLD_THRESHOLD # Celsius degrees
end
def <=>(other)
temperature <=> other.temperature
end
def to_s
temperature.to_s
end
# Hashing implementation (transformation of the object_id into an integer)
# which used in checking for unique entries.
def hash
temperature.hash # the value must remain constant during the life span of the process
end
alias eql? ==
protected
COLD_THRESHOLD = new(15)
attr_reader :temperature
end
Temperature.new(3).cold? # => true
Temperature.new(3) > Temperature.new(20) # => false
Temperature.new(3) == Temperature.new(3) # => true
[Temperature.new(7), Temperature.new(20), Temperature.new(20)].uniq # => [7.0, 20.0]
We use a value @temperature
to define its identity in hashing implementation? That's right! Because value objects define their identity by their value. 20°C is.. 20°C – and it will be always equal to 20°C.
Another implementation using a gem
There is a gem called Values Show archive.org snapshot which is a tiny library for creating simple immutable value objects for ruby. Here is an example from above but with values gem.
require 'values'
class Temperature < Value.new(:temperature)
include Comparable
def cold?
self < COLD_THRESHOLD
end
def <=>(other)
temperature <=> other.temperature
end
def to_s
"#{temperature.to_s} °C"
end
protected
COLD_THRESHOLD = new(15)
end
Temperature.new(3).cold? # => true
Temperature.new(3) > Temperature.new(20) # => false
Temperature.new(3) == Temperature.new(3) # => true
[Temperature.new(7), Temperature.new(20), Temperature.new(20)].uniq
# => [#<Temperature temperature=7>, #<Temperature temperature=20>]
Posted by Alexander M to Ruby and RoR knowledge base (2016-11-23 10:49)