Skip to content
Permalink
Browse files

Add immutable option

  • Loading branch information...
skryukov committed Oct 25, 2019
1 parent a82d751 commit 68f943d30aae54c0a3eea55c373a9f84d93b6386
Showing with 36 additions and 11 deletions.
  1. +24 −11 lib/dry/equalizer.rb
  2. +12 −0 spec/unit/equalizer/universal_spec.rb
@@ -14,13 +14,16 @@ class Equalizer < Module
# #hash, and #inspect
#
# @param [Array<Symbol>] keys
# @param [Hash] options
# @option options [Boolean] :inspect whether to define #inspect method
# @option options [Boolean] :immutable whether to memoize #hash method
#
# @return [undefined]
#
# @api private
def initialize(*keys, inspect: true)
def initialize(*keys, **options)
@keys = keys.uniq
define_methods(inspect: inspect)
define_methods(**options)
freeze
end

@@ -36,17 +39,20 @@ def initialize(*keys, inspect: true)
# @api private
def included(descendant)
super
descendant.send(:include, Methods)
descendant.include Methods
end

# Define the equalizer methods based on #keys
#
# @param [Boolean] inspect whether to define #inspect method
# @param [Boolean] immutable whether to memoize #hash method
#
# @return [undefined]
#
# @api private
def define_methods(inspect: true)
def define_methods(inspect: true, immutable: false)
define_cmp_method
define_hash_method
define_hash_method(immutable: immutable)
define_inspect_method if inspect
end

@@ -70,10 +76,17 @@ def define_cmp_method
# @return [undefined]
#
# @api private
def define_hash_method
def define_hash_method(immutable:)
keys = @keys
define_method(:hash) do | |
keys.map(&method(:send)).push(self.class).hash
calculate_hash = ->(obj) { keys.map { |key| obj.send(key) }.push(obj.class).hash }
if immutable
define_method(:hash) do
@__hash__ ||= calculate_hash.call(self)
end
else
define_method(:hash) do
calculate_hash.call(self)
end
end
end

@@ -84,7 +97,7 @@ def define_hash_method
# @api private
def define_inspect_method
keys = @keys
define_method(:inspect) do | |
define_method(:inspect) do
klass = self.class
name = klass.name || klass.inspect
"#<#{name}#{keys.map { |key| " #{key}=#{__send__(key).inspect}" }.join}>"
@@ -122,6 +135,6 @@ def eql?(other)
def ==(other)
other.is_a?(self.class) && cmp?(__method__, other)
end
end # module Methods
end # class Equalizer
end
end
end
@@ -80,6 +80,7 @@
let(:klass) do
::Class.new do
attr_reader :firstname, :lastname
attr_writer :firstname
private :firstname, :lastname

def initialize(firstname, lastname)
@@ -151,6 +152,17 @@ def initialize(firstname, lastname)
.to eql('#<User firstname="John" lastname="Doe">')
end
end

context 'when immutable' do
describe '#hash' do

subject { Dry::Equalizer(*keys, immutable: true) }

it 'returns memoized hash' do
expect { instance.firstname = 'Changed' }.not_to(change { instance.hash })
end
end
end
end

context 'with duplicate keys' do

0 comments on commit 68f943d

Please sign in to comment.
You can’t perform that action at this time.