From d6fb95b06f960e4cd11cf0aeadfd1a0c52cba060 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 23 Jul 2012 12:13:08 -0500 Subject: [PATCH] Add nested md5 digest --- lib/linguist/md5.rb | 50 ++++++++++++++++++++++++++++++++++++++ test/test_md5.rb | 59 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 lib/linguist/md5.rb create mode 100644 test/test_md5.rb diff --git a/lib/linguist/md5.rb b/lib/linguist/md5.rb new file mode 100644 index 00000000..4860bcda --- /dev/null +++ b/lib/linguist/md5.rb @@ -0,0 +1,50 @@ +require 'digest/md5' + +module Linguist + module MD5 + # Public: Create deep nested digest of value object. + # + # Useful for object comparsion. + # + # obj - Object to digest. + # + # Returns String hex digest + def self.hexdigest(obj) + digest = Digest::MD5.new + digest_strings(obj).each { |e| digest.update(e) } + digest.hexdigest + end + + # Internal: Get String representations for digest. + # + # obj - Object to digest + # + # Returns an Array of Strings. + def self.digest_strings(obj) + case obj + when String + ["#{obj.class}", "#{obj}"] + when Symbol + ["#{obj.class}", "#{obj}"] + when Integer + ["#{obj.class}", "#{obj}"] + when TrueClass, FalseClass, NilClass + ["#{obj.class}"] + when Array + r = ["#{obj.class}"] + obj.each do |e| + r.concat(digest_strings(e)) + end + r + when Hash + r = ["#{obj.class}"] + obj.map { |k, v| digest_strings([k, v]) }.sort.each do |e| + r.concat(digest_strings(e)) + end + r + else + raise TypeError, "can't convert #{obj.inspect} into String" + end + end + end +end diff --git a/test/test_md5.rb b/test/test_md5.rb new file mode 100644 index 00000000..789e8b36 --- /dev/null +++ b/test/test_md5.rb @@ -0,0 +1,59 @@ +require 'linguist/md5' + +require 'test/unit' + +class TestMD5 < Test::Unit::TestCase + include Linguist + + def test_hexdigest_string + assert_equal "af0ede4f95acad6d331a813dc3904c11", MD5.hexdigest("foo") + assert_equal "05427b9908edb6995ed76faa23e4471f", MD5.hexdigest("bar") + end + + def test_hexdigest_symbol + assert_equal "450c1ae043459546517b3dd2f98250f0", MD5.hexdigest(:foo) + assert_equal "f06967526af9d7a512594b0a81b31ede", MD5.hexdigest(:bar) + + assert_not_equal MD5.hexdigest("foo"), MD5.hexdigest(:foo) + end + + def test_hexdigest_integer + assert_equal "7605ec17fd7fd213fdcd23cac302cbb4", MD5.hexdigest(1) + assert_equal "097c311a46d330e4e119ba2b1dc0f9a5", MD5.hexdigest(2) + + assert_not_equal MD5.hexdigest("1"), MD5.hexdigest(1) + end + + def test_hexdigest_boolean + assert_equal "a690a0615820e2e5c53901d8b8958509", MD5.hexdigest(true) + assert_equal "fca6a9b459e702fa93513c6a8b8c5dfe", MD5.hexdigest(false) + + assert_not_equal MD5.hexdigest("true"), MD5.hexdigest(true) + assert_not_equal MD5.hexdigest("false"), MD5.hexdigest(false) + end + + def test_hexdigest_nil + assert_equal "35589a1cc0b3ca90fc52d0e711c0c434", MD5.hexdigest(nil) + + assert_not_equal MD5.hexdigest("nil"), MD5.hexdigest(nil) + end + + def test_hexdigest_array + assert_equal "4410ec34d9e6c1a68100ca0ce033fb17", MD5.hexdigest([]) + assert_equal "a57e31bc7bce57d04dd6a07e74fd0d88", MD5.hexdigest([1]) + assert_equal "ecbda6354c6e0370df33d43ce14701f4", MD5.hexdigest([1, 2]) + assert_equal "5c42a2601d344f359017e2b76390e2cc", MD5.hexdigest([1, 2, 3]) + assert_equal "c1816755a379d2b32289647ad1870d12", MD5.hexdigest([1, 2, [3]]) + end + + def test_hexdigest_hash + assert_equal "fae8a9257e154175da4193dbf6552ef6", MD5.hexdigest({}) + assert_equal "b3646d6d84a803baa5ee2e5354057a65", MD5.hexdigest({:a => 1}) + assert_equal "92c08085470d42ca0d33dd3ec5a8e098", MD5.hexdigest({:b => 2}) + + assert_not_equal MD5.hexdigest([:b, 2]), MD5.hexdigest({:b => 2}) + + assert_equal MD5.hexdigest({:b => 2, :a => 1}), MD5.hexdigest({:a => 1, :b => 2}) + assert_equal MD5.hexdigest({:c => 3, :b => 2, :a => 1}), MD5.hexdigest({:a => 1, :b => 2, :c => 3}) + end +end