hash_digest 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ODI5OGYwMmEyMTQ1ZjMyNmNiMjcyMmIyMTU3NTBmMWFmNjgzMDVmNA==
5
+ data.tar.gz: !binary |-
6
+ ZmVlMDRkN2MxOTY4MDRiMWJjNjMyOGNmMmUwMDdlMWQyYzAyM2VlZg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MjllMmZlNjc3Zjg4M2Q2YzA5OTFlOTA4MWUzYjA0MDQ5YzA3MjRhNmU4ZWZk
10
+ NWM0Yjg4MTM3NzdjNTYwMjNmMTA0ODFiMmJjMTY5N2MyZWI0MDE2MDdkYjVm
11
+ MTcwNmY1Y2Y5MmUyYzAxNjIzOWM2MzY5ZDQ3ODY1MzZhZTgyOGE=
12
+ data.tar.gz: !binary |-
13
+ MTk0MDNkMzk5ZTMzOTNkMGY5OTJhOWMzZDhjNGFhY2ZiY2VkYTNkZTc0OTE2
14
+ MWJiZTFiMzdmNTgwMGE1ZjVkNGQ1YWQ4MTdlZmRhOWNiMGYxYTM0ZTgzMjUw
15
+ YzlkZDNhYzFjNWFkMzIxMDQzNGIzNTFhNmMzNjVmMDg5ZDYwMzg=
data/CHANGELOG ADDED
@@ -0,0 +1,12 @@
1
+ 1.1.0 / 2014-01-01
2
+
3
+ * Breaking changes
4
+
5
+ * UTF-8 strings only because of EscapeUtils "limitations"
6
+
7
+ * Enhancements
8
+
9
+ * a changelog!
10
+ * replace CGI.escape with EscapeUtils.escape_url, which is 2x faster or so
11
+ * add HashDigest.digest2, which uses murmurhash3 instead of md5
12
+ * remove runtime dependency on activesupport
data/README.markdown CHANGED
@@ -1,4 +1,4 @@
1
- = HashHash
1
+ # HashDigest
2
2
 
3
3
  Generates MD5 digests of Hashes (and Arrays) indifferent to key type and ordering.
4
4
 
@@ -6,19 +6,19 @@ Useful for hashing rows in a 2-dimensional table.
6
6
 
