diff --git a/lib/nanoc/base/checksummer.rb b/lib/nanoc/base/checksummer.rb index 6938e8cc2e..df270160ae 100644 --- a/lib/nanoc/base/checksummer.rb +++ b/lib/nanoc/base/checksummer.rb @@ -18,23 +18,28 @@ def calc(obj) private - def update(obj, digest) + def update(obj, digest, visited = Set.new) digest.update(obj.class.to_s) + if visited.include?(obj) + digest.update('recur') + return + end + case obj when String digest.update(obj) when Array obj.each do |el| digest.update('elem') - update(el, digest) + update(el, digest, visited + [obj]) end when Hash obj.each do |key, value| digest.update('key') - update(key, digest) + update(key, digest, visited + [obj]) digest.update('value') - update(value, digest) + update(value, digest, visited + [obj]) end when Pathname filename = obj.to_s @@ -59,7 +64,7 @@ def update(obj, digest) digest.update('attributes') attributes = obj.attributes.dup attributes.delete(:file) - update(attributes, digest) + update(attributes, digest, visited + [obj]) else data = begin Marshal.dump(obj) diff --git a/test/base/checksummer_spec.rb b/test/base/checksummer_spec.rb index a32baff42b..13708bb2f2 100644 --- a/test/base/checksummer_spec.rb +++ b/test/base/checksummer_spec.rb @@ -25,6 +25,12 @@ it 'should checksum non-serializable arrays' do subject.calc([-> {}]).must_match(CHECKSUM_REGEX) end + + it 'should checksum recursive arrays' do + array = [:a] + array << array + subject.calc(array).must_equal('mR3c98xA5ecazxx+M3h0Ss4/J20=') + end end describe 'for Hash' do @@ -39,6 +45,18 @@ it 'should checksum non-serializable hashes' do subject.calc({ a: -> {} }).must_match(CHECKSUM_REGEX) end + + it 'should checksum recursive hash keys' do + hash = {} + hash[hash] = 123 + subject.calc(hash).must_equal('mKucWMhRtR/FHWNqR/EErF4qgTk=') + end + + it 'should checksum recursive hash values' do + hash = {} + hash[123] = hash + subject.calc(hash).must_equal('PBiDX0nWnV+DAYB+w+xM0Kf21ZM=') + end end describe 'for Pathname' do @@ -195,6 +213,14 @@ subject.calc(item).must_equal(normal_checksum) end + describe 'with recursive attributes' do + it 'should checksum' do + item.attributes[:a] = item + subject.calc(item).must_match(CHECKSUM_REGEX) + subject.calc(item).wont_equal(normal_checksum) + end + end + describe 'with changed attributes' do let(:attributes) { { x: 4, y: 5 } }