diff --git a/lib/immutable-struct.rb b/lib/immutable-struct.rb index 31d09e9..b914461 100644 --- a/lib/immutable-struct.rb +++ b/lib/immutable-struct.rb @@ -82,13 +82,18 @@ def self.new(*attributes,&block) end alias_method :eql?, :== + + define_method(:hash) do + attribute_values = attributes.map { |attr| self.send(attr) } + (attribute_values + [self.class]).hash + end end klass.class_exec(&block) unless block.nil? imethods = klass.instance_methods(include_super=false) klass.class_exec(imethods) do |imethods| define_method(:to_h) do imethods.inject({}) do |hash, method| - next hash if [:==, :eql?, :merge].include?(method) + next hash if [:==, :eql?, :merge, :hash].include?(method) hash.merge(method.to_sym => self.send(method)) end end diff --git a/spec/immutable_struct_spec.rb b/spec/immutable_struct_spec.rb index 67801ab..854961f 100644 --- a/spec/immutable_struct_spec.rb +++ b/spec/immutable_struct_spec.rb @@ -192,5 +192,45 @@ def lawsuit end + describe "hash" do + + it "should have same hash value as itself" do + @k1_a.hash.eql?(@k1_a.hash).should be true + end + + it "should have same hash value as same class with identical attribute values" do + @k1_a.hash.eql?(@k1_c.hash).should be true + end + + it 'should not have hash value as same class with different attribute values' do + @k1_a.hash.eql?(@k1_b.hash).should be false + end + + it 'should not have hash value equal to different class with identical attribute values' do + @k1_a.hash.eql?(@k3_a.hash).should be false + end + + it 'should reject set addition if same instance is already a member' do + set = Set.new([@k1_a]) + set.add?(@k1_a).should be nil + end + + it 'should reject set addition if different instance, but attributes are the same' do + set = Set.new([@k1_a]) + set.add?(@k1_c).should be nil + end + + it 'should allow set addition if different instance and attribute values' do + set = Set.new([@k1_a]) + set.add?(@k1_b).should_not be nil + end + + it 'should allow set addition if different class' do + set = Set.new([@k1_a]) + set.add?(@k2_a).should_not be nil + end + + end + end end