hashman 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []