Ruby Value Objects

#code #summaries #ruby #codesummary #value #objects

# Code Summary for https://thoughtbot.com/blog/value-object-semantics-in-ruby # Part 1/4 : Concepts of equality in Ruby # Two concepts of equality in Ruby: # 1️⃣ Equal by identity (when objects are the same object in memory) # 2️⃣ Equal by value (when objects represents the same value) # Some objects supports by default comparison by value obj_a="Model" obj_a.object_id # 31320 obj_b="Model" obj_b.object_id # 55600 obj_a== obj_b # ✅ true because== compares in this case by value obj_a.equal?(obj_b) # ❌ false because .equal? compares by identity # But when you create your own class, by default the comparison is by identity class Duration def initialize(duration) = @duration = duration end Duration.new(5) == Duration.new(5) # ❌ false - compared by default by identify
# Code Summary for https://thoughtbot.com/blog/value-object-semantics-in-ruby # Part 2/4 : Value objects # 1. Should== each other # 2. Should NOT #equal? each other (as equal? should compare by identity) # 3. Should #hash to the same value # 4. Should #eql? each other (Ruby expects objects that have them same hash to be eql?) class Duration attr_reader :minutes def initialize(minutes) = @minutes = minutes def==(other) = minutes== other.minutes alias_method :eql?, :== def hash=[self.class, minutes].hash end # #hash and #eql? are importyant as it allows the value object to be used as hash key events={ Duration.new(5) => [ 1, 2, 3], Duration.new(10) => [5, 6, 7] } events[Duration.new(5)] # will return [1, 2, 3] # Struct objects are compared by value by default Duration=Struct.new(:minutes) Duration.new(5) == Duration.new(5)
# Code Summary for https://thoughtbot.com/blog/value-object-semantics-in-ruby # Part 3/4: Value objects should be immutable # add freeze to make the object immutable class Duration attr_reader :minutes def initialize(minutes) @minutes = minutes freeze end def add(other) = @minutes += other.minutes def +(other) = Duration.new(self.minutes + other.minutes) end d=Duration.new(5) t=Duration.new(10) d.add(t) # ❌ will throw error FrozenError s=(d + t) # will create a new object
 # Part 4/4: An extra note about working with strings and freeze # If you add at the top of your file the magic comment # # frozen_string_literal: true # or freeze the string obj_a="Model".freeze obj_b="Model".freeze # Then "Model" will always be the same object obj_a.equal?(obj_b) # ✅ true obj_a.object_id == obj_b.object_id # ✅ true obj_a== obj_b # ✅ true

Reply

or to participate.