123 lines
2.6 KiB
Ruby
123 lines
2.6 KiB
Ruby
|
# encoding: utf-8
|
||
|
|
||
|
# Define equality, equivalence and inspection methods
|
||
|
class Equalizer < Module
|
||
|
|
||
|
# Initialize an Equalizer with the given keys
|
||
|
#
|
||
|
# Will use the keys with which it is initialized to define #cmp?,
|
||
|
# #hash, and #inspect
|
||
|
#
|
||
|
# @param [Array<Symbol>] keys
|
||
|
#
|
||
|
# @return [undefined]
|
||
|
#
|
||
|
# @api private
|
||
|
def initialize(*keys)
|
||
|
@keys = keys
|
||
|
define_methods
|
||
|
freeze
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
# Hook called when module is included
|
||
|
#
|
||
|
# @param [Module] descendant
|
||
|
# the module or class including Equalizer
|
||
|
#
|
||
|
# @return [self]
|
||
|
#
|
||
|
# @api private
|
||
|
def included(descendant)
|
||
|
super
|
||
|
descendant.module_eval { include Methods }
|
||
|
end
|
||
|
|
||
|
# Define the equalizer methods based on #keys
|
||
|
#
|
||
|
# @return [undefined]
|
||
|
#
|
||
|
# @api private
|
||
|
def define_methods
|
||
|
define_cmp_method
|
||
|
define_hash_method
|
||
|
define_inspect_method
|
||
|
end
|
||
|
|
||
|
# Define an #cmp? method based on the instance's values identified by #keys
|
||
|
#
|
||
|
# @return [undefined]
|
||
|
#
|
||
|
# @api private
|
||
|
def define_cmp_method
|
||
|
keys = @keys
|
||
|
define_method(:cmp?) do |comparator, other|
|
||
|
keys.all? { |key| send(key).send(comparator, other.send(key)) }
|
||
|
end
|
||
|
private :cmp?
|
||
|
end
|
||
|
|
||
|
# Define a #hash method based on the instance's values identified by #keys
|
||
|
#
|
||
|
# @return [undefined]
|
||
|
#
|
||
|
# @api private
|
||
|
def define_hash_method
|
||
|
keys = @keys
|
||
|
define_method(:hash) do ||
|
||
|
keys.map(&method(:send)).push(self.class).hash
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Define an inspect method that reports the values of the instance's keys
|
||
|
#
|
||
|
# @return [undefined]
|
||
|
#
|
||
|
# @api private
|
||
|
def define_inspect_method
|
||
|
keys = @keys
|
||
|
define_method(:inspect) do ||
|
||
|
klass = self.class
|
||
|
name = klass.name || klass.inspect
|
||
|
"#<#{name}#{keys.map { |key| " #{key}=#{send(key).inspect}" }.join}>"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# The comparison methods
|
||
|
module Methods
|
||
|
|
||
|
# Compare the object with other object for equality
|
||
|
#
|
||
|
# @example
|
||
|
# object.eql?(other) # => true or false
|
||
|
#
|
||
|
# @param [Object] other
|
||
|
# the other object to compare with
|
||
|
#
|
||
|
# @return [Boolean]
|
||
|
#
|
||
|
# @api public
|
||
|
def eql?(other)
|
||
|
instance_of?(other.class) && cmp?(__method__, other)
|
||
|
end
|
||
|
|
||
|
# Compare the object with other object for equivalency
|
||
|
#
|
||
|
# @example
|
||
|
# object == other # => true or false
|
||
|
#
|
||
|
# @param [Object] other
|
||
|
# the other object to compare with
|
||
|
#
|
||
|
# @return [Boolean]
|
||
|
#
|
||
|
# @api public
|
||
|
def ==(other)
|
||
|
other = coerce(other) if respond_to?(:coerce, true)
|
||
|
other.kind_of?(self.class) && cmp?(__method__, other)
|
||
|
end
|
||
|
|
||
|
end # module Methods
|
||
|
end # class Equalizer
|