flexkey 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.
- data/.gitignore +18 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +169 -0
- data/Rakefile +4 -0
- data/flexkey.gemspec +26 -0
- data/lib/flexkey.rb +3 -0
- data/lib/flexkey/char_pool.rb +108 -0
- data/lib/flexkey/generator.rb +120 -0
- data/lib/flexkey/version.rb +3 -0
- data/spec/flexkey/char_pool_spec.rb +188 -0
- data/spec/flexkey/generator_spec.rb +152 -0
- data/spec/spec_helper.rb +3 -0
- metadata +134 -0
data/.gitignore
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup=markdown
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Devin McCabe
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
## Flexkey
|
2
|
+
|
3
|
+
Flexkey is a Ruby gem for generating unique, random strings for use as product keys, redemption codes, invoice numbers, passwords, etc.
|
4
|
+
|
5
|
+
### Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'flexkey'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install flexkey
|
18
|
+
|
19
|
+
##### Dependencies
|
20
|
+
|
21
|
+
None. Flexkey has been tested with Ruby 1.9.3 and 2.0.
|
22
|
+
|
23
|
+
### Usage
|
24
|
+
|
25
|
+
##### Basic usage
|
26
|
+
|
27
|
+
Instantiate a new generator with `Flexkey::Generator.new` and provide a `format` and `char_pool`. Then, generate a single key or multiple unique keys with the `generate` method:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
keygen = Flexkey::Generator.new(format: 'aaaa-aa', char_pool: { 'a' => :alpha_upper })
|
31
|
+
# => #<Flexkey::Generator @format="aaaa-aa", @char_pool={"a"=>:alpha_upper}, @n_possible_keys=308915776>
|
32
|
+
keygen.generate
|
33
|
+
# => "KBND-NR"
|
34
|
+
keygen.generate(3)
|
35
|
+
# => ["IVXT-LB", "BFZB-LM", "WIVG-ZJ"]
|
36
|
+
```
|
37
|
+
|
38
|
+
In this example, Flexkey generates a key by replacing each instance of `'a'` in the string `'aaaa-aa'` with a character
|
39
|
+
randomly drawn from a pool of uppercase letters while leaving unspecified characters (i.e. `'-'`) alone.
|
40
|
+
|
41
|
+
Since you're most likely persisting generated keys in a database, even though `generate(n)` will return `n` _unique_ keys, you'll still want to validate them for uniqueness against keys you've previously saved.
|
42
|
+
|
43
|
+
##### Built-in character types
|
44
|
+
|
45
|
+
| **Character type** | **Description** |
|
46
|
+
|-------------------|--------------------------------------------------------|
|
47
|
+
| alpha_upper | uppercase letters |
|
48
|
+
| alpha_lower | lowercase letters |
|
49
|
+
| numeric | numerals |
|
50
|
+
| alpha_upper_clear | alpha_upper without ambiguous letters ("S", "O", etc.) |
|
51
|
+
| alpha_lower_clear | alpha_lower without ambiguous letters ("l", "O", etc.) |
|
52
|
+
| numeric_clear | numeric without ambiguous numerals ("0", "1", and "5") |
|
53
|
+
| symbol | symbols on a standard U.S. keyboard |
|
54
|
+
| basic_symbol | basic symbols on a standard U.S. keyboard |
|
55
|
+
|
56
|
+
The names and characters present in each type can be retrieved as such:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
Flexkey::CharPool.available_char_types
|
60
|
+
# => [:alpha_upper, :alpha_lower, :numeric, :alpha_upper_clear, :alpha_lower_clear, :numeric_clear, :symbol, :basic_symbol]
|
61
|
+
Flexkey::CharPool.available_char_pools
|
62
|
+
# => {:alpha_upper=>"ABCDEFGHIJKLMNOPQRSTUVWXYZ", :alpha_lower=>"abcdefghijklmnopqrstuvwxyz", :numeric=>"0123456789", :alpha_upper_clear=>"ABCDEFGHJKLMNPQRTUVWXYZ", :alpha_lower_clear=>"abcdefghjkmnpqrtuvwxyz", :numeric_clear=>"2346789", :symbol=>"!@\#$%^&*;:()_+-=[]{}\\|'\",.<>/?", :basic_symbol=>"!@\#$%^&*;:"}
|
63
|
+
```
|
64
|
+
|
65
|
+
##### Examples
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
Flexkey::Generator.new(format: 'a-nnnn', char_pool: {
|
69
|
+
'a' => :alpha_upper, 'n' => :numeric
|
70
|
+
}).generate
|
71
|
+
# => "Y-1145"
|
72
|
+
```
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
Flexkey::Generator.new(format: 'SN-nnnnnnnnn', char_pool: {
|
76
|
+
'n' => :numeric
|
77
|
+
}).generate
|
78
|
+
# => "SN-181073470"
|
79
|
+
```
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
Flexkey::Generator.new(format: 'AA.aa.nn.ss', char_pool: {
|
83
|
+
'A' => :alpha_upper_clear, 'a' => :alpha_lower_clear, 'n' => :numeric_clear, 's' => :basic_symbol
|
84
|
+
}).generate
|
85
|
+
# => "MX.mh.33.*:"
|
86
|
+
```
|
87
|
+
|
88
|
+
You can also specify a custom string instead of using a built-in type.
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
Flexkey::Generator.new(format: 'annn/c', char_pool: {
|
92
|
+
'a' => :alpha_upper, 'n' => :numeric, 'c' => 'LMN'
|
93
|
+
}).generate(5)
|
94
|
+
# => ["X905/N", "F865/L", "M423/N", "V564/L", "V874/M"]
|
95
|
+
```
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
Flexkey::Generator.new(format: 'mnnnnnnn', char_pool: {
|
99
|
+
'm' => '123456789', 'n' => :numeric
|
100
|
+
}).generate(5)
|
101
|
+
# => ["45862188", "23054329", "36248220", "56044911", "49873464"]
|
102
|
+
```
|
103
|
+
|
104
|
+
##### Selecting from multiple character types
|
105
|
+
|
106
|
+
To replace a character in the `format` from a pool of multiple character types, specify the both the types and proportions in the `char_pool`. For example, to generate keys of length 10 where each character is randomly selected from a pool of uppercase letters and numerals with equal proportions, do the following:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
Flexkey::Generator.new(format: '..........', char_pool: {
|
110
|
+
'.' => { alpha_upper: 0.5, numeric: 0.5 }
|
111
|
+
}).generate(5)
|
112
|
+
# => ["OXAEXC2O87", "3D95JXQ60P", "8F28OX31Y8", "65ZY7IM6TL", "B971Q602SO"]
|
113
|
+
```
|
114
|
+
|
115
|
+
You can specify proportions with any positive numbers:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
Flexkey::Generator.new(format: 'U-ccccc-ccccc', char_pool: {
|
119
|
+
'U' => :alpha_upper_clear, 'c' => { numeric_clear: 1, alpha_upper_clear: 7 }
|
120
|
+
}).generate(5)
|
121
|
+
# => ["H-KYLYK-DQLK3", "U-7QUXV-6DXNW", "X-REYAX-LL489", "L-8ABNJ-ZW3A7", "M-TPVTW-VEMTE"]
|
122
|
+
```
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
Flexkey::Generator.new(format: 'UUUUU.LLLLL', char_pool: {
|
126
|
+
'U' => { 'WXYZ' => 3, 'FGH' => 1 }, 'L' => :alpha_lower_clear
|
127
|
+
}).generate(5)
|
128
|
+
# => ["XZYYF.kwkth", "HHFYW.nkpdr", "WXXGW.fvyeu", "HWFXW.xzgeq", "WXWXW.twrdk"]
|
129
|
+
```
|
130
|
+
|
131
|
+
##### Number of possible keys
|
132
|
+
|
133
|
+
Flexkey will raise an exception if you try to request more keys than are possible with the `format` you provided:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
keygen = Flexkey::Generator.new(format: 'annn', char_pool: {
|
137
|
+
'a' => :alpha_upper, 'n' => :numeric
|
138
|
+
})
|
139
|
+
keygen.generate(3)
|
140
|
+
# => ["F202", "U811", "W802"]
|
141
|
+
keygen.generate(26001)
|
142
|
+
# Flexkey::GeneratorError: There are only 26000 possible keys
|
143
|
+
keygen.n_possible_keys
|
144
|
+
# => 26000
|
145
|
+
```
|
146
|
+
|
147
|
+
The instance method `n_possible_keys` is available for reference.
|
148
|
+
|
149
|
+
##### CharPool
|
150
|
+
|
151
|
+
If you're only interested in using the proportional sampling feature of Flexkey and will generate keys yourself, use `Flexkey::CharPool.generate` with a single hash argument:
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
char_pool = Flexkey::CharPool.generate({ alpha_upper: 0.75, numeric: 0.25 })
|
155
|
+
10.times.map { char_pool.sample }.join
|
156
|
+
=> "XC3RPKKWKA"
|
157
|
+
```
|
158
|
+
|
159
|
+
### Additional documentation
|
160
|
+
|
161
|
+
Somewhere.
|
162
|
+
|
163
|
+
### Contributing
|
164
|
+
|
165
|
+
1. Fork it
|
166
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
167
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
168
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
169
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/flexkey.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'flexkey/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'flexkey'
|
8
|
+
spec.version = Flexkey::VERSION
|
9
|
+
spec.authors = ['Devin McCabe']
|
10
|
+
spec.email = ['devin.mccabe@gmail.com']
|
11
|
+
spec.description = %q{Flexible product key generation}
|
12
|
+
spec.summary = %q{Use Flexkey to generate random product keys for use in software licenses,
|
13
|
+
invoices, etc.}
|
14
|
+
spec.homepage = 'https://github.com/dpmccabe/flexkey'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
23
|
+
spec.add_development_dependency 'rake'
|
24
|
+
spec.add_development_dependency 'rspec'
|
25
|
+
spec.add_development_dependency 'rspec-its'
|
26
|
+
end
|
data/lib/flexkey.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
module Flexkey
|
2
|
+
class CharPoolError < StandardError; end
|
3
|
+
|
4
|
+
module CharPool
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Generates an array of characters of the specified types and proportions
|
8
|
+
#
|
9
|
+
# @param arg [Hash{ Symbol, String => Fixnum }, Symbol, String] a single character type or a
|
10
|
+
# hash of several character types and their proportions
|
11
|
+
#
|
12
|
+
# @return [Array<String>] the requested character pool
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# Flexkey::CharPool.generate(:numeric)
|
16
|
+
# Flexkey::CharPool.generate('XYZ01234')
|
17
|
+
# Flexkey::CharPool.generate({ numeric: 0.25, alpha_upper: 0.75 })
|
18
|
+
# Flexkey::CharPool.generate({ alpha_lower_clear: 1, alpha_upper_clear: 3 })
|
19
|
+
# Flexkey::CharPool.generate({ 'AEIOU' => 1, 'BCDFGHJKLMNPQRSTVWXYZ' => 1, symbol: 0.5 })
|
20
|
+
def generate(arg)
|
21
|
+
if arg.is_a?(Hash)
|
22
|
+
# Extract character types and proportions.
|
23
|
+
char_arrays = arg.keys.map { |t| single_pool(t) }
|
24
|
+
char_props = arg.values.each do |p|
|
25
|
+
raise CharPoolError.new("Invalid char_pool proportion #{p.inspect}") unless
|
26
|
+
p.is_a?(Numeric) && p >= 0
|
27
|
+
end
|
28
|
+
|
29
|
+
# Standardize the proportions if they don't sum to 1.
|
30
|
+
total_prop = char_props.inject(:+).to_f
|
31
|
+
char_props = char_props.map { |prop| prop / total_prop }
|
32
|
+
|
33
|
+
multiple_pool(char_arrays, char_props)
|
34
|
+
else
|
35
|
+
single_pool(arg)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Provides a list of the available built-in character pool types.
|
40
|
+
#
|
41
|
+
# @return [Array<Symbol>] the list of character pool types
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# Flexkey::CharPool.available_char_types
|
45
|
+
def available_char_types
|
46
|
+
character_types.keys
|
47
|
+
end
|
48
|
+
|
49
|
+
# Provides a list of the available built-in character pool types with the characters of each
|
50
|
+
# type.
|
51
|
+
#
|
52
|
+
# @return [Hash{ Symbol => String }] a hash of character pool types and their characters
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# Flexkey::CharPool.available_char_pools
|
56
|
+
def available_char_pools
|
57
|
+
character_types.inject({}) { |acc, (k, v)| acc[k] = v.join; acc }
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def character_types
|
63
|
+
{
|
64
|
+
alpha_upper: 'A'.upto('Z').to_a,
|
65
|
+
alpha_lower: 'a'.upto('z').to_a,
|
66
|
+
numeric: '0'.upto('9').to_a,
|
67
|
+
alpha_upper_clear: 'A'.upto('Z').to_a - 'IOS'.chars.to_a,
|
68
|
+
alpha_lower_clear: 'a'.upto('z').to_a - 'ilos'.chars.to_a,
|
69
|
+
numeric_clear: '2346789'.chars.to_a,
|
70
|
+
symbol: "!@#\$%^&*;:()_+-=[]{}\\|'\",.<>/?".chars.to_a,
|
71
|
+
basic_symbol: '!@#$%^&*;:'.chars.to_a
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
# Return an array of characters from either a custom pool or a built-in type
|
76
|
+
def single_pool(char_type)
|
77
|
+
if char_type.respond_to?(:chars)
|
78
|
+
if char_type.empty?
|
79
|
+
raise CharPoolError.new('A custom char_pool was blank')
|
80
|
+
else
|
81
|
+
char_type.chars.to_a
|
82
|
+
end
|
83
|
+
elsif character_types.keys.include?(char_type)
|
84
|
+
character_types[char_type]
|
85
|
+
else
|
86
|
+
raise CharPoolError.new("Invalid char_pool #{char_type.inspect}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return a flat array of characters from the provided character pools and proportions
|
91
|
+
def multiple_pool(char_arrays, char_props)
|
92
|
+
# Compute LCM of sizes of char type arrays
|
93
|
+
char_array_sizes = char_arrays.map(&:size)
|
94
|
+
char_array_sizes_lcm = char_array_sizes.inject(:lcm)
|
95
|
+
|
96
|
+
# Compute approximate number of multiples needed for each char type array
|
97
|
+
char_array_multiples = char_array_sizes.zip(char_props).map { |arr|
|
98
|
+
(char_array_sizes_lcm / arr[0] * arr[1] * 100).round }
|
99
|
+
|
100
|
+
# Construct combined array of all char types with correct proportions
|
101
|
+
char_array_multiples_gcd = char_array_multiples.inject(:gcd)
|
102
|
+
char_arrays_mult = char_arrays.zip(char_array_multiples).map { |arr|
|
103
|
+
arr[0] * (arr[1] / char_array_multiples_gcd) }
|
104
|
+
|
105
|
+
char_arrays_mult.flatten
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Flexkey
|
2
|
+
class GeneratorError < StandardError; end
|
3
|
+
|
4
|
+
class Generator
|
5
|
+
# @return [String] the format of the keys to be generated
|
6
|
+
attr_accessor :format
|
7
|
+
|
8
|
+
# @return [Hash{ String => Symbol, String }] a pool of available character types specified in
|
9
|
+
# the format
|
10
|
+
attr_accessor :char_pool
|
11
|
+
|
12
|
+
# @return [Fixnum] the number of possible keys for a Flexkey instance with given format and
|
13
|
+
# character pool
|
14
|
+
attr_reader :n_possible_keys
|
15
|
+
|
16
|
+
# Initializes and validates a new Flexkey key generator.
|
17
|
+
#
|
18
|
+
# @param args [Hash{ Symbol => String, Hash{ String => Symbol, String }}] the format of the keys
|
19
|
+
# to be generated and a pool of available character types specified in the format
|
20
|
+
#
|
21
|
+
# @return [Flexkey] the Flexkey instance
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# Flexkey::Generator.new(format: 'nnn-aaa',
|
25
|
+
# char_pool: { 'n' => :numeric, 'a' => :alpha_upper_clear })
|
26
|
+
# Flexkey::Generator.new(format: 'ccccc-ccccc',
|
27
|
+
# char_pool: { 'c' => { alpha_upper: 0.75, alpha_lower: 0.25 } })
|
28
|
+
# Flexkey::Generator.new(format: 'key_#######', char_pool: { '#' => :numeric_clear })
|
29
|
+
# Flexkey::Generator.new(format: 'a-nnnn', char_pool: { 'a' => :alpha_upper, 'n' => '12345' })
|
30
|
+
def initialize(args = {})
|
31
|
+
@format, @char_pool = args[:format], args[:char_pool]
|
32
|
+
validate!
|
33
|
+
set_char_pool
|
34
|
+
calculate_n_possible_keys
|
35
|
+
end
|
36
|
+
|
37
|
+
def format=(new_format)
|
38
|
+
@format = new_format
|
39
|
+
validate!
|
40
|
+
calculate_n_possible_keys
|
41
|
+
end
|
42
|
+
|
43
|
+
def char_pool=(new_char_pool)
|
44
|
+
@char_pool = new_char_pool
|
45
|
+
validate!
|
46
|
+
set_char_pool
|
47
|
+
calculate_n_possible_keys
|
48
|
+
end
|
49
|
+
|
50
|
+
# Generates a single key or an array of `n` keys.
|
51
|
+
#
|
52
|
+
# @param n [Integer] the number of keys to generate
|
53
|
+
#
|
54
|
+
# @return [String] a single key
|
55
|
+
# @return [Array<String>] `n` keys
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# fk = Flexkey::Generator.new(format: 'nnn-aaa',
|
59
|
+
# char_pool: { 'n' => :numeric, 'a' => :alpha_upper_clear })
|
60
|
+
# fk.generate
|
61
|
+
# fk.generate(10)
|
62
|
+
def generate(n = 1)
|
63
|
+
validate_n!(n)
|
64
|
+
|
65
|
+
if n == 1
|
66
|
+
generate_one
|
67
|
+
elsif n > 1
|
68
|
+
keys = []
|
69
|
+
new_key = nil
|
70
|
+
|
71
|
+
n.times do
|
72
|
+
loop do
|
73
|
+
new_key = generate_one
|
74
|
+
break unless keys.include?(new_key)
|
75
|
+
end
|
76
|
+
|
77
|
+
keys << new_key
|
78
|
+
end
|
79
|
+
|
80
|
+
keys
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def inspect
|
85
|
+
%Q{#<#{self.class} @format="#{format}", @char_pool=#{@char_pool}, } +
|
86
|
+
%Q{@n_possible_keys=#{@n_possible_keys}>}
|
87
|
+
end
|
88
|
+
alias to_s inspect
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def validate!
|
93
|
+
raise GeneratorError.new('format is required') if @format.nil? || @format.empty?
|
94
|
+
raise GeneratorError.new('char_pool is required') if @char_pool.nil? || @char_pool.empty?
|
95
|
+
raise GeneratorError.new('char_pool letters must each be strings of length 1') unless
|
96
|
+
@char_pool.keys.all? { |letter| letter.is_a?(String) && letter.length == 1 }
|
97
|
+
raise GeneratorError.new('No char_pool letters present in format') if
|
98
|
+
(@format.chars.to_a & @char_pool.keys).empty?
|
99
|
+
end
|
100
|
+
|
101
|
+
def set_char_pool
|
102
|
+
@_char_pool = @char_pool.inject({}) { |h, (k, v)| h[k] = CharPool.generate(v); h }
|
103
|
+
end
|
104
|
+
|
105
|
+
def calculate_n_possible_keys
|
106
|
+
@n_possible_keys = @format.chars.to_a.map do |c|
|
107
|
+
@_char_pool[c].nil? ? 1 : @_char_pool[c].size
|
108
|
+
end.inject(:*)
|
109
|
+
end
|
110
|
+
|
111
|
+
def generate_one
|
112
|
+
@format.chars.to_a.map { |c| @_char_pool[c].nil? ? c : @_char_pool[c].sample }.join
|
113
|
+
end
|
114
|
+
|
115
|
+
def validate_n!(n)
|
116
|
+
raise GeneratorError.new("There are only #{@n_possible_keys} possible keys") if
|
117
|
+
n > @n_possible_keys
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CharPoolSpecHelpers
|
4
|
+
def cpj(arg)
|
5
|
+
Flexkey::CharPool.generate(arg).join
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Flexkey::CharPool do
|
10
|
+
include CharPoolSpecHelpers
|
11
|
+
|
12
|
+
describe 'utility methods' do
|
13
|
+
it 'should provide a list of available character types' do
|
14
|
+
expect(Flexkey::CharPool.available_char_types).to eq([
|
15
|
+
:alpha_upper, :alpha_lower, :numeric, :alpha_upper_clear, :alpha_lower_clear,
|
16
|
+
:numeric_clear, :symbol, :basic_symbol])
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should provide a list of character pools for available character types' do
|
20
|
+
expect(Flexkey::CharPool.available_char_pools).to eq({
|
21
|
+
alpha_upper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
22
|
+
alpha_lower: 'abcdefghijklmnopqrstuvwxyz',
|
23
|
+
numeric: '0123456789',
|
24
|
+
alpha_upper_clear: 'ABCDEFGHJKLMNPQRTUVWXYZ',
|
25
|
+
alpha_lower_clear: 'abcdefghjkmnpqrtuvwxyz',
|
26
|
+
numeric_clear: '2346789',
|
27
|
+
symbol: "!@\#$%^&*;:()_+-=[]{}\\|'\",.<>/?",
|
28
|
+
basic_symbol: "!@\#$%^&*;:"
|
29
|
+
})
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when a single type is requested' do
|
34
|
+
describe 'validations' do
|
35
|
+
it 'raises an exception for an unknown character pool symbol' do
|
36
|
+
expect {
|
37
|
+
Flexkey::CharPool.generate(:bad)
|
38
|
+
}.to raise_error(Flexkey::CharPoolError, 'Invalid char_pool :bad')
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'raises an exception for an nil character pool symbol' do
|
42
|
+
expect {
|
43
|
+
Flexkey::CharPool.generate(nil)
|
44
|
+
}.to raise_error(Flexkey::CharPoolError, 'Invalid char_pool nil')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'raises an exception for a non-symbol type' do
|
48
|
+
expect {
|
49
|
+
Flexkey::CharPool.generate(123)
|
50
|
+
}.to raise_error(Flexkey::CharPoolError, 'Invalid char_pool 123')
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'raises an exception for a blank custom type' do
|
54
|
+
expect {
|
55
|
+
Flexkey::CharPool.generate('')
|
56
|
+
}.to raise_error(Flexkey::CharPoolError, 'A custom char_pool was blank')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'generates an uppercase alphabetical character pool' do
|
61
|
+
expect(cpj(:alpha_upper)).to eq('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'generates a lowercase alphabetical character pool' do
|
65
|
+
expect(cpj(:alpha_lower)).to eq('abcdefghijklmnopqrstuvwxyz')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'generates a numeric character pool' do
|
69
|
+
expect(cpj(:numeric)).to eq('0123456789')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'generates an uppercase and clear alphabetical character pool' do
|
73
|
+
expect(cpj(:alpha_upper_clear)).to eq('ABCDEFGHJKLMNPQRTUVWXYZ')
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'generates a lowercase and clear alphabetical character pool' do
|
77
|
+
expect(cpj(:alpha_lower_clear)).to eq('abcdefghjkmnpqrtuvwxyz')
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'generates a numeric and clear character pool' do
|
81
|
+
expect(cpj(:numeric_clear)).to eq('2346789')
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'generates a full symbol character pool' do
|
85
|
+
expect(cpj(:symbol)).to eq("!@#\$%^&*;:()_+-=[]{}\\|'\",.<>/?")
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'generates a basic symbol character pool' do
|
89
|
+
expect(cpj(:basic_symbol)).to eq('!@#$%^&*;:')
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'generates a custom character pool' do
|
93
|
+
expect(cpj('ABC123')).to eq('ABC123')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'when multiple types are requested' do
|
98
|
+
describe 'validations' do
|
99
|
+
it 'raises an exception for an unknown character pool symbol' do
|
100
|
+
expect {
|
101
|
+
Flexkey::CharPool.generate({ alpha_upper: 0.5, bad: 0.5 })
|
102
|
+
}.to raise_error(Flexkey::CharPoolError, 'Invalid char_pool :bad')
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'raises an exception for a non-symbol type' do
|
106
|
+
expect {
|
107
|
+
Flexkey::CharPool.generate({ alpha_upper: 0.5, 123 => 0.5 })
|
108
|
+
}.to raise_error(Flexkey::CharPoolError, 'Invalid char_pool 123')
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'raises an exception for a negative proportion' do
|
112
|
+
expect {
|
113
|
+
Flexkey::CharPool.generate({ alpha_upper: 0.5, numeric: -0.5 })
|
114
|
+
}.to raise_error(Flexkey::CharPoolError, 'Invalid char_pool proportion -0.5')
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'raises an exception for a non-numeric proportion' do
|
118
|
+
expect {
|
119
|
+
Flexkey::CharPool.generate({ alpha_upper: 0.5, numeric: 'test' })
|
120
|
+
}.to raise_error(Flexkey::CharPoolError, 'Invalid char_pool proportion "test"')
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'raises an exception for missing proportion' do
|
124
|
+
expect {
|
125
|
+
Flexkey::CharPool.generate({ alpha_upper: 0.5, numeric: nil })
|
126
|
+
}.to raise_error(Flexkey::CharPoolError, 'Invalid char_pool proportion nil')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'generates a pool of upper and lowercase alphabetical types in equal proportions' do
|
131
|
+
expect(cpj({ alpha_upper: 0.5, alpha_lower: 0.5 })).to eq(
|
132
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'generates a pool of upper and lowercase alphabetical types in a 1:1 proportion' do
|
136
|
+
expect(cpj({ alpha_upper: 1, alpha_lower: 1 })).to eq(
|
137
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'generates a pool of upper and lowercase alphabetical types in a 3:1 proportion' do
|
141
|
+
expect(cpj({ alpha_upper: 0.75, alpha_lower: 0.25 })).to eq(
|
142
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ' +
|
143
|
+
'abcdefghijklmnopqrstuvwxyz')
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'generates a pool of upper and lowercase alphabetical types in a 3:1 proportion' do
|
147
|
+
expect(cpj({ alpha_upper: 3, alpha_lower: 1 })).to eq(
|
148
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ' +
|
149
|
+
'abcdefghijklmnopqrstuvwxyz')
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'generates a pool of upper and lowercase alphabetical types in a 1:3 proportion' do
|
153
|
+
expect(cpj({ alpha_upper: 0.25, alpha_lower: 0.75 })).to eq(
|
154
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz' +
|
155
|
+
'abcdefghijklmnopqrstuvwxyz')
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'generates a pool of upper and lowercase alphabetical types in a 0:1 proportion' do
|
159
|
+
expect(cpj({ alpha_upper: 0, alpha_lower: 99 })).to eq('abcdefghijklmnopqrstuvwxyz')
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'generates a pool of upper and lowercase alphabetical types in a 0.2:0.2 proportion' do
|
163
|
+
expect(cpj({ alpha_upper: 0.2, alpha_lower: 0.2 })).to eq(
|
164
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'generates a pool of uppercase and numeric character types in equal proportions' do
|
168
|
+
expect(cpj({ alpha_upper: 0.5, numeric: 0.5 })).to eq(
|
169
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ' +
|
170
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ0' +
|
171
|
+
'1234567890123456789012345678901234567890123456789012345678901234567890123456789' +
|
172
|
+
'01234567890123456789012345678901234567890123456789')
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'generates a pool of numeric and a custom type in equal proportions' do
|
176
|
+
expect(cpj({ numeric: 0.5, 'ABCDE' => 0.5 })).to eq('0123456789ABCDEABCDE')
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'generates a pool of uppercase and another custom type in equal proportions' do
|
180
|
+
expect(cpj({ alpha_upper: 1, '0123456789!@#' => 1 })).to eq(
|
181
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#0123456789!@#')
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'generates a pool of two custom types in a 1:4 proportion' do
|
185
|
+
expect(cpj({ 'ABCD' => 1, 'WXYZ' => 4 })).to eq('ABCDWXYZWXYZWXYZWXYZ')
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Flexkey::Generator do
|
4
|
+
describe '#new' do
|
5
|
+
describe 'validations' do
|
6
|
+
it 'raises an exception when format is missing' do
|
7
|
+
expect {
|
8
|
+
Flexkey::Generator.new(char_pool: { 'n' => :numeric })
|
9
|
+
}.to raise_error(Flexkey::GeneratorError, 'format is required')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'raises an exception when format is blank' do
|
13
|
+
expect {
|
14
|
+
Flexkey::Generator.new(format: '', char_pool: { 'n' => :numeric })
|
15
|
+
}.to raise_error(Flexkey::GeneratorError, 'format is required')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'raises an exception when char_pool is missing' do
|
19
|
+
expect {
|
20
|
+
Flexkey::Generator.new(format: 'nnn')
|
21
|
+
}.to raise_error(Flexkey::GeneratorError, 'char_pool is required')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'raises an exception when char_pool has non-string letters' do
|
25
|
+
expect {
|
26
|
+
Flexkey::Generator.new(format: 'nnn', char_pool: { 123 => :numeric })
|
27
|
+
}.to raise_error(Flexkey::GeneratorError,
|
28
|
+
'char_pool letters must each be strings of length 1')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'raises an exception when char_pool has letters longer than 1 character' do
|
32
|
+
expect {
|
33
|
+
Flexkey::Generator.new(format: 'nnn', char_pool: { 'nn' => :numeric })
|
34
|
+
}.to raise_error(Flexkey::GeneratorError,
|
35
|
+
'char_pool letters must each be strings of length 1')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'raises an exception when char_pool has blank letters' do
|
39
|
+
expect {
|
40
|
+
Flexkey::Generator.new(format: 'nnn', char_pool: { '' => :numeric })
|
41
|
+
}.to raise_error(Flexkey::GeneratorError,
|
42
|
+
'char_pool letters must each be strings of length 1')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'raises an exception when no char_pool letters found in format' do
|
46
|
+
expect {
|
47
|
+
Flexkey::Generator.new(format: 'nnn', char_pool: { 'a' => :numeric })
|
48
|
+
}.to raise_error(Flexkey::GeneratorError, 'No char_pool letters present in format')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when a valid format and char_pool is provided' do
|
53
|
+
subject { Flexkey::Generator.new(format: 'nnn', char_pool: { 'n' => :numeric }) }
|
54
|
+
|
55
|
+
its(:format) { is_expected.to eq('nnn') }
|
56
|
+
its(:char_pool) { is_expected.to eq({ 'n' => :numeric }) }
|
57
|
+
its(:n_possible_keys) { is_expected.to eq(10**3) }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'accessors' do
|
62
|
+
let(:key_gen) {
|
63
|
+
Flexkey::Generator.new(format: 'nnn-aaa', char_pool: {
|
64
|
+
'n' => :numeric, 'a' => :alpha_upper }) }
|
65
|
+
|
66
|
+
it 'updates the format' do
|
67
|
+
key_gen.format = 'aaa-nnn'
|
68
|
+
expect(key_gen.format).to eq('aaa-nnn')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'updates the char_pool' do
|
72
|
+
key_gen.char_pool = { 'n' => :numeric, 'a' => :alpha_lower }
|
73
|
+
expect(key_gen.char_pool).to eq({ 'n' => :numeric, 'a' => :alpha_lower })
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'updates the number of possible keys' do
|
77
|
+
key_gen.format = 'aaaa-nnnn-0000'
|
78
|
+
key_gen.char_pool = { 'n' => :numeric, 'a' => :alpha_lower_clear }
|
79
|
+
expect(key_gen.n_possible_keys).to eq(10**4 * 22**4)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#generate' do
|
84
|
+
context 'when a single key is requested' do
|
85
|
+
context 'with alphabetical and numeric character types' do
|
86
|
+
subject { Flexkey::Generator.new(format: 'aaaaa-nnnn', char_pool: {
|
87
|
+
'a' => :alpha_lower, 'n' => :numeric }).generate }
|
88
|
+
|
89
|
+
it { is_expected.not_to be_nil }
|
90
|
+
it { is_expected.to match(/^[a-z]{5}-\d{4}$/) }
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'with all character types' do
|
94
|
+
subject { Flexkey::Generator.new(format: 'u-l-n-v-m-o-s-b', char_pool: {
|
95
|
+
'u' => :alpha_upper, 'l' => :alpha_lower, 'n' => :numeric, 'v' => :alpha_upper_clear,
|
96
|
+
'm' => :alpha_lower_clear, 'o' => :numeric_clear, 's' => :symbol, 'b' => :basic_symbol
|
97
|
+
}).generate }
|
98
|
+
|
99
|
+
it { is_expected.to match(%r{
|
100
|
+
^[A-Z]-[a-z]-[0-9]-[ABCDEFGHJKLMNPQRTUVWXYZ]-[abcdefghjkmnpqrtuvwxyz]-
|
101
|
+
[2346789]-[!@#\$%\^&*;:\(\)_\+-=\[\]{}\\\|'",.<>\/?]-[!@#\$%\^&\*;\:]$
|
102
|
+
}x) }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'when the format and char_pool change' do
|
107
|
+
let(:key_gen) { Flexkey::Generator.new(format: 'aaa-nnn', char_pool: {
|
108
|
+
'n' => :numeric, 'a' => :alpha_upper }) }
|
109
|
+
|
110
|
+
it 'generates a key with the new format and char_pool' do
|
111
|
+
key_gen.format = 'nnn-aaa'
|
112
|
+
key_gen.char_pool = { 'n' => :numeric, 'a' => :alpha_lower }
|
113
|
+
expect(key_gen.generate).to match(/^\d{3}-[a-z]{3}$/)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when multiple keys are requested' do
|
118
|
+
let(:new_keys) { Flexkey::Generator.new(format: 'aaaaa-nnnn/cc', char_pool: {
|
119
|
+
'a' => :alpha_lower, 'n' => :numeric, 'c' => 'LMN' }).generate(5) }
|
120
|
+
|
121
|
+
it 'generates the specified number of keys' do
|
122
|
+
expect(new_keys.size).to eq(5)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'generates unique keys' do
|
126
|
+
expect(new_keys.uniq.size).to eq(5)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'generates keys with the specified format' do
|
130
|
+
expect(new_keys.count { |key| key =~ /^[a-z]{5}-\d{4}\/[LMN]{2}$/ }).to eq(5)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'raises an exception when too many keys are requested' do
|
135
|
+
expect {
|
136
|
+
Flexkey::Generator.new(format: 'nnn', char_pool: { 'n' => :numeric }).generate(1001)
|
137
|
+
}.to raise_error(Flexkey::GeneratorError, "There are only 1000 possible keys")
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'allows inspection of an instance' do
|
142
|
+
fk = Flexkey::Generator.new(format: 'nnn', char_pool: { 'n' => :numeric })
|
143
|
+
expect(fk.inspect).to eq(%q{#<Flexkey::Generator @format="nnn", @char_pool={"n"=>:numeric}, } +
|
144
|
+
%q{@n_possible_keys=1000>})
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'displays an instance as a string' do
|
148
|
+
fk = Flexkey::Generator.new(format: 'nnn', char_pool: { 'n' => :numeric })
|
149
|
+
expect(fk.to_s).to eq(%q{#<Flexkey::Generator @format="nnn", @char_pool={"n"=>:numeric}, } +
|
150
|
+
%q{@n_possible_keys=1000>})
|
151
|
+
end
|
152
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flexkey
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Devin McCabe
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-06-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
type: :development
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
none: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.3'
|
28
|
+
none: false
|
29
|
+
prerelease: false
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
type: :development
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
none: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
none: false
|
45
|
+
prerelease: false
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
type: :development
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
none: false
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
none: false
|
61
|
+
prerelease: false
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec-its
|
64
|
+
type: :development
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
none: false
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
none: false
|
77
|
+
prerelease: false
|
78
|
+
description: Flexible product key generation
|
79
|
+
email:
|
80
|
+
- devin.mccabe@gmail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- .yardopts
|
87
|
+
- Gemfile
|
88
|
+
- LICENSE.txt
|
89
|
+
- README.md
|
90
|
+
- Rakefile
|
91
|
+
- flexkey.gemspec
|
92
|
+
- lib/flexkey.rb
|
93
|
+
- lib/flexkey/char_pool.rb
|
94
|
+
- lib/flexkey/generator.rb
|
95
|
+
- lib/flexkey/version.rb
|
96
|
+
- spec/flexkey/char_pool_spec.rb
|
97
|
+
- spec/flexkey/generator_spec.rb
|
98
|
+
- spec/spec_helper.rb
|
99
|
+
homepage: https://github.com/dpmccabe/flexkey
|
100
|
+
licenses:
|
101
|
+
- MIT
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
hash: 3377894280690752207
|
113
|
+
version: '0'
|
114
|
+
none: false
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
segments:
|
120
|
+
- 0
|
121
|
+
hash: 3377894280690752207
|
122
|
+
version: '0'
|
123
|
+
none: false
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 1.8.23
|
127
|
+
signing_key:
|
128
|
+
specification_version: 3
|
129
|
+
summary: Use Flexkey to generate random product keys for use in software licenses,
|
130
|
+
invoices, etc.
|
131
|
+
test_files:
|
132
|
+
- spec/flexkey/char_pool_spec.rb
|
133
|
+
- spec/flexkey/generator_spec.rb
|
134
|
+
- spec/spec_helper.rb
|