hashman 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f1c21f334159a16a1c366703d22c2459332b8074
4
+ data.tar.gz: d1b72f68482fb9911dea923e8037ea2a0b04f291
5
+ SHA512:
6
+ metadata.gz: a63aada8af51e93873e377ed4ab1246ddc20a8f6f29c781ccb15db9c8f2d3b3ee86990d066d39207e62cd95aa0e20d9f04f9ac13e102f1e212bb5389ec5d4155
7
+ data.tar.gz: 66be8d0cbcf7cd11c65129ca24e56dbaf4def4f5a6241a3aee6e17f28bda3c3b0956be51d0e22f0d6020b6dd9296ce6e27e76242709affca6d51b193fd480a82
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.md
3
+ LICENSE.txt
@@ -0,0 +1,7 @@
1
+ /.bundle
2
+ /.idea
3
+ /.yardoc/
4
+ /Gemfile.lock
5
+ /doc/
6
+ /pkg/
7
+ /vendor/cache/*.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
@@ -0,0 +1 @@
1
+ --markup markdown --title "hashman Documentation" --protected
@@ -0,0 +1,4 @@
1
+ ### 0.1.0 / 2016-07-27
2
+
3
+ * Initial release:
4
+
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'kramdown'
7
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2016 Evan Surdam
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,119 @@
1
+ # hashman
2
+
3
+ * [Homepage](https://github.com/esurdam/hashman#readme)
4
+ * [Issues](https://github.com/esurdam/hashman/issues)
5
+ * [Documentation](http://rubydoc.info/gems/hashman/frames)
6
+ * [Email](mailto:es at cosi.io)
7
+
8
+ ## Description
9
+
10
+ Add some hash magic to your ruby classes.
11
+
12
+ Use this gem to conceal ids or any integer based array.
13
+
14
+ Ideally, user a serializer to exlucde ids and include your hash.
15
+
16
+ Credits on hasher to [Peter Hellberg's Hashid](https://github.com/peterhellberg/hashids.rb)
17
+
18
+ ## Features
19
+ - Hash integer and UUID based ids
20
+ - Reverse hash for lookup
21
+ - Create a hashprint based on an array of integers.
22
+ - Index your hashprint for uniqueness (Useful for payment data!) [EXAMPLE]()
23
+
24
+ ## Examples
25
+
26
+ ### Rails Usage
27
+
28
+ Include ```HashMan``` in your model
29
+ ```
30
+ class User < ActiveRecord::Base
31
+ include HashMan
32
+ # include activuuid only if thats your style
33
+ include activeuuid
34
+
35
+ # optionally set a hash length minimum
36
+ # useful for integer based ids
37
+ hash_length 8
38
+ end
39
+ ```
40
+
41
+ A typical user will look like:
42
+ ```
43
+ user = User.create({ :name => "awesome" })
44
+ user.id
45
+ => #<UUID:0x3fe2f1dda1d0 UUID:5604ee6e-c934-44cb-89ff-d2934a708e55>
46
+ ```
47
+
48
+ Generate a hash for an id (integer or uuid):
49
+ ```
50
+ user.hash_id
51
+ => "rkALRLO3rJyz3vRjx9XJrzlq6"
52
+ ```
53
+
54
+ Reverse the hash for lookup:
55
+ ```
56
+ User.reverse_hash("rkALRLO3rJyz3vRjx9XJrzlq6")
57
+ => #<UUID:0x3fe2f1d00f70 UUID:5604ee6e-c934-44cb-89ff-d2934a708e55>
58
+ ```
59
+
60
+ Hashprint sensitive data:
61
+ ```
62
+ # secrets are created in userspace
63
+ secrets = {:number => 1234123412341234, :cvv => 701, :zip => 90025 }
64
+
65
+ # hashprint is created before saving to model
66
+ secret_hash = User.create_hashprint = [ secrets[:number], secrets[:cvv], secrets[:zip ]
67
+ => "evALO3rJyzJrzlq6"
68
+
69
+ # persist the secret
70
+ user.hashprint = secret_hash
71
+ user.save!
72
+ ```
73
+ ```
74
+ # Do some cool stuff in your model
75
+ def verify_cvv(cvv)
76
+ # where [1] is the index of the value
77
+ # `decoded_hashprint` is a private method form hashman
78
+ cvv == self.decoded_hashprint[1]
79
+ end
80
+
81
+ user.verify_cvv(700)
82
+ => false
83
+
84
+ user.verify_cvv(701)
85
+ => true
86
+ ```
87
+ Secret data is then hidden from prying eyes!
88
+
89
+
90
+ Index hashprint for uniqueness validation! ;)
91
+
92
+ ## Standalone
93
+
94
+ require 'hashman'
95
+
96
+ magic
97
+
98
+ ```
99
+ HashMan.encode(integer)
100
+ HashMan.create_hashprint(integer_array)
101
+ Hashman.decode(hash)
102
+
103
+ ```
104
+ ## Requirements
105
+
106
+ ## Todo
107
+
108
+ - Auto intercept `find` method on model to allow hash_id
109
+ - I forget the others
110
+
111
+ ## Install
112
+
113
+ $ gem install hashman
114
+
115
+ ## Copyright
116
+
117
+ Copyright (c) 2016 Evan Surdam
118
+
119
+ See {file:LICENSE.txt} for details.
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler/setup'
7
+ rescue LoadError => e
8
+ abort e.message
9
+ end
10
+
11
+ require 'rake'
12
+
13
+ require 'rubygems/tasks'
14
+ Gem::Tasks.new
15
+
16
+ require 'rspec/core/rake_task'
17
+ RSpec::Core::RakeTask.new
18
+
19
+ task :test => :spec
20
+ task :default => :spec
21
+
22
+ require 'yard'
23
+ YARD::Rake::YardocTask.new
24
+ task :doc => :yard
25
+
26
+ task :console do
27
+ exec "irb -r hashman -I ./lib"
28
+ end
@@ -0,0 +1,19 @@
1
+ name: hashman
2
+ summary: "add some hash magic to your classes"
3
+ description: "HashMan takes any number (or uuid) and converts it to a unique hash. Includes methods on your class
4
+ to access and reverse hash"
5
+ license: MIT
6
+ authors: Evan Surdam
7
+ email: es@cosi.io
8
+ homepage: https://github.com/esurdam/hashman#readme
9
+
10
+ dependencies:
11
+ activeuuid: ~> 0.6
12
+ activesupport: ~> 4.0
13
+
14
+ development_dependencies:
15
+ bundler: ~> 1.10
16
+ rake: ~> 10.0
17
+ rspec: ~> 3.0
18
+ rubygems-tasks: ~> 0.2
19
+ yard: ~> 0.8
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ require 'yaml'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gemspec = YAML.load_file('gemspec.yml')
7
+
8
+ gem.name = gemspec.fetch('name')
9
+ gem.version = gemspec.fetch('version') do
10
+ lib_dir = File.join(File.dirname(__FILE__),'lib')
11
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
12
+
13
+ require 'hashman/version'
14
+ HashMan::VERSION
15
+ end
16
+
17
+ gem.summary = gemspec['summary']
18
+ gem.description = gemspec['description']
19
+ gem.licenses = Array(gemspec['license'])
20
+ gem.authors = Array(gemspec['authors'])
21
+ gem.email = gemspec['email']
22
+ gem.homepage = gemspec['homepage']
23
+
24
+ glob = lambda { |patterns| gem.files & Dir[*patterns] }
25
+
26
+ gem.files = `git ls-files`.split($/)
27
+ gem.files = glob[gemspec['files']] if gemspec['files']
28
+
29
+ gem.executables = gemspec.fetch('executables') do
30
+ glob['bin/*'].map { |path| File.basename(path) }
31
+ end
32
+ gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
33
+
34
+ gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
35
+ gem.test_files = glob[gemspec['test_files'] || '{test/{**/}*_test.rb']
36
+ gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
37
+
38
+ gem.require_paths = Array(gemspec.fetch('require_paths') {
39
+ %w[ext lib].select { |dir| File.directory?(dir) }
40
+ })
41
+
42
+ gem.requirements = Array(gemspec['requirements'])
43
+ gem.required_ruby_version = gemspec['required_ruby_version']
44
+ gem.required_rubygems_version = gemspec['required_rubygems_version']
45
+ gem.post_install_message = gemspec['post_install_message']
46
+
47
+ split = lambda { |string| string.split(/,\s*/) }
48
+
49
+ if gemspec['dependencies']
50
+ gemspec['dependencies'].each do |name,versions|
51
+ gem.add_dependency(name,split[versions])
52
+ end
53
+ end
54
+
55
+ if gemspec['development_dependencies']
56
+ gemspec['development_dependencies'].each do |name,versions|
57
+ gem.add_development_dependency(name,split[versions])
58
+ end
59
+ end
60
+ end
Binary file
@@ -0,0 +1,7 @@
1
+ require 'hashman/hasher'
2
+ require 'hashman/inclusion'
3
+ require 'hashman/version'
4
+
5
+ module HashMan
6
+ # include HashMan::Inclusion
7
+ end
@@ -0,0 +1,297 @@
1
+ module HashMan
2
+ class Hasher
3
+ # VERSION = "1.0.2"
4
+
5
+ MIN_ALPHABET_LENGTH = 16
6
+ SEP_DIV = 3.5
7
+ GUARD_DIV = 12.0
8
+
9
+ DEFAULT_SEPS = "cfhistuCFHISTU"
10
+
11
+ DEFAULT_ALPHABET = "abcdefghijklmnopqrstuvwxyz" +
12
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
13
+ "1234567890"
14
+
15
+ attr_reader :salt, :min_hash_length, :alphabet, :seps, :guards
16
+
17
+ def initialize(salt = "", min_hash_length = 0, alphabet = DEFAULT_ALPHABET)
18
+ @salt = salt
19
+ @min_hash_length = min_hash_length
20
+ @alphabet = alphabet
21
+
22
+ setup_alphabet
23
+ end
24
+
25
+ def encode(*numbers)
26
+ numbers.flatten! if numbers.length == 1
27
+
28
+ if numbers.empty? || numbers.reject { |n| Integer(n) && n >= 0 }.any?
29
+ ""
30
+ else
31
+ internal_encode(numbers)
32
+ end
33
+ end
34
+
35
+ def encode_hex(str)
36
+ return "" unless hex_string?(str)
37
+
38
+ numbers = str.scan(/[\w\W]{1,12}/).map do |num|
39
+ "1#{num}".to_i(16)
40
+ end
41
+
42
+ encode(numbers)
43
+ end
44
+
45
+ def decode(hash)
46
+ return [] if hash.nil? || hash.empty?
47
+
48
+ internal_decode(hash, @alphabet)
49
+ end
50
+
51
+ def decode_hex(hash)
52
+ ret = ""
53
+ numbers = decode(hash)
54
+
55
+ numbers.length.times do |i|
56
+ ret += numbers[i].to_s(16)[1 .. -1]
57
+ end
58
+
59
+ ret.upcase
60
+ end
61
+
62
+ protected
63
+
64
+ def internal_encode(numbers)
65
+ ret = ""
66
+
67
+ alphabet = @alphabet
68
+ length = numbers.length
69
+ hash_int = 0
70
+
71
+ length.times do |i|
72
+ hash_int += (numbers[i] % (i + 100))
73
+ end
74
+
75
+ lottery = ret = alphabet[hash_int % alphabet.length]
76
+
77
+ length.times do |i|
78
+ num = numbers[i]
79
+ buf = lottery + salt + alphabet
80
+
81
+ alphabet = consistent_shuffle(alphabet, buf[0, alphabet.length])
82
+ last = hash(num, alphabet)
83
+
84
+ ret += last
85
+
86
+ if (i + 1) < length
87
+ num %= (last.ord + i)
88
+ ret += seps[num % seps.length]
89
+ end
90
+ end
91
+
92
+ if ret.length < min_hash_length
93
+ ret = guards[(hash_int + ret[0].ord) % guards.length] + ret
94
+
95
+ if ret.length < min_hash_length
96
+ ret += guards[(hash_int + ret[2].ord) % guards.length]
97
+ end
98
+ end
99
+
100
+ half_length = alphabet.length.div(2)
101
+
102
+ while(ret.length < min_hash_length)
103
+ alphabet = consistent_shuffle(alphabet, alphabet)
104
+ ret = alphabet[half_length .. -1] + ret + alphabet[0, half_length]
105
+
106
+ excess = ret.length - min_hash_length
107
+ ret = ret[excess / 2, min_hash_length] if excess > 0
108
+ end
109
+
110
+ ret
111
+ end
112
+
113
+ def internal_decode(hash, alphabet)
114
+ ret = []
115
+
116
+ breakdown = hash.gsub(/[#{@guards}]/, " ")
117
+ array = breakdown.split(" ")
118
+
119
+ i = [3,2].include?(array.length) ? 1 : 0
120
+
121
+ if breakdown = array[i]
122
+ lottery = breakdown[0]
123
+ breakdown = breakdown[1 .. -1].gsub(/[#{@seps}]/, " ")
124
+ array = breakdown.split(" ")
125
+
126
+ array.length.times do |i|
127
+ sub_hash = array[i]
128
+ buffer = lottery + salt + alphabet
129
+ alphabet = consistent_shuffle(alphabet, buffer[0, alphabet.length])
130
+
131
+ ret.push unhash(sub_hash, alphabet)
132
+ end
133
+
134
+ if encode(ret) != hash
135
+ ret = []
136
+ end
137
+ end
138
+
139
+ ret
140
+ end
141
+
142
+ def consistent_shuffle(alphabet, salt)
143
+ return alphabet if salt.nil? || salt.empty?
144
+
145
+ v = 0
146
+ p = 0
147
+
148
+ (alphabet.length-1).downto(1) do |i|
149
+ v = v % salt.length
150
+ p += n = salt[v].ord
151
+ j = (n + v + p) % i
152
+
153
+ tmp_char = alphabet[j]
154
+
155
+ alphabet = alphabet[0, j] + alphabet[i] + alphabet[j + 1..-1]
156
+ alphabet = alphabet[0, i] + tmp_char + alphabet[i + 1..-1]
157
+
158
+ v += 1
159
+ end
160
+
161
+ alphabet
162
+ end
163
+
164
+ def hash(input, alphabet)
165
+ num = input.to_i
166
+ len = alphabet.length
167
+ res = ""
168
+
169
+ begin
170
+ res = "#{alphabet[num % len]}#{res}"
171
+ num = num.div(alphabet.length)
172
+ end while num > 0
173
+
174
+ res
175
+ end
176
+
177
+ def unhash(input, alphabet)
178
+ num = 0
179
+
180
+ input.length.times do |i|
181
+ pos = alphabet.index(input[i])
182
+
183
+ raise InputError, "unable to unhash" unless pos
184
+
185
+ num += pos * alphabet.length ** (input.length - i - 1)
186
+ end
187
+
188
+ num
189
+ end
190
+
191
+ private
192
+
193
+ def setup_alphabet
194
+ validate_attributes
195
+
196
+ @alphabet = uniq_characters(alphabet)
197
+
198
+ validate_alphabet
199
+
200
+ setup_seps
201
+ setup_guards
202
+ end
203
+
204
+ def setup_seps
205
+ @seps = DEFAULT_SEPS
206
+
207
+ seps.length.times do |i|
208
+ # Seps should only contain characters present in alphabet,
209
+ # and alphabet should not contains seps
210
+ if j = alphabet.index(seps[i])
211
+ @alphabet = pick_characters(alphabet, j)
212
+ else
213
+ @seps = pick_characters(seps, i)
214
+ end
215
+ end
216
+
217
+ alphabet.delete!(' ')
218
+ seps.delete!(' ')
219
+
220
+ @seps = consistent_shuffle(seps, salt)
221
+
222
+ if seps.length == 0 || (alphabet.length / seps.length.to_f) > SEP_DIV
223
+ seps_length = (alphabet.length / SEP_DIV).ceil
224
+ seps_length = 2 if seps_length == 1
225
+
226
+ if seps_length > seps.length
227
+ diff = seps_length - seps.length;
228
+
229
+ @seps += alphabet[0, diff]
230
+ @alphabet = alphabet[diff .. -1]
231
+ else
232
+ @seps = seps[0, seps_length]
233
+ end
234
+ end
235
+
236
+ @alphabet = consistent_shuffle(alphabet, salt)
237
+ end
238
+
239
+ def setup_guards
240
+ gc = (alphabet.length / GUARD_DIV).ceil
241
+
242
+ if alphabet.length < 3
243
+ @guards = seps[0, gc]
244
+ @seps = seps[gc .. -1]
245
+ else
246
+ @guards = alphabet[0, gc]
247
+ @alphabet = alphabet[gc .. -1]
248
+ end
249
+ end
250
+
251
+ SaltError = Class.new(ArgumentError)
252
+ MinLengthError = Class.new(ArgumentError)
253
+ AlphabetError = Class.new(ArgumentError)
254
+ InputError = Class.new(ArgumentError)
255
+
256
+ def validate_attributes
257
+ unless salt.kind_of?(String)
258
+ raise SaltError, "The salt must be a String"
259
+ end
260
+
261
+ unless min_hash_length.kind_of?(Fixnum)
262
+ raise MinLengthError, "The min length must be a Fixnum"
263
+ end
264
+
265
+ unless min_hash_length >= 0
266
+ raise MinLengthError, "The min length must be 0 or more"
267
+ end
268
+
269
+ unless alphabet.kind_of?(String)
270
+ raise AlphabetError, "The alphabet must be a String"
271
+ end
272
+
273
+ if alphabet.include?(' ')
274
+ raise AlphabetError, "The alphabet can’t include spaces"
275
+ end
276
+ end
277
+
278
+ def validate_alphabet
279
+ unless alphabet.length >= MIN_ALPHABET_LENGTH
280
+ raise AlphabetError, "Alphabet must contain at least " +
281
+ "#{MIN_ALPHABET_LENGTH} unique characters."
282
+ end
283
+ end
284
+
285
+ def hex_string?(string)
286
+ string.to_s.match(/\A[0-9a-fA-F]+\Z/)
287
+ end
288
+
289
+ def pick_characters(array, index)
290
+ array[0, index] + " " + array[index + 1 .. -1]
291
+ end
292
+
293
+ def uniq_characters(string)
294
+ string.split('').uniq.join('')
295
+ end
296
+ end
297
+ end
@@ -0,0 +1,62 @@
1
+ require 'hashman/version'
2
+ require 'active_support'
3
+
4
+ module HashMan
5
+ module Inclusion
6
+ extend ActiveSupport::Concern
7
+
8
+ def hash_id
9
+ self.class.generate_hash_id(id)
10
+ end
11
+
12
+ # extend methods for class
13
+ def self.included(base)
14
+ base.extend(ClassMethods)
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ # generate the hash_id using the @magic_number declared in class
20
+ # id class === String || UUIDTools::UUID || Fixnum
21
+
22
+ def hash_length(number)
23
+ @magic_number = number
24
+ end
25
+
26
+ def generate_hash_id(id)
27
+ hasher.encode(rinse_id(id))
28
+ end
29
+
30
+ # reverse the has to get the original id
31
+ # if UUID, parse it and return the UUID object
32
+
33
+ def reverse_hash(hash)
34
+ id = hasher.decode(hash).try(:first)
35
+ (self.include? Dynamoid::Document) || (self.columns_hash["id"].type == :uuid) ? UUIDTools::UUID.parse_int(id) : id
36
+ end
37
+
38
+ private
39
+
40
+ def hasher
41
+ Hashman::Hasher.new("#{Rails.application.secrets.magic_number}", @magic_number || 4)
42
+ end
43
+
44
+ # standardize the id integer
45
+ # we do not specify UUIDTools since comparisons break!
46
+ # String (activeUUID tables), FixNum (normal ids), UUID (dynamoid/VideoClips)
47
+
48
+ def rinse_id(id)
49
+ case
50
+ when id.class == String
51
+ return UUIDTools::UUID.parse(id).to_i
52
+ when id.class == Fixnum
53
+ return id
54
+ else
55
+ return id.to_i
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,4 @@
1
+ module HashMan
2
+ # hashman version
3
+ VERSION = "0.1.0"
4
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require 'hashman'
3
+
4
+ describe Hashman do
5
+ it "should have a VERSION constant" do
6
+ expect(subject.const_get('VERSION')).to_not be_empty
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ require 'rspec'
2
+ require 'hashman/version'
3
+
4
+ include HashMan
5
+
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hashman
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Evan Surdam
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-07-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activeuuid
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubygems-tasks
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.2'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.2'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.8'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.8'
111
+ description: HashMan takes any number (or uuid) and converts it to a unique hash.
112
+ Includes methods on your class to access and reverse hash
113
+ email: es@cosi.io
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files:
117
+ - ChangeLog.md
118
+ - LICENSE.txt
119
+ - README.md
120
+ files:
121
+ - ".document"
122
+ - ".gitignore"
123
+ - ".rspec"
124
+ - ".yardopts"
125
+ - ChangeLog.md
126
+ - Gemfile
127
+ - LICENSE.txt
128
+ - README.md
129
+ - Rakefile
130
+ - gemspec.yml
131
+ - hashman.gemspec
132
+ - lib/.DS_Store
133
+ - lib/hashman.rb
134
+ - lib/hashman/hasher.rb
135
+ - lib/hashman/inclusion.rb
136
+ - lib/hashman/version.rb
137
+ - spec/hashman_spec.rb
138
+ - spec/spec_helper.rb
139
+ homepage: https://github.com/esurdam/hashman#readme
140
+ licenses:
141
+ - MIT
142
+ metadata: {}
143
+ post_install_message:
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 2.5.1
160
+ signing_key:
161
+ specification_version: 4
162
+ summary: add some hash magic to your classes
163
+ test_files: []