crimp 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +3 -1
- data/LICENSE.txt +17 -18
- data/README.md +157 -15
- data/Rakefile +6 -2
- data/crimp.gemspec +13 -24
- data/lib/crimp.rb +41 -63
- data/spec/acceptance_data.yml +66 -0
- data/spec/acceptance_spec.rb +23 -0
- data/spec/crimp_spec.rb +295 -95
- data/spec/spec_helper.rb +0 -1
- metadata +14 -99
- data/Guardfile +0 -6
- data/lib/crimp/version.rb +0 -3
- data/spec/consistency_spec.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c137825335bbd1636911bede1f6acba9dc001070c68cce11aa26d7bb1e79f120
|
4
|
+
data.tar.gz: 610febce9965c5c7f2efe2aea310613f7e854b9de8de3ae1263acf525f12f78a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87a85aedb707739ea28220183e96dbbfb51ed84443314a5cb2459c36f9ea0dd2d1d4e1ddd6a61e6ec43730414d73a021961e3861f1aca4c9e13fb7d8025ed6d3
|
7
|
+
data.tar.gz: 64edcb40a960ad815a0b4c2006d89e091f27b4a1102ad622be001dcae0b324942493d6737bd7b7205945253710c94e1e977dc6699e9ef354cbd44046c15ae969
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.5.1
|
data/.travis.yml
CHANGED
data/LICENSE.txt
CHANGED
@@ -1,22 +1,21 @@
|
|
1
|
-
|
1
|
+
The MIT License
|
2
2
|
|
3
|
-
|
3
|
+
Copyright (c) 2014-2018 BBC News https://www.bbc.co.uk/news
|
4
4
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
the following conditions:
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
12
11
|
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
15
14
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
OF
|
22
|
-
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
@@ -3,45 +3,187 @@
|
|
3
3
|
[](https://travis-ci.org/BBC-News/crimp)
|
4
4
|
[](http://badge.fury.io/rb/crimp)
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-

|
9
|
-
|
10
|
-
Shamelessly copied from [this Stack Overflow
|
11
|
-
answer](http://stackoverflow.com/a/6462589/3243663).
|
6
|
+
Creates an MD5 hash from simple data structures made of numbers, strings, booleans, nil, arrays or hashes.
|
12
7
|
|
13
8
|
## Installation
|
14
9
|
|
15
10
|
Add this line to your application's Gemfile:
|
16
11
|
|
17
|
-
|
12
|
+
```ruby
|
13
|
+
gem 'crimp'
|
14
|
+
|
15
|
+
```
|
18
16
|
|
19
17
|
And then execute:
|
20
18
|
|
21
|
-
|
19
|
+
```shell
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
```
|
22
23
|
|
23
24
|
Or install it yourself as:
|
24
25
|
|
25
|
-
|
26
|
+
```shell
|
27
|
+
$ gem install crimp
|
28
|
+
```
|
26
29
|
|
27
30
|
## Usage
|
28
31
|
|
29
|
-
```
|
32
|
+
```ruby
|
30
33
|
require 'crimp'
|
31
34
|
|
32
|
-
Crimp.
|
35
|
+
Crimp.signature({ a: { b: 1 } })
|
36
|
+
=> "ac13c15d07e5fa3992fc6b15113db900"
|
37
|
+
```
|
38
|
+
|
39
|
+
## Multiplatform design
|
40
|
+
|
41
|
+
At the BBC we use Crimp to build keys for database and cache entries.
|
42
|
+
|
43
|
+
If you want to build a similar library with your language of choice you should be able to follow the simple specifications defined in `spec/crimp_spec.rb`. Using these simple rules you will produce a string ready to be MD5 signed.
|
44
|
+
|
45
|
+
Once you get your string, is very important to be sure that you can produce the same key in any language. MD5 is your friend:
|
46
|
+
|
47
|
+
### Ruby
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
irb(main):001:0> require 'digest'
|
51
|
+
=> true
|
52
|
+
irb(main):002:0> Digest::MD5.hexdigest('abc')
|
53
|
+
=> "900150983cd24fb0d6963f7d28e17f72"
|
54
|
+
```
|
55
|
+
|
56
|
+
### Lua
|
57
|
+
|
58
|
+
```lua
|
59
|
+
Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio
|
60
|
+
> md5 = require 'md5'
|
61
|
+
> md5.sumhexa('abc')
|
62
|
+
900150983cd24fb0d6963f7d28e17f72
|
63
|
+
```
|
33
64
|
|
34
|
-
|
65
|
+
### Elixir
|
35
66
|
|
36
|
-
|
67
|
+
``` elixir
|
68
|
+
Erlang/OTP 21 [erts-10.0.4] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]
|
69
|
+
Interactive Elixir (1.7.2) - press Ctrl+C to exit (type h() ENTER for help)
|
70
|
+
iex(1)> :crypto.hash(:md5 , "abc") |> Base.encode16() |> String.downcase
|
71
|
+
"900150983cd24fb0d6963f7d28e17f72"
|
72
|
+
```
|
37
73
|
|
38
|
-
|
74
|
+
### Node.js
|
39
75
|
|
76
|
+
``` javascript
|
77
|
+
> var crypto = require('crypto');
|
78
|
+
undefined
|
79
|
+
> crypto.createHash('md5').update('abc').digest('hex');
|
80
|
+
'900150983cd24fb0d6963f7d28e17f72'
|
40
81
|
```
|
41
82
|
|
83
|
+
## Fine prints
|
84
|
+
|
85
|
+
### Symbols
|
86
|
+
|
87
|
+
To make Crimp signatures reproducible in any platform we decided to ignore Ruby symbols and treat them as strings, so:
|
88
|
+
|
89
|
+
``` ruby
|
90
|
+
Crimp.signature(:a) == Crimp.Signature('a')
|
91
|
+
```
|
92
|
+
|
93
|
+
### Sets
|
94
|
+
|
95
|
+
Also Sets get transformed to Arrays:
|
96
|
+
|
97
|
+
``` ruby
|
98
|
+
Crimp.signature(Set.new(['a', 'b'])) == Crimp.signature(['a', 'b'])
|
99
|
+
```
|
100
|
+
|
101
|
+
### Sorting of collections
|
102
|
+
|
103
|
+
Crimp signatures are generated against sorted collections.
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
Crimp.signature([1, 2]) == Crimp.signature([2, 1])
|
107
|
+
Crimp.signature({'b' => 2, 'a' => 1}) == Crimp.signature({'a' => 1, 'b' => 2})
|
108
|
+
```
|
109
|
+
|
110
|
+
Crimp also sorts nested collections.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
Crimp.signature([1, [3, 2], 4]) == Crimp.signature([4, [2, 3], 1])
|
114
|
+
Crimp.signature({'b' => {'d' => 2,'c' => 1}, 'a' => [3, 1, 2]}) == Crimp.signature({'a' => [1, 2, 3], 'b' => { 'c' => 1, 'd' => 2 }})
|
115
|
+
```
|
116
|
+
|
117
|
+
### Custom objects
|
118
|
+
|
119
|
+
Crimp will complain if you try to get a signature from an instance of some custom object:
|
120
|
+
|
121
|
+
``` ruby
|
122
|
+
Crimp.signature(Object.new)
|
123
|
+
=> TypeError: Expected a (String|Number|Boolean|Nil|Hash|Array), Got Object
|
124
|
+
```
|
125
|
+
It is your responsibility to pass a compatible representation of your object to Crimp.
|
126
|
+
|
127
|
+
## Implementation details
|
128
|
+
|
129
|
+
Under the hood Crimp annotates the passed data structure to a nested array of primitives (strings, numbers, booleans, nils, etc.) and a single byte to indicate the type of the primitive:
|
130
|
+
|
131
|
+
| Type | Byte |
|
132
|
+
| :-: | :-: |
|
133
|
+
| String | `S` |
|
134
|
+
| Number | `N` |
|
135
|
+
| Boolean | `B` |
|
136
|
+
| nil | `_` |
|
137
|
+
| Array | `A` |
|
138
|
+
| Hash | `H` |
|
139
|
+
|
140
|
+
You can verify it using the `#annotate` method:
|
141
|
+
|
142
|
+
``` ruby
|
143
|
+
Crimp.annotate({ a: 1 })
|
144
|
+
=> [[[[[1, "N"], ["a", "S"]], "A"]], "H"]
|
145
|
+
```
|
146
|
+
Notice how Crimp marks the collection as Hash (`H`) and then transforms the tuple of key/values to an Array (`A`).
|
147
|
+
|
148
|
+
Here's an example with nested hashes:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
Crimp.annotate({ a: { b: 'c' } })
|
152
|
+
=> [[[[["a", "S"], [[[[["b", "S"], ["c", "S"]], "A"]], "H"]], "A"]], "H"]
|
153
|
+
```
|
154
|
+
|
155
|
+
Before signing Crimp transforms the collection of nested array to a string.
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
Crimp.notation({ a: { b: 'c' } })
|
159
|
+
=> "aSbScSAHAH"
|
160
|
+
```
|
161
|
+
|
162
|
+
Please note the Arrays and Hash keys are sorted before signing.
|
163
|
+
|
164
|
+
``` ruby
|
165
|
+
Crimp.notation([3, 1, 2])
|
166
|
+
=> "1N2N3NA"
|
167
|
+
```
|
168
|
+
|
169
|
+
key/value tuples get sorted as well.
|
170
|
+
|
171
|
+
``` ruby
|
172
|
+
Crimp.notation({ a: 1 })
|
173
|
+
=> "1NaSAH"
|
174
|
+
```
|
175
|
+
|
176
|
+
## Changelog
|
177
|
+
|
178
|
+
| Version | Changes |
|
179
|
+
|---------|----------------------------------------------------------------------------|
|
180
|
+
|`v0.x` | Original version of Crimp. |
|
181
|
+
|`v0.2.0` | Crimp compatibility with Ruby >= 2.4, use this for legacy projects. |
|
182
|
+
|`v1.0.0` | Includes **breaking changes** and returns different signatures from `v0.2` |
|
183
|
+
|
42
184
|
## Contributing
|
43
185
|
|
44
|
-
1. Fork it ( http://github.com
|
186
|
+
1. Fork it ( http://github.com/BBC-News/crimp/fork )
|
45
187
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
46
188
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
47
189
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
|
2
2
|
|
3
|
+
require 'rspec/core/rake_task'
|
3
4
|
require 'bundler/gem_tasks'
|
4
|
-
require 'rake/rspec'
|
5
5
|
|
6
|
-
|
6
|
+
RSpec::Core::RakeTask.new :specs do |task|
|
7
|
+
task.pattern = Dir['spec/**/*_spec.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
task default: [:specs]
|
data/crimp.gemspec
CHANGED
@@ -1,35 +1,24 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'crimp/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
-
spec.version =
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
|
14
|
-
|
15
|
-
All credit should go to http://stackoverflow.com/users/394282/luke
|
16
|
-
EOS
|
17
|
-
spec.homepage = ""
|
18
|
-
spec.license = "MIT"
|
6
|
+
spec.name = 'crimp'
|
7
|
+
spec.version = '1.0.0'
|
8
|
+
spec.authors = ['BBC News']
|
9
|
+
spec.email = ['D&ENewsFrameworksTeam@bbc.co.uk']
|
10
|
+
spec.summary = 'Creates an MD5 hash from simple data structures.'
|
11
|
+
spec.description = 'Platform agnostic MD5 hash from simple data structures made of numbers, strings, booleans, nil, arrays or hashes.'
|
12
|
+
spec.homepage = 'https://www.bbc.co.uk/news'
|
13
|
+
spec.license = 'MIT'
|
19
14
|
|
20
15
|
spec.files = `git ls-files -z`.split("\x0")
|
21
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
-
spec.require_paths = [
|
18
|
+
spec.require_paths = ['lib']
|
24
19
|
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.
|
29
|
-
spec.add_development_dependency "rspec-nc"
|
30
|
-
spec.add_development_dependency "guard"
|
31
|
-
spec.add_development_dependency "guard-rspec"
|
32
|
-
spec.add_development_dependency "pry"
|
33
|
-
spec.add_development_dependency "pry-remote"
|
34
|
-
spec.add_development_dependency "pry-nav"
|
20
|
+
spec.add_development_dependency 'bundler'
|
21
|
+
spec.add_development_dependency 'rake'
|
22
|
+
spec.add_development_dependency 'rspec'
|
23
|
+
spec.add_dependency 'deepsort'
|
35
24
|
end
|
data/lib/crimp.rb
CHANGED
@@ -1,74 +1,52 @@
|
|
1
|
-
|
2
|
-
require 'digest'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
self >= 4611686018427387904
|
8
|
-
end
|
9
|
-
end
|
3
|
+
require 'digest/md5'
|
4
|
+
require 'set'
|
5
|
+
require 'deepsort'
|
10
6
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def self.stringify(obj)
|
17
|
-
convert(obj).tap { |o| return o.class == String ? o : to_string(o) }
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def self.convert(obj)
|
23
|
-
case obj
|
24
|
-
when Array
|
25
|
-
parse_array obj
|
26
|
-
when Hash
|
27
|
-
parse_hash obj
|
28
|
-
when String
|
29
|
-
obj
|
30
|
-
else
|
31
|
-
to_string obj
|
7
|
+
class Crimp
|
8
|
+
class << self
|
9
|
+
def signature(obj)
|
10
|
+
Digest::MD5.hexdigest(notation(obj))
|
32
11
|
end
|
33
|
-
end
|
34
12
|
|
35
|
-
|
36
|
-
|
37
|
-
hash.each { |k, v| a << pair_to_string(k, v) }
|
13
|
+
def notation(obj)
|
14
|
+
annotate(obj).flatten.join
|
38
15
|
end
|
39
|
-
end
|
40
16
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
17
|
+
def annotate(obj)
|
18
|
+
obj = coerce(obj)
|
19
|
+
|
20
|
+
case obj
|
21
|
+
when String
|
22
|
+
[obj, 'S']
|
23
|
+
when Numeric
|
24
|
+
[obj, 'N']
|
25
|
+
when TrueClass, FalseClass
|
26
|
+
[obj, 'B']
|
27
|
+
when NilClass
|
28
|
+
[nil, '_']
|
29
|
+
when Array
|
30
|
+
[sort(obj), 'A']
|
31
|
+
when Hash
|
32
|
+
[sort(obj), 'H']
|
33
|
+
else
|
34
|
+
raise TypeError, "Expected a (String|Number|Boolean|Nil|Hash|Array), Got #{obj.class}."
|
35
|
+
end
|
36
|
+
end
|
48
37
|
|
49
|
-
|
50
|
-
stringify hash_to_array(hash)
|
51
|
-
end
|
38
|
+
private
|
52
39
|
|
53
|
-
|
54
|
-
|
55
|
-
|
40
|
+
def sort(coll)
|
41
|
+
coll.deep_sort_by { |obj| obj.to_s }.map { |obj| annotate(obj) }
|
42
|
+
end
|
56
43
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
# Say you have a huge number of stored keys and you migrate your app from 2.1 to >= 2.4
|
65
|
-
# this would cause a change of the signature for a subset of the keys which would be hard
|
66
|
-
# to debug especially for nested data structures.
|
67
|
-
#
|
68
|
-
def self.legacy_class(obj)
|
69
|
-
return obj.class unless obj.is_a?(Numeric)
|
70
|
-
return 'Float' if obj.is_a?(Float)
|
71
|
-
return 'Bignum' if obj.bignum?
|
72
|
-
'Fixnum'
|
44
|
+
def coerce(obj)
|
45
|
+
case obj
|
46
|
+
when Symbol then obj.to_s
|
47
|
+
when Set then obj.to_a
|
48
|
+
else obj
|
49
|
+
end
|
50
|
+
end
|
73
51
|
end
|
74
52
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
---
|
2
|
+
-
|
3
|
+
desc: verify String handling
|
4
|
+
json: abc
|
5
|
+
string: abcS
|
6
|
+
signature: c4449120506d97975c67be69719a78e2
|
7
|
+
-
|
8
|
+
desc: verify integers handling
|
9
|
+
json: 1
|
10
|
+
string: 1N
|
11
|
+
signature: 594170053719896a11eb08ee513813d5
|
12
|
+
-
|
13
|
+
desc: verify floats handling
|
14
|
+
json: 1.2
|
15
|
+
string: 1.2N
|
16
|
+
signature: f1ab6592886cd4b1b66ed55e73d9ab81
|
17
|
+
-
|
18
|
+
desc: verify Array handling
|
19
|
+
json: [1, "a", 3]
|
20
|
+
string: 1N3NaSA
|
21
|
+
signature: cd1c43797d488d0f6c0d71537c64d30b
|
22
|
+
-
|
23
|
+
desc: verify Array sorting
|
24
|
+
json: [3, null, 1, "1"]
|
25
|
+
string: _1N1S3NA
|
26
|
+
signature: 518e7bb17674f6acbb296845862a152d
|
27
|
+
-
|
28
|
+
desc: verify Array sorting with capital letters
|
29
|
+
json: ["a", "A", "b", "B"]
|
30
|
+
string: ASBSaSbSA
|
31
|
+
signature: f6692ab4bc94b35e61ec15c2d1891734
|
32
|
+
-
|
33
|
+
desc: verify nested Arrays
|
34
|
+
json: ["a", 1, ["b", "2"]]
|
35
|
+
string: 1N2SbSAaSA
|
36
|
+
signature: 3aaa58da4841eaeb41d3726d2c6fd875
|
37
|
+
-
|
38
|
+
desc: verify nested Arrays
|
39
|
+
json: [["b", "2"], "a", 1]
|
40
|
+
string: 1N2SbSAaSA
|
41
|
+
signature: 3aaa58da4841eaeb41d3726d2c6fd875
|
42
|
+
-
|
43
|
+
desc: verify hash like data structures
|
44
|
+
json: {"a": 1}
|
45
|
+
string: 1NaSAH
|
46
|
+
signature: 8cb44d69badda0f34b0bab6bb3e7fdbf
|
47
|
+
-
|
48
|
+
desc: verify nested hash
|
49
|
+
json: {"a": {"c": null, "2": 2 }}
|
50
|
+
string: aS2S2NA_cSAHAH
|
51
|
+
signature: bff3538075e4007c7679a7ba0d0a5f30
|
52
|
+
-
|
53
|
+
desc: verify null values
|
54
|
+
json: null
|
55
|
+
string: _
|
56
|
+
signature: b14a7b8059d9c055954c92674ce60032
|
57
|
+
-
|
58
|
+
desc: verify true boolean values
|
59
|
+
json: true
|
60
|
+
string: trueB
|
61
|
+
signature: 6413cfeb7a89f7e0a8872f82b919c0d9
|
62
|
+
-
|
63
|
+
desc: verify false boolean values
|
64
|
+
json: false
|
65
|
+
string: falseB
|
66
|
+
signature: fa39253035cfe44c8638b8f5d7a3402e
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'yaml'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
# other libraries could fetch the test data from Github.
|
6
|
+
file = File.join(__dir__, 'acceptance_data.yml')
|
7
|
+
tests = YAML::load_file(file)
|
8
|
+
|
9
|
+
describe 'Multiplatform compatibility acceptance tests' do
|
10
|
+
tests.each do |test|
|
11
|
+
input = JSON.parse(test['json'].to_json, quirks_mode: true)
|
12
|
+
signature = test['signature']
|
13
|
+
string = test['string']
|
14
|
+
|
15
|
+
specify "#{test['desc']} string" do
|
16
|
+
expect(Crimp.notation(input)).to eq(string)
|
17
|
+
end
|
18
|
+
|
19
|
+
specify "#{test['desc']} signature" do
|
20
|
+
expect(Crimp.signature(input)).to eq(signature)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/spec/crimp_spec.rb
CHANGED
@@ -1,99 +1,299 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
|
-
describe
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
5
|
+
describe '.signature' do
|
6
|
+
it 'will return an md5 hash' do
|
7
|
+
expect(Crimp.signature('a')).to eq 'd132c0567a5964930f9ee5f14e779e32'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.notation' do
|
12
|
+
it 'returns a string representation of the passed data' do
|
13
|
+
expect(Crimp.notation([123, 'abc'])).to eq('123NabcSA')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '.annotate' do
|
18
|
+
it 'returns an array of tuples representing the value and the type' do
|
19
|
+
expect(Crimp.annotate([123, 'abc'])).to eq([[[123, 'N'], ['abc', 'S']], 'A'])
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns a tuple [val, 'N'] for numeric primitives" do
|
23
|
+
expect(Crimp.annotate(123)).to eq([123, 'N'])
|
24
|
+
end
|
25
|
+
|
26
|
+
it "returns a tuple [val, 'S'] for string primitives" do
|
27
|
+
expect(Crimp.annotate('abc')).to eq(['abc', 'S'])
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns a tuple [[], 'A'] for empty arrays" do
|
31
|
+
expect(Crimp.annotate([])).to eq([[], 'A'])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'Strings' do
|
36
|
+
it 'handles strings' do
|
37
|
+
expect(Crimp.annotate('a')).to eq(['a', 'S'])
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'handles capitalised strings with no modifications' do
|
41
|
+
expect(Crimp.annotate('A')).to eq(['A', 'S'])
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'handles utf-8 strings' do
|
45
|
+
expect(Crimp.annotate('å')).to eq(['å', 'S'])
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'treats symbols like strings' do
|
49
|
+
expect(Crimp.annotate(:a)).to eq(['a', 'S'])
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'treats empty strings like strings' do
|
53
|
+
expect(Crimp.annotate('')).to eq(['', 'S'])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'Numbers' do
|
58
|
+
it 'handles integers' do
|
59
|
+
expect(Crimp.annotate(1)).to eq([1, 'N'])
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'handles floats' do
|
63
|
+
expect(Crimp.annotate(3.14)).to eq([3.14, 'N'])
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'handles bignums' do
|
67
|
+
bignum = 10_000_000_000_000_000_000
|
68
|
+
|
69
|
+
expect(Crimp.annotate(bignum)).to eq([bignum, 'N'])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'Nils' do
|
74
|
+
it 'handles nils' do
|
75
|
+
expect(Crimp.annotate(nil)).to eq([nil, '_'])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'Booleans' do
|
80
|
+
it 'handles falsey values' do
|
81
|
+
expect(Crimp.annotate(false)).to eq([false, 'B'])
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'handles truthy values' do
|
85
|
+
expect(Crimp.annotate(true)).to eq([true, 'B'])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'Arrays' do
|
90
|
+
it 'handles arrays as collection of primitives' do
|
91
|
+
expect(Crimp.annotate([1, 2])).to eq([[[1, 'N'], [2, 'N']], 'A'])
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'sorts arrays' do
|
95
|
+
expect(Crimp.annotate([2, 1])).to eq([[[1, 'N'], [2, 'N']], 'A'])
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'returns the same signature for two arrays containing the same (unordered) values' do
|
99
|
+
arr1 = [1, 2, 3]
|
100
|
+
arr2 = [2, 1, 3]
|
101
|
+
|
102
|
+
expect(Crimp.signature(arr1)).to eq(Crimp.signature(arr2))
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'does not return the same signature for two arrays containing different values' do
|
106
|
+
arr1 = [1, 2, 3]
|
107
|
+
arr2 = ['1', '2', '3']
|
108
|
+
|
109
|
+
expect(Crimp.signature(arr1)).to_not eq(Crimp.signature(arr2))
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'sorts an array with mixed strings and symbols' do
|
113
|
+
expect(Crimp.notation(["b", :a, "c"])).to eq 'aSbScSA'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe 'Nested Arrays' do
|
118
|
+
it 'sorts arrays with a single nested array' do
|
119
|
+
expect(Crimp.notation([3, [4, 2], 1])).to eq('1N3N2N4NAA')
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'sorts arrays with a multiple nested arrays' do
|
123
|
+
expect(Crimp.notation([3, [4, 2], 1, [6, 5]])).to eq('1N3N2N4NA5N6NAA')
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe 'Hashes' do
|
128
|
+
it 'handles hashes as collection of primitives' do
|
129
|
+
expected = [
|
130
|
+
[
|
131
|
+
[
|
132
|
+
[
|
133
|
+
['a', 'S'],
|
134
|
+
['b', 'S']
|
135
|
+
],
|
136
|
+
'A'
|
137
|
+
]
|
138
|
+
],
|
139
|
+
'H'
|
140
|
+
]
|
141
|
+
|
142
|
+
expect(Crimp.annotate({a: 'b'})).to eq(expected)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'sorts hashes by key and then sorts the resulting pair of tuples' do
|
146
|
+
expected = [
|
147
|
+
[
|
148
|
+
[
|
149
|
+
[
|
150
|
+
[1, 'N'],
|
151
|
+
['e', 'S']
|
152
|
+
],
|
153
|
+
'A'
|
154
|
+
],
|
155
|
+
[
|
156
|
+
[
|
157
|
+
['a', 'S'],
|
158
|
+
['b', 'S']
|
159
|
+
],
|
160
|
+
'A'
|
161
|
+
],
|
162
|
+
[
|
163
|
+
[
|
164
|
+
['c', 'S'],
|
165
|
+
['f', 'S']
|
166
|
+
],
|
167
|
+
'A'
|
168
|
+
]
|
169
|
+
],
|
170
|
+
'H'
|
171
|
+
]
|
172
|
+
|
173
|
+
expect(Crimp.annotate({ a: 'b', f: 'c', 'e' => 1 })).to eq(expected)
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'returns the same signature for two hashes containing the same (unordered) values' do
|
177
|
+
hsh1 = { a: 2, b: 1 }
|
178
|
+
hsh2 = { b: 1, a: 2 }
|
179
|
+
|
180
|
+
expect(Crimp.signature(hsh1)).to eq(Crimp.signature(hsh2))
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'does not return the same signature for two hashes containing the different values' do
|
184
|
+
hsh1 = { a: 1, b: 2 }
|
185
|
+
hsh2 = { a: 2, b: 1 }
|
186
|
+
|
187
|
+
expect(Crimp.signature(hsh1)).to_not eq(Crimp.signature(hsh2))
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'sorts an hash with mixed key types' do
|
191
|
+
expect(Crimp.notation({:b => "c", "d" => "a"})).to eq 'aSdSAbScSAH'
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe 'Sets' do
|
196
|
+
it 'handles sets as arrays' do
|
197
|
+
expect(Crimp.annotate(Set.new([1, 2]))).to eq([[[1, 'N'], [2, 'N']], 'A'])
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'produces the same signature for Array Sets and Arrays' do
|
201
|
+
expect(Crimp.signature(Set.new([1, 2]))).to eq(Crimp.signature([2, 1]))
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'handles Hash sets as arrays' do
|
205
|
+
expect(Crimp.annotate(Set.new({ 1 => 2 }))).to eq([[[[[1, "N"], [2, "N"]], "A"]], "A"])
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'does NOT produce the same signature for Hash Sets and Hashes' do
|
209
|
+
expect(Crimp.signature(Set.new({ 1 => 2 }))).to_not eq(Crimp.signature({ 1 => 2 }))
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'sorts sets as arrays' do
|
213
|
+
expect(Crimp.annotate(Set.new([2, 1]))).to eq([[[1, 'N'], [2, 'N']], 'A'])
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe 'nested data structures' do
|
218
|
+
it 'handles a hash with nested arrays and hashes' do
|
219
|
+
obj = { a: [1, 2], b: { c: 'd' } }
|
220
|
+
|
221
|
+
expected = [
|
222
|
+
[
|
223
|
+
[
|
224
|
+
[
|
225
|
+
[
|
226
|
+
[
|
227
|
+
[1, 'N'],
|
228
|
+
[2, 'N']
|
229
|
+
],
|
230
|
+
'A'
|
231
|
+
],
|
232
|
+
['a', 'S']
|
233
|
+
],
|
234
|
+
'A'
|
235
|
+
],
|
236
|
+
[
|
237
|
+
[
|
238
|
+
['b', 'S'],
|
239
|
+
[
|
240
|
+
[
|
241
|
+
[
|
242
|
+
[
|
243
|
+
['c', 'S'],
|
244
|
+
['d', 'S']
|
245
|
+
],
|
246
|
+
'A']
|
247
|
+
],
|
248
|
+
'H']
|
249
|
+
],
|
250
|
+
'A']
|
251
|
+
],
|
252
|
+
'H'
|
253
|
+
]
|
254
|
+
|
255
|
+
expect(Crimp.annotate(obj)).to eq(expected)
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'handles an array of hashes' do
|
259
|
+
obj = [{ a: 1 }, { b: 2 }]
|
260
|
+
expected= [
|
261
|
+
[
|
262
|
+
[
|
263
|
+
[
|
264
|
+
[
|
265
|
+
[
|
266
|
+
[1, 'N'],
|
267
|
+
['a', 'S']
|
268
|
+
],
|
269
|
+
'A'
|
270
|
+
]
|
271
|
+
],
|
272
|
+
'H'
|
273
|
+
],
|
274
|
+
[
|
275
|
+
[
|
276
|
+
[
|
277
|
+
[
|
278
|
+
[2, 'N'],
|
279
|
+
['b', 'S']
|
280
|
+
],
|
281
|
+
'A'
|
282
|
+
]
|
283
|
+
],
|
284
|
+
'H'
|
285
|
+
]
|
286
|
+
],
|
287
|
+
'A'
|
288
|
+
]
|
289
|
+
|
290
|
+
expect(Crimp.annotate(obj)).to eq(expected)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
describe 'Objects' do
|
295
|
+
it 'raise an error if not in the list of allowed primitives' do
|
296
|
+
expect { Crimp.signature(Object.new) }
|
297
|
+
.to raise_error(TypeError, 'Expected a (String|Number|Boolean|Nil|Hash|Array), Got Object.')
|
98
298
|
end
|
99
299
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,59 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crimp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- BBC News
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-08-
|
11
|
+
date: 2018-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.5'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.5'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rake-rspec
|
57
15
|
requirement: !ruby/object:Gem::Requirement
|
58
16
|
requirements:
|
59
17
|
- - ">="
|
@@ -67,49 +25,7 @@ dependencies:
|
|
67
25
|
- !ruby/object:Gem::Version
|
68
26
|
version: '0'
|
69
27
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
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: guard
|
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'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: guard-rspec
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: pry
|
28
|
+
name: rake
|
113
29
|
requirement: !ruby/object:Gem::Requirement
|
114
30
|
requirements:
|
115
31
|
- - ">="
|
@@ -123,7 +39,7 @@ dependencies:
|
|
123
39
|
- !ruby/object:Gem::Version
|
124
40
|
version: '0'
|
125
41
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
42
|
+
name: rspec
|
127
43
|
requirement: !ruby/object:Gem::Requirement
|
128
44
|
requirements:
|
129
45
|
- - ">="
|
@@ -137,22 +53,21 @@ dependencies:
|
|
137
53
|
- !ruby/object:Gem::Version
|
138
54
|
version: '0'
|
139
55
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
56
|
+
name: deepsort
|
141
57
|
requirement: !ruby/object:Gem::Requirement
|
142
58
|
requirements:
|
143
59
|
- - ">="
|
144
60
|
- !ruby/object:Gem::Version
|
145
61
|
version: '0'
|
146
|
-
type: :
|
62
|
+
type: :runtime
|
147
63
|
prerelease: false
|
148
64
|
version_requirements: !ruby/object:Gem::Requirement
|
149
65
|
requirements:
|
150
66
|
- - ">="
|
151
67
|
- !ruby/object:Gem::Version
|
152
68
|
version: '0'
|
153
|
-
description:
|
154
|
-
|
155
|
-
All credit should go to http://stackoverflow.com/users/394282/luke
|
69
|
+
description: Platform agnostic MD5 hash from simple data structures made of numbers,
|
70
|
+
strings, booleans, nil, arrays or hashes.
|
156
71
|
email:
|
157
72
|
- D&ENewsFrameworksTeam@bbc.co.uk
|
158
73
|
executables: []
|
@@ -163,17 +78,16 @@ files:
|
|
163
78
|
- ".ruby-version"
|
164
79
|
- ".travis.yml"
|
165
80
|
- Gemfile
|
166
|
-
- Guardfile
|
167
81
|
- LICENSE.txt
|
168
82
|
- README.md
|
169
83
|
- Rakefile
|
170
84
|
- crimp.gemspec
|
171
85
|
- lib/crimp.rb
|
172
|
-
-
|
173
|
-
- spec/
|
86
|
+
- spec/acceptance_data.yml
|
87
|
+
- spec/acceptance_spec.rb
|
174
88
|
- spec/crimp_spec.rb
|
175
89
|
- spec/spec_helper.rb
|
176
|
-
homepage:
|
90
|
+
homepage: https://www.bbc.co.uk/news
|
177
91
|
licenses:
|
178
92
|
- MIT
|
179
93
|
metadata: {}
|
@@ -196,8 +110,9 @@ rubyforge_project:
|
|
196
110
|
rubygems_version: 2.7.6
|
197
111
|
signing_key:
|
198
112
|
specification_version: 4
|
199
|
-
summary:
|
113
|
+
summary: Creates an MD5 hash from simple data structures.
|
200
114
|
test_files:
|
201
|
-
- spec/
|
115
|
+
- spec/acceptance_data.yml
|
116
|
+
- spec/acceptance_spec.rb
|
202
117
|
- spec/crimp_spec.rb
|
203
118
|
- spec/spec_helper.rb
|
data/Guardfile
DELETED
data/lib/crimp/version.rb
DELETED
data/spec/consistency_spec.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Crimp do
|
4
|
-
describe '.signature' do
|
5
|
-
context 'check MD5 consistent across versions' do
|
6
|
-
context 'given a Hash' do
|
7
|
-
let(:hash) do
|
8
|
-
{
|
9
|
-
a: 123,
|
10
|
-
b: 1.23,
|
11
|
-
c: 'string',
|
12
|
-
d: :sym
|
13
|
-
}
|
14
|
-
end
|
15
|
-
let(:md5) { '1dd744d51279187cc08cabe240f98be2' }
|
16
|
-
|
17
|
-
specify { expect(subject.signature(hash)).to eq md5 }
|
18
|
-
end
|
19
|
-
|
20
|
-
context 'given legacy Hash' do
|
21
|
-
let(:hash) do
|
22
|
-
{ a: { b: 'b', c: 'c' }, d: 'd' }
|
23
|
-
end
|
24
|
-
let(:md5) { '68d07febc4f47f56fa6ef5de063a77b1' }
|
25
|
-
|
26
|
-
specify { expect(subject.signature(hash)).to eq md5 }
|
27
|
-
end
|
28
|
-
|
29
|
-
context 'given an Array' do
|
30
|
-
let(:array) { [123, 1.23, 'string', :sym] }
|
31
|
-
let(:md5) { 'cd29980f258eef3faceca4f4da02ec65' }
|
32
|
-
|
33
|
-
specify { expect(subject.signature(array)).to eq md5 }
|
34
|
-
end
|
35
|
-
|
36
|
-
context 'given legacy Array' do
|
37
|
-
let(:array) { [1, 2, 3, [4, [5, 6]]] }
|
38
|
-
let(:md5) { '4dc4e1161c9315db0bc43c0201b3ec05' }
|
39
|
-
|
40
|
-
specify { expect(subject.signature(array)).to eq md5 }
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|