wordhash 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENCE +21 -0
- data/README.md +70 -0
- data/lib/word_hash.rb +108 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/word_hash_spec.rb +81 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bafbadf27d8434cf3cc4f76759abf5cdcea16c9c
|
4
|
+
data.tar.gz: 7147720cbd69eadcefdfe6cc3cdbedcf1c339bdb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1a575fd75b4116782b6318fff4eaab149f618660e29f383bb75ae7ca236a605ad5a77e9dfcc62d683d9abfea8e70bd1cb33f21a43316488824d3125aa3330ff2
|
7
|
+
data.tar.gz: 4dc26aa7779d1c85160f0f0079aa7f2b2aba1a7a882568eab772d90d3253bb359d12b2a759e59eb93373b8f28a2b1874ffb9d176fe09e63ad0f7398b87c83b52
|
data/LICENCE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright © 2015 Louis Pilfold. All Rights Reserved.
|
4
|
+
|
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:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
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
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# wordhash
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/lpil/wordhash.svg?branch=master)](https://travis-ci.org/lpil/humanhash.rb)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/lpil/wordhash/badges/gpa.svg)](https://codeclimate.com/github/lpil/humanhash.rb)
|
5
|
+
|
6
|
+
wordhash provides human-readable representations of digests.
|
7
|
+
A clone of Zachary Voase's
|
8
|
+
[humanhash](https://github.com/zacharyvoase/humanhash).
|
9
|
+
|
10
|
+
## Example
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
require 'word_hash'
|
14
|
+
|
15
|
+
digest = '7528880a986c40e78c38115e640da2a1'
|
16
|
+
# => "7528880a986c40e78c38115e640da2a1"
|
17
|
+
|
18
|
+
WordHash.new.humanize digest
|
19
|
+
# => "three-georgia-xray-jig"
|
20
|
+
WordHash.new.humanize digest, 6
|
21
|
+
# => "high-mango-white-oregon-purple-charlie"
|
22
|
+
|
23
|
+
WordHash.new.uuid
|
24
|
+
# => ["04edcd5325a34dc191d4880676ebae61", "lemon-artist-snake-gee"]
|
25
|
+
```
|
26
|
+
|
27
|
+
## Caveats
|
28
|
+
|
29
|
+
Don't store the humanhash output, as its statistical uniqueness is only around
|
30
|
+
1 in 4.3 billion. Its intended use is as a human-readable (and, most
|
31
|
+
importantly, memorable) representation of a longer digest, unique enough for
|
32
|
+
display in a user interface, where a user may need to remember or verbally
|
33
|
+
communicate the identity of a hash, without having to remember a 40-character
|
34
|
+
hexadecimal sequence. Nevertheless, you should keep original digests around,
|
35
|
+
then pass them through humanize() only as you're displaying them.
|
36
|
+
|
37
|
+
## How It Works
|
38
|
+
|
39
|
+
The procedure for generating a humanhash involves compressing the input to a
|
40
|
+
fixed length (default: 4 bytes), then mapping each of these bytes to a word in
|
41
|
+
a pre-defined wordlist (a default wordlist is supplied with the library). This
|
42
|
+
algorithm is consistent, so the same input, given the same wordlist, will
|
43
|
+
always give the same output. You can also use your own wordlist, and specify a
|
44
|
+
different number of words for output.
|
45
|
+
|
46
|
+
(The algorithm and this text are completely lifted from Zachary's version)
|
47
|
+
|
48
|
+
## Licence
|
49
|
+
|
50
|
+
```
|
51
|
+
Copyright © 2015 Louis Pilfold. All Rights Reserved.
|
52
|
+
|
53
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
54
|
+
a copy of this software and associated documentation files (the "Software"),
|
55
|
+
to deal in the Software without restriction, including without limitation
|
56
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
57
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
58
|
+
Software is furnished to do so, subject to the following conditions:
|
59
|
+
|
60
|
+
The above copyright notice and this permission notice shall be included
|
61
|
+
in all copies or substantial portions of the Software.
|
62
|
+
|
63
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
64
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
65
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
66
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
67
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
68
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
69
|
+
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
70
|
+
```
|
data/lib/word_hash.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
# Transforms hex digests to human-readable strings.
|
4
|
+
#
|
5
|
+
# The format of these strings will look something like:
|
6
|
+
# `victor-bacon-zulu-lima`. The output is obtained by compressing the input
|
7
|
+
# digest to a fixed number of bytes, then mapping those bytes to one of 256
|
8
|
+
# words. A default wordlist is provided, but you can override this if you
|
9
|
+
# prefer.
|
10
|
+
#
|
11
|
+
# As long as you use the same wordlist, the output will be consistent (i.e.
|
12
|
+
# the same digest will always render the same representation).
|
13
|
+
class WordHash
|
14
|
+
attr_reader :wordlist
|
15
|
+
|
16
|
+
DEFAULT_WORDLIST = %w(
|
17
|
+
ack alabama alanine alaska alpha angel apart april arizona arkansas artist
|
18
|
+
asparagus aspen august autumn avocado bacon bakerloo batman beer berlin
|
19
|
+
beryllium black blossom blue bluebird bravo bulldog burger butter
|
20
|
+
california carbon cardinal carolina carpet cat ceiling charlie chicken
|
21
|
+
coffee cola cold colorado comet connecticut crazy cup dakota december
|
22
|
+
delaware delta diet don double early earth east echo edward eight eighteen
|
23
|
+
eleven emma enemy equal failed fanta fifteen fillet finch fish five fix
|
24
|
+
floor florida football four fourteen foxtrot freddie friend fruit gee
|
25
|
+
georgia glucose golf green grey hamper happy harry hawaii helium high hot
|
26
|
+
hotel hydrogen idaho illinois india indigo ink iowa island item jersey jig
|
27
|
+
johnny juliet july jupiter kansas kentucky kilo king kitten lactose lake
|
28
|
+
lamp lemon leopard lima lion lithium london louisiana low magazine
|
29
|
+
magnesium maine mango march mars maryland massachusetts may mexico michigan
|
30
|
+
mike minnesota mirror mississippi missouri mobile mockingbird monkey
|
31
|
+
montana moon mountain muppet music nebraska neptune network nevada nine
|
32
|
+
nineteen nitrogen north november nuts october ohio oklahoma one orange
|
33
|
+
oranges oregon oscar oven oxygen papa paris pasta pennsylvania pip pizza
|
34
|
+
pluto potato princess purple quebec queen quiet red river robert robin
|
35
|
+
romeo rugby sad salami saturn september seven seventeen shade sierra single
|
36
|
+
sink six sixteen skylark snake social sodium solar south spaghetti speaker
|
37
|
+
spring stairway steak stream summer sweet table tango ten tennessee tennis
|
38
|
+
texas thirteen three timing triple twelve twenty two uncle undress uniform
|
39
|
+
uranus utah vegan venus vermont victor video violet virginia washington
|
40
|
+
west whiskey white william winner winter wisconsin wolfram wyoming xray
|
41
|
+
yankee yellow zebra zulu
|
42
|
+
)
|
43
|
+
|
44
|
+
def initialize(wordlist = DEFAULT_WORDLIST)
|
45
|
+
fail ArgumentError, 'Wordlist must have exactly 256 items' unless
|
46
|
+
wordlist.length == 256
|
47
|
+
|
48
|
+
@wordlist = wordlist
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# Compress a list of byte values to a fixed target length.
|
53
|
+
#
|
54
|
+
# Attempting to compress a smaller number of bytes to a larger
|
55
|
+
# number is an error
|
56
|
+
def compress(bytes, target)
|
57
|
+
fail ArgumentError, 'Fewer input bytes than requested output' if
|
58
|
+
target > bytes.size
|
59
|
+
|
60
|
+
slices = slice_bytes bytes, target
|
61
|
+
|
62
|
+
# XOR checksum-like compression
|
63
|
+
slices.map do |slice|
|
64
|
+
slice.reduce :^
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Humanize a given hexadecimal digest.
|
69
|
+
#
|
70
|
+
# Change the number of words output by specifying `words`. Change the
|
71
|
+
# word separator with `separator`.
|
72
|
+
def humanize(hexdigest, words = 4, separator = '-')
|
73
|
+
bytes = hexdigest.split('')
|
74
|
+
.each_slice(2).to_a
|
75
|
+
bytes.pop if bytes.last.size == 1
|
76
|
+
|
77
|
+
bytes = bytes.map { |byte| byte.join('').to_i 16 }
|
78
|
+
|
79
|
+
wordify_bytes(bytes, words, separator)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Generate a UUID with a human-readable representation.
|
83
|
+
#
|
84
|
+
# Returns `[human_repr, full_digest]`.
|
85
|
+
# Accepts the same keyword arguments as `humanize`
|
86
|
+
def uuid(*args)
|
87
|
+
digest = SecureRandom.uuid.delete '-'
|
88
|
+
word_hash = humanize digest, *args
|
89
|
+
[digest, word_hash]
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def wordify_bytes(bytes, words, separator)
|
95
|
+
compress(bytes, words).map { |byte| wordlist[byte] }
|
96
|
+
.join separator
|
97
|
+
end
|
98
|
+
|
99
|
+
def slice_bytes(bytes, target)
|
100
|
+
slice_size = bytes.size / target
|
101
|
+
slices = bytes.each_slice(slice_size).to_a
|
102
|
+
|
103
|
+
# Catch any left-over bytes in the last segment.
|
104
|
+
extra = slices.pop(slices.size - target).flatten
|
105
|
+
slices.last.concat extra unless extra.empty?
|
106
|
+
slices
|
107
|
+
end
|
108
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative '../lib/human_hash'
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe WordHash do
|
4
|
+
describe '#initialize' do
|
5
|
+
it 'requires a wordlist of length 256' do
|
6
|
+
expect { WordHash.new Array.new(255, 'word') }
|
7
|
+
.to raise_error ArgumentError
|
8
|
+
|
9
|
+
expect { WordHash.new Array.new(257, 'word') }
|
10
|
+
.to raise_error ArgumentError
|
11
|
+
|
12
|
+
expect { WordHash.new Array.new(256, 'word') }
|
13
|
+
.not_to raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'will initialize with no args to use a default' do
|
17
|
+
expect { WordHash.new }.not_to raise_error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#compress' do
|
22
|
+
it 'requires a byte list longer than the target length' do
|
23
|
+
expect { WordHash.new.compress [1, 2, 3], 4 }
|
24
|
+
.to raise_error ArgumentError
|
25
|
+
end
|
26
|
+
|
27
|
+
it do
|
28
|
+
bytes = [96, 173, 141, 13, 135, 27, 96, 149, 128, 130, 151]
|
29
|
+
expect(WordHash.new.compress bytes, 4).to eq [205, 128, 156, 96]
|
30
|
+
end
|
31
|
+
|
32
|
+
it do
|
33
|
+
bytes = [96, 173, 141, 13, 135, 27, 96, 149, 128, 130, 151]
|
34
|
+
expect(WordHash.new.compress bytes, 2).to eq [202, 123]
|
35
|
+
end
|
36
|
+
|
37
|
+
it do
|
38
|
+
bytes = [96, 173, 141, 13, 135, 27, 96, 149, 128, 130, 151]
|
39
|
+
expect(WordHash.new.compress bytes, 8)
|
40
|
+
.to eq [96, 173, 141, 13, 135, 27, 96, 0]
|
41
|
+
end
|
42
|
+
|
43
|
+
it do
|
44
|
+
bytes = [96, 173, 141, 13, 135, 149, 128, 130, 151]
|
45
|
+
expect(WordHash.new.compress bytes, 3).to eq [64, 31, 149]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#humanize' do
|
50
|
+
it 'humanizes' do
|
51
|
+
digest = '60ad8d0d871b6095808297'
|
52
|
+
expect(WordHash.new.humanize digest)
|
53
|
+
.to eq 'sodium-magnesium-nineteen-hydrogen'
|
54
|
+
end
|
55
|
+
|
56
|
+
it do
|
57
|
+
expect(WordHash.new.humanize '60ad8d0d871b60958082971')
|
58
|
+
.to eq 'sodium-magnesium-nineteen-hydrogen'
|
59
|
+
end
|
60
|
+
|
61
|
+
it do
|
62
|
+
expect(WordHash.new.humanize '295734958734589739587')
|
63
|
+
.to eq 'low-october-princess-pennsylvania'
|
64
|
+
end
|
65
|
+
|
66
|
+
it do
|
67
|
+
expect(WordHash.new.humanize '2398532498572856792989')
|
68
|
+
.to eq 'robin-lemon-south-oregon'
|
69
|
+
end
|
70
|
+
|
71
|
+
it do
|
72
|
+
expect(WordHash.new.humanize '205934859384566493698')
|
73
|
+
.to eq 'lima-pluto-blossom-six'
|
74
|
+
end
|
75
|
+
|
76
|
+
it do
|
77
|
+
expect(WordHash.new.humanize '298234859234895734895389')
|
78
|
+
.to eq 'november-cat-vegan-georgia'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wordhash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Louis Pilfold
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.1'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.28'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.28'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.10'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.10'
|
55
|
+
description: |2-
|
56
|
+
|
57
|
+
wordhash: Human-readable representations of digests.
|
58
|
+
|
59
|
+
A clone of Zachary Voase's humanhash.
|
60
|
+
email: louis@lpil.uk
|
61
|
+
executables: []
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- LICENCE
|
66
|
+
- README.md
|
67
|
+
- lib/word_hash.rb
|
68
|
+
- spec/spec_helper.rb
|
69
|
+
- spec/word_hash_spec.rb
|
70
|
+
homepage: http://github.com/lpil/wordhash
|
71
|
+
licenses:
|
72
|
+
- MIT
|
73
|
+
metadata: {}
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 2.4.5
|
91
|
+
signing_key:
|
92
|
+
specification_version: 4
|
93
|
+
summary: Human-readable representations of digests
|
94
|
+
test_files:
|
95
|
+
- spec/spec_helper.rb
|
96
|
+
- spec/word_hash_spec.rb
|