Loading…
Implementing Equality in Ruby
2023-10-18
- Source
- Shopify
- Published
- Added to Yomu
Summary
Ruby has several equality and comparison mechanisms, so custom classes need explicit implementations when instance identity is not the intended meaning. The post distinguishes value objects, whose equality depends on all attributes, from entities, whose equality depends on an explicit identifier; its Point and Employee examples also use class checks, while an entity with a nil ID is unequal to other entities. It defines reflexivity, symmetry, and transitivity as properties to preserve, notes NaN as an exception to reflexivity, and explains #equal?, #eql?, #hash, and #===. For ordered values, #<=> returns -1, 0, 1, or nil, while Comparable supplies relational operators and useful methods including #min, #sort, #between?, and #clamp.
Context
Ruby has multiple equality mechanisms, and incorrect implementations can conflict with expected object behavior and cause bugs. The relevant notion of equality differs between entities, value objects, and ordinary object instances.
Approach / What changed
Implement equality according to the object’s identity: compare all attributes for value objects and explicit IDs for entities, while preserving class checks and equality properties. Use #eql? with #hash for hash-key behavior, #<=> for ordering, and include Comparable to obtain relational operators and related methods.
Takeaways
- Value objects such as Point instances are equal when all identity-defining attributes match; entities are equal when their explicit IDs match, and an entity with a nil ID is not equal to another entity.
- Ruby equality implementations should preserve reflexivity, symmetry, and transitivity. Object#== provides same-instance behavior by default, while NaN is noted as an exception to reflexivity.
- A #<=> implementation returns -1, 0, 1, or nil for ordering. Including Comparable adds <, <=, >, >=, between?, and clamp, and enables methods such as min, max, minmax, and sort.