7
7
  Used by the [remote_table](https://github.com/seamusabshere/remote_table) gem.
8
8
 
9
- == Example
9
+ ## Example
10
10
 
11
- === Indifferent to key type
11
+ ### Indifferent to key type
12
12
 
13
13
  HashDigest.hexdigest(:a => 1) #=> '3872c9ae3f427af0be0ead09d07ae2cf'
14
14
  HashDigest.hexdigest('a' => 1) #=> '3872c9ae3f427af0be0ead09d07ae2cf'
15
15
 
16
- === Indifferent to key order
16
+ ### Indifferent to key order
17
17
 
18
18
  HashDigest.hexdigest(:a => 1, 'b' => 2) == HashDigest.hexdigest('a' => 1, :b => 2) # true
19
19
  HashDigest.hexdigest(:a => 1, 'b' => 2) == HashDigest.hexdigest(:b => 2, 'a' => 1) # true
20
20
 
21
- == Algorithm
21
+ ## Algorithm
22
22
 
23
23
  Basically represent the hash as a URL querystring, ordered by key, and MD5 that.
24
24
 
@@ -29,12 +29,12 @@ Basically represent the hash as a URL querystring, ordered by key, and MD5 that.
29
29
 
30
30
  To digest an array, just pretend it's a hash with keys like 1, 2, 3, etc.
31
31
 
32
- == Potential issues
32
+ ## Potential issues
33
33
 
34
34
  * Uses MD5 (not cryptographically awesome)
35
35
  * Uses ActiveSupport's <tt>#to_query</tt> method to create a digestible string like "foo=bar&baz=bam" (slow)
36
36
  * Meant for flat hashes, e.g. { :a => 1, :b => 2 } and not { :x => { :y => :z } }
37
37
 
38
- == Copyright
38
+ ## Copyright
39
39
 
40
40
  Copyright 2011 Seamus Abshere
data/hash_digest.gemspec CHANGED
@@ -18,11 +18,11 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- # specify any dependencies here; for example:
22
- # s.add_development_dependency "rspec"
23
- # s.add_runtime_dependency "rest-client"
24
- s.add_runtime_dependency 'activesupport'
25
-
21
+ s.add_runtime_dependency 'murmurhash3'
22
+ s.add_runtime_dependency 'escape_utils'
23
+
26
24
  s.add_development_dependency 'minitest'
27
25
  s.add_development_dependency 'rake'
26
+ s.add_development_dependency 'activesupport'
27
+ s.add_development_dependency 'benchmark-ips'
28
28
  end
@@ -1,3 +1,3 @@
1
1
  module HashDigest
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
data/lib/hash_digest.rb CHANGED
@@ -1,27 +1,76 @@
1
1
  require 'digest/md5'
2
- require 'active_support'
3
- require 'active_support/version'
4
- %w{
5
- active_support/core_ext/hash/keys
6
- active_support/core_ext/object/to_query
7
- }.each do |active_support_3_requirement|
8
- require active_support_3_requirement
9
- end if ::ActiveSupport::VERSION::MAJOR >= 3
2
+ require 'murmurhash3'
3
+ require 'escape_utils'
10
4
 
11
5
  require "hash_digest/version"
12
6
 
13
7
  module HashDigest
14
- def self.hexdigest(hsh)
15
- ordered_list = if hsh.is_a?(::Hash)
16
- hsh = hsh.stringify_keys
17
- hsh.keys.sort.map { |k| hsh[k].to_query k }
18
- elsif hsh.is_a?(::Array)
8
+ # CURRENT
9
+
10
+ def self.as_digest2(obj)
11
+ obj.to_hash_digest_query
12
+ end
13
+
14
+ def self.digest2(obj)
15
+ ::MurmurHash3::V32.str_hash(as_digest2(obj)).to_s 36
16
+ end
17
+
18
+ # LEGACY
19
+
20
+ def self.as_digest1(obj)
21
+ ordered_list = case obj
22
+ when ::Hash
23
+ obj.map do |k, v|
24
+ obj[k].to_hash_digest_query k
25
+ end.sort
26
+ when ::Array
19
27
  ary = []
20
- hsh.each_with_index { |v, i| ary.push v.to_query(i.to_s) }
28
+ obj.each_with_index do |v, i|
29
+ ary << v.to_hash_digest_query(i)
30
+ end
21
31
  ary
22
32
  else
23
- raise ::ArgumentError, "[hash_digest gem] Can only digest Hashes and Arrays, not #{hsh.class}"
33
+ raise ::ArgumentError, "[hash_digest gem] Can only digest Hashes and Arrays, not #{obj.class}"
24
34
  end
25
- ::Digest::MD5.hexdigest ordered_list.join('&')
35
+ ordered_list.join '&'
36
+ end
37
+
38
+ def self.hexdigest(obj)
39
+ ::Digest::MD5.hexdigest as_digest1(obj)
40
+ end
41
+ end
42
+
43
+ # EVERYTHING BELOW IS COPIED FROM ACTIVESUPPORT 4.0.2
44
+
45
+ class Object
46
+ def to_hash_digest_param
47
+ to_s
48
+ end
49
+ end
50
+
51
+ class Array
52
+ def to_hash_digest_param
53
+ map { |e| e.to_hash_digest_param }.join '/'
54
+ end
55
+
56
+ def to_hash_digest_query(key)
57
+ prefix = "#{key}[]"
58
+ map { |value| value.to_hash_digest_query(prefix) }.join '&'
59
+ end
60
+ end
61
+
62
+ class Hash
63
+ def to_hash_digest_param(namespace = nil)
64
+ map do |key, value|
65
+ value.to_hash_digest_query(namespace ? "#{namespace}[#{key}]" : key)
66
+ end.sort.join '&'
67
+ end
68
+ alias_method :to_hash_digest_query, :to_hash_digest_param
69
+ end
70
+
71
+ class Object
72
+ def to_hash_digest_query(key)
73
+ # "#{::CGI.escape(key.to_hash_digest_param)}=#{::CGI.escape(to_hash_digest_param)}"
74
+ "#{::EscapeUtils.escape_url(key.to_hash_digest_param)}=#{::EscapeUtils.escape_url(to_hash_digest_param)}"
26
75
  end
27
76
  end
data/test/helper.rb CHANGED
@@ -1,10 +1,31 @@
1
1
  require 'bundler/setup'
2
- require 'minitest/spec'
3
2
  require 'minitest/autorun'
4
3
 
5
4
  $LOAD_PATH.unshift(File.dirname(__FILE__))
6
5
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
6
  require 'hash_digest'
8
7
 
9
- class MiniTest::Unit::TestCase
8
+ require 'benchmark/ips'
9
+ require 'active_support/core_ext'
10
+
11
+ module TestHelper
12
+ def as_digest1(hsh)
13
+ ordered_list = if hsh.is_a?(::Hash)
14
+ hsh = hsh.stringify_keys
15
+ hsh.keys.sort.map { |k| hsh[k].to_query k }
16
+ elsif hsh.is_a?(::Array)
17
+ ary = []
18
+ hsh.each_with_index { |v, i| ary.push v.to_query(i.to_s) }
19
+ ary
20
+ else
21
+ raise ::ArgumentError, "[hash_digest gem] Can only digest Hashes and Arrays, not #{hsh.class}"
22
+ end
23
+ ordered_list.join('&')
24
+ end
25
+
26
+ def assert_same_as_old(a)
27
+ expected = as_digest1(a)
28
+ got = HashDigest.as_digest1(a)
29
+ got.must_equal expected
30
+ end
10
31
  end
@@ -1,24 +1,82 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  require 'helper'
3
+ require 'stringio'
3
4
 
4
5
  describe HashDigest do
5
- it 'generates a hash of a hash' do
6
- HashDigest.hexdigest(:a => 1).must_equal '3872c9ae3f427af0be0ead09d07ae2cf'
7
- end
8
-
9
- it 'is indifferent to key type' do
10
- HashDigest.hexdigest(:a => 1, 'b' => 2).must_equal(HashDigest.hexdigest('a' => 1, :b => 2))
6
+ include TestHelper
7
+
8
+ describe '.digest2' do
9
+ it "is much faster than .hexdigest" do
10
+ hsh = { 'a' => 1, :b => { foo: :bar, zoo: 'animal' }, ";b\n`&" => { foo: :bar, zoo: "==&ani!!.;&mal\n" } }
11
+ begin
12
+ old_stdout = $stdout
13
+ $stdout = StringIO.new
14
+ Benchmark.ips do |x|
15
+ x.report("HashDigest.hexdigest") { HashDigest.hexdigest(hsh) }
16
+ x.report("HashDigest.digest2") { HashDigest.digest2(hsh) }
17
+ end
18
+ $stdout.rewind
19
+ result = $stdout.read
20
+ ensure
21
+ $stdout = old_stdout
22
+ end
23
+ result.must_equal ''
24
+ end
11
25
  end
12
-
13
- it 'is indifferent to key order' do
14
- HashDigest.hexdigest(:a => 1, 'b' => 2).must_equal(HashDigest.hexdigest(:b => 2, 'a' => 1))
26
+
27
+ describe 'backwards compatibility' do
28
+ it 'for hashes' do
29
+ HashDigest.hexdigest(:a => 1).must_equal '3872c9ae3f427af0be0ead09d07ae2cf'
30
+ end
31
+
32
+ it 'for arrays' do
33
+ HashDigest.hexdigest([:a, 1]).must_equal '8ce19b95077ec34a4fd06b089f368678'
34
+ end
35
+
36
+ [
37
+ { :a => 1 },
38
+ { 'b' => 99.9 },
39
+ { 'b`&' => 99.9 },
40
+ { ';b`&' => '99.9!!.;&' },
41
+ { ";b\n`&" => "99.9!\n!.;&" },
42
+ { :b => { foo: :bar, zoo: 'animal' } },
43
+ { :b => { foo: :bar, zoo: '==&ani!!.;&mal' } },
44
+ { :b => { foo: :bar, zoo: "==&ani!!.;&mal\n" } },
45
+ { :a => 1, 'b' => 2 },
46
+ { 'a' => 1, :b => 2 },
47
+ { 'a' => 1, :b => [1, 2] },
48
+ { 'a' => 1, :b => { foo: :bar, zoo: 'animal' } },
49
+ ].each do |hsh|
50
+ it "treats #{hsh} same as old" do
51
+ assert_same_as_old hsh
52
+ end
53
+ end
54
+
15
55
  end
16
-
17
- it 'just as a bonus, works on arrays' do
18
- HashDigest.hexdigest([:a, 1]).must_equal '8ce19b95077ec34a4fd06b089f368678'
56
+
57
+ describe 'indifference to' do
58
+ it 'key type' do
59
+ HashDigest.as_digest2(:a => 1, 'b' => 2).must_equal(HashDigest.as_digest2('a' => 1, :b => 2))
60
+ end
61
+
62
+ it 'key order' do
63
+ HashDigest.as_digest2(:a => 1, 'b' => 2).must_equal(HashDigest.as_digest2(:b => 2, 'a' => 1))
64
+ end
65
+
66
+ [
67
+ [{:a => 1, 'b' => 2}, {'a' => 1, :b => 2}],
68
+ [{:a => 1, 'b' => [1, 2]}, {'a' => 1, :b => ['1', 2]}],
69
+ [{:a => 1, 'b' => { foo: :bar }}, {'a' => 1, :b => { 'foo' => 'bar' }}],
70
+ ].each do |a, b|
71
+ it "trivial difference between #{a} and #{b}" do
72
+ assert_same_as_old a
73
+ assert_same_as_old b
74
+ HashDigest.as_digest2(b).must_equal(HashDigest.as_digest2(b))
75
+ end
76
+ end
19
77
  end
20
78
 
21
- it "raises an exception if you try to digest something it doesn't handle" do
79
+ it "raises an exception if you try to digest anything other than a Hash or Array" do
22
80
  lambda { HashDigest.hexdigest('foobar') }.must_raise(::ArgumentError)
23
81
  end
24
82
  end
metadata CHANGED
@@ -1,49 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hash_digest
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
5
- prerelease:
4
+ version: 1.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Seamus Abshere
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2011-09-23 00:00:00.000000000Z
11
+ date: 2014-01-01 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: activesupport
16
- requirement: &2154254960 !ruby/object:Gem::Requirement
17
- none: false
14
+ name: murmurhash3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: escape_utils
29
+ requirement: !ruby/object:Gem::Requirement
18
30
  requirements:
19
31
  - - ! '>='
20
32
  - !ruby/object:Gem::Version
21
33
  version: '0'
22
34
  type: :runtime
23
35
  prerelease: false
24
- version_requirements: *2154254960
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
25
41
  - !ruby/object:Gem::Dependency
26
42
  name: minitest
27
- requirement: &2154254540 !ruby/object:Gem::Requirement
28
- none: false
43
+ requirement: !ruby/object:Gem::Requirement
29
44
  requirements:
30
45
  - - ! '>='
31
46
  - !ruby/object:Gem::Version
32
47
  version: '0'
33
48
  type: :development
34
49
  prerelease: false
35
- version_requirements: *2154254540
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
36
55
  - !ruby/object:Gem::Dependency
37
56
  name: rake
38
- requirement: &2154254120 !ruby/object:Gem::Requirement
39
- none: false
57
+ requirement: !ruby/object:Gem::Requirement
40
58
  requirements:
41
59
  - - ! '>='
42
60
  - !ruby/object:Gem::Version
43
61
  version: '0'
44
62
  type: :development
45
63
  prerelease: false
46
- version_requirements: *2154254120
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: benchmark-ips
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
47
97
  description: Make consistent hashcodes from flat Hashes, regardless of key ordering.
48
98
  Useful for hashing rows in a table.
49
99
  email:
@@ -53,6 +103,7 @@ extensions: []
53
103
  extra_rdoc_files: []
54
104
  files:
55
105
  - .gitignore
106
+ - CHANGELOG
56
107
  - Gemfile
57
108
  - README.markdown
58
109
  - Rakefile
@@ -63,28 +114,28 @@ files:
63
114
  - test/test_hash_digest.rb
64
115
  homepage: https://github.com/seamusabshere/hash_digest
65
116
  licenses: []
117
+ metadata: {}
66
118
  post_install_message:
67
119
  rdoc_options: []
68
120
  require_paths:
69
121
  - lib
70
122
  required_ruby_version: !ruby/object:Gem::Requirement
71
- none: false
72
123
  requirements:
73
124
  - - ! '>='
74
125
  - !ruby/object:Gem::Version
75
126
  version: '0'
76
127
  required_rubygems_version: !ruby/object:Gem::Requirement
77
- none: false
78
128
  requirements:
79
129
  - - ! '>='
80
130
  - !ruby/object:Gem::Version
81
131
  version: '0'
82
132
  requirements: []
83
133
  rubyforge_project: hash_digest
84
- rubygems_version: 1.8.6
134
+ rubygems_version: 2.1.11
85
135
  signing_key:
86
- specification_version: 3
136
+ specification_version: 4
87
137
  summary: Make consistent hashcodes from flat Hashes, regardless of key ordering
88
138
  test_files:
89
139
  - test/helper.rb
90
140
  - test/test_hash_digest.rb
141
+ has_rdoc: