human_readable 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +31 -11
- data/lib/human_readable.rb +110 -49
- data/lib/human_readable/version.rb +1 -1
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0c42d3ef19502845eba7a407bd1b341c10598cf4625a748c740eec35467e572
|
4
|
+
data.tar.gz: 3b1f3ea6ce717b68854f5f7c30e43a27c988fa6c4f863140c36f5cab66d962d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78ab8e290c1cf235581efb86d42357a55f1c8972fd5a1d642c908291dcd6b7f8656ec4988579017c13bb20bd7487a75e30b9963fdc2ffef83d80da30b40fa25b
|
7
|
+
data.tar.gz: bec6d603d7670e5fd47e5f8ccea02d5a65fc6fda31dbc78df0dd3d86302516cacb265ab8758505550b39654ae62f61bfe800d14d592dcb74a4c44cf3f313e2ac
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/human_readable.svg)](https://badge.fury.io/rb/human_readable)
|
2
|
+
|
1
3
|
# HumanReadable
|
2
4
|
|
3
|
-
Human readable random tokens
|
5
|
+
Human readable random tokens without ambiguous characters, and optional Emoji support.
|
4
6
|
|
5
7
|
Focus is readability in poor conditions or from potentially damaged printed documents rather than cryptographic uses.
|
6
8
|
Despite this focus, SecureRandom is used to help avoid collisions.
|
@@ -28,27 +30,45 @@ Or install it yourself as:
|
|
28
30
|
## Usage
|
29
31
|
|
30
32
|
For 10 characters of the default character set, use `HumanReadable.generate`.
|
31
|
-
For other lengths (2..x), use `HumanReadable.generate(output_size: 50)
|
33
|
+
For other lengths (2..x), use `HumanReadable.generate(output_size: 50)`, or change `output_size` in the configuration.
|
32
34
|
|
33
35
|
## Configuration
|
34
36
|
|
35
|
-
*
|
36
|
-
* To include non-default characters
|
37
|
+
* Add or change substitutions by configuring `substitution_hash`
|
38
|
+
* To include non-default characters without substitution, configure `extend_chars`
|
39
|
+
* To exclude default characters, configure `exclude_chars`
|
37
40
|
* Inspect available characters using `HumanReadable.charset`
|
38
|
-
* For convenience, numbers and symbols are allowed in
|
41
|
+
* For convenience, numbers and symbols are allowed in `substitution_hash` and are translated to characters during usage
|
39
42
|
|
40
|
-
**CAUTION:** Changing
|
43
|
+
**CAUTION:** Changing available characters alters the check character, invalidating previous tokens.
|
41
44
|
|
42
45
|
|
43
46
|
HumanReadable.configure do |c|
|
44
|
-
|
47
|
+
c.substitution_hash = { %w[I L] => 1, O: 0, U: :V } # Default
|
48
|
+
c.output_size = 10 # Default
|
45
49
|
|
46
|
-
#
|
50
|
+
# Add or change substitutions
|
47
51
|
c.substitution_hash[:B] = 8
|
48
52
|
c.substitution_hash[:U] = nil
|
49
|
-
c.substitution_hash['$'] = '$'
|
50
53
|
# or equivalently
|
51
|
-
c.substitution_hash = { I
|
54
|
+
c.substitution_hash = { %w[I L] => 1, O: 0, U: nil, B: 8}
|
55
|
+
|
56
|
+
# Extend charset when no substitution is needed
|
57
|
+
c.extend_chars << %w[~ ! @ $]
|
58
|
+
|
59
|
+
# Exclude from charset
|
60
|
+
c.exclude_chars = %w[X Y Z]
|
61
|
+
|
62
|
+
# Supports Emoji!!
|
63
|
+
c.extend_chars << %w[⛰️ 🧻 ✂️ 🦎 🖖]
|
64
|
+
c.substitution_hash['🖤'] = '❤️'
|
65
|
+
|
66
|
+
# And understands skin tones
|
67
|
+
c.remove_skin_tones = false # Default
|
68
|
+
c.substitution_hash[%w[👍🏻 👍🏼 👍🏽 👍🏾 👍🏿]] = '👍'
|
69
|
+
# -or-
|
70
|
+
c.remove_skin_tones = true
|
71
|
+
c.extend_chars << '👍'
|
52
72
|
end
|
53
73
|
|
54
74
|
## Development
|
@@ -59,7 +79,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
59
79
|
|
60
80
|
## Contributing
|
61
81
|
|
62
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/MacksMind/human_readable
|
82
|
+
Bug reports and pull requests are welcome on GitHub at <https://github.com/MacksMind/human_readable>.
|
63
83
|
|
64
84
|
|
65
85
|
## License
|
data/lib/human_readable.rb
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
# Copyright 2020 Mack Earnhardt
|
4
4
|
|
5
|
-
|
5
|
+
require_relative 'human_readable/version'
|
6
6
|
require 'ostruct'
|
7
7
|
require 'securerandom'
|
8
8
|
|
9
|
-
# Human readable random tokens
|
9
|
+
# Human readable random tokens without ambiguous characters, and optional Emoji support
|
10
10
|
module HumanReadable
|
11
11
|
# +#generate+ output_size must be >= 2 due to check character
|
12
12
|
class MinSizeTwo < StandardError; end
|
@@ -15,34 +15,54 @@ module HumanReadable
|
|
15
15
|
# Yields block for configuration
|
16
16
|
#
|
17
17
|
# HumanReadable.configure do |c|
|
18
|
+
# c.substitution_hash = { %w[I L] => 1, O: 0, U: :V } # Default
|
19
|
+
# c.output_size = 10 # Default
|
20
|
+
#
|
21
|
+
# # Substitution hash
|
18
22
|
# c.substitution_hash[:B] = 8
|
19
23
|
# c.substitution_hash[:U] = nil
|
20
|
-
# c.substitution_hash['$'] = '$'
|
21
24
|
# # or equivalently
|
22
|
-
# c.substitution_hash = { I
|
23
|
-
#
|
25
|
+
# c.substitution_hash = { %w[I L] => 1, O: 0, U: nil, B: 8}
|
26
|
+
#
|
27
|
+
# # Extend charset
|
28
|
+
# c.extend_chars = %w[~ ! @ $]
|
24
29
|
#
|
25
|
-
#
|
26
|
-
#
|
30
|
+
# # Exclude charset
|
31
|
+
# c.exclude_chars = %w[X Y Z]
|
32
|
+
#
|
33
|
+
# # Supports Emoji!!
|
34
|
+
# c.extend_chars << %w[⛰️ 🧻 ✂️ 🦎 🖖]
|
35
|
+
# c.substitution_hash['🖤'] = '❤️'
|
36
|
+
#
|
37
|
+
# # And understands skin tones
|
38
|
+
# c.remove_skin_tones = false # Default
|
39
|
+
# c.substitution_hash[%w[👍🏻 👍🏼 👍🏽 👍🏾 👍🏿]] = '👍'
|
40
|
+
# # -or-
|
41
|
+
# c.remove_skin_tones = true
|
42
|
+
# c.extend_chars << '👍'
|
43
|
+
# end
|
27
44
|
#
|
28
45
|
# Specified keys won't be used during generation, and values will be substituted during
|
29
46
|
# validation, increasing the likelihood that a misread character can be restored. Extend
|
30
|
-
# or replace the substitutions to
|
47
|
+
# or replace the substitutions to alter the character set. For convenience, digits
|
31
48
|
# and symbols are allowed in the hash and are translated to characters during usage.
|
32
|
-
# Alter as needed per examples below.
|
33
49
|
#
|
34
|
-
#
|
50
|
+
# @note Changing substitution_hash keys alters the check character, invalidating previous tokens.
|
51
|
+
# @return [nil]
|
35
52
|
def configure
|
36
53
|
yield(configuration)
|
54
|
+
nil
|
37
55
|
end
|
38
56
|
|
39
57
|
# Generates a random token of the requested size
|
40
58
|
#
|
41
|
-
# Minimum size is 2 since the last character is a check character
|
42
|
-
|
59
|
+
# @note Minimum size is 2 since the last character is a check character
|
60
|
+
# @param output_size [Integer] desired number of printable characters
|
61
|
+
# @return [String] random token with check character
|
62
|
+
def generate(output_size: configuration.output_size)
|
43
63
|
raise(MinSizeTwo) if output_size < 2
|
44
64
|
|
45
|
-
|
65
|
+
"#{token = generate_random(output_size - 1)}#{check_character(token)}"
|
46
66
|
end
|
47
67
|
|
48
68
|
# Clean and validate a candidate token
|
@@ -52,11 +72,16 @@ module HumanReadable
|
|
52
72
|
# * Remove characters not in available character set
|
53
73
|
# * Validates the check character
|
54
74
|
#
|
55
|
-
#
|
75
|
+
# @param input [String] the candidate token
|
76
|
+
# @return [String, nil] possibly modified token if valid, else nil
|
56
77
|
def valid_token?(input)
|
57
78
|
return unless input.is_a?(String)
|
58
79
|
|
59
|
-
codepoints =
|
80
|
+
codepoints =
|
81
|
+
input.upcase.each_grapheme_cluster.map do |c|
|
82
|
+
c.gsub!(SKIN_TONE_REGEXP, '') if configuration.remove_skin_tones
|
83
|
+
charset_hash[validation_hash[c] || c]
|
84
|
+
end
|
60
85
|
codepoints.compact!
|
61
86
|
|
62
87
|
return if codepoints.size < 2
|
@@ -72,18 +97,53 @@ module HumanReadable
|
|
72
97
|
|
73
98
|
# Characters available for token generation
|
74
99
|
#
|
75
|
-
#
|
100
|
+
# DEFAULT: Digits 0-9 and uppercase letters A-Z except for ILOU
|
76
101
|
#
|
77
|
-
#
|
102
|
+
# @note Manipulate via {#configure}
|
103
|
+
# @return [Array] of available characters
|
78
104
|
def charset
|
79
|
-
@charset ||=
|
105
|
+
@charset ||=
|
106
|
+
begin
|
107
|
+
array = (
|
108
|
+
('0'..'9').to_a +
|
109
|
+
('A'..'Z').to_a +
|
110
|
+
extend_chars -
|
111
|
+
exclude_chars -
|
112
|
+
validation_hash.keys +
|
113
|
+
validation_hash.values
|
114
|
+
)
|
115
|
+
array.uniq!
|
116
|
+
array.sort!
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Reset configuration and memoizations
|
121
|
+
#
|
122
|
+
# @return [Array] list of variables reset
|
123
|
+
def reset
|
124
|
+
instance_variables.each { |sym| remove_instance_variable(sym) }
|
80
125
|
end
|
81
126
|
|
82
127
|
private
|
83
128
|
|
129
|
+
SKIN_TONE_REGEXP = /[🏻🏼🏽🏾🏿]/.freeze
|
130
|
+
|
131
|
+
# HumanReadable configuration
|
132
|
+
Configuration = Struct.new(
|
133
|
+
:substitution_hash,
|
134
|
+
:extend_chars,
|
135
|
+
:exclude_chars,
|
136
|
+
:output_size,
|
137
|
+
:remove_skin_tones
|
138
|
+
)
|
139
|
+
|
84
140
|
def configuration
|
85
|
-
@configuration ||=
|
86
|
-
|
141
|
+
@configuration ||= Configuration.new(
|
142
|
+
{ %w[I L] => 1, O: 0, U: :V },
|
143
|
+
[],
|
144
|
+
[],
|
145
|
+
10,
|
146
|
+
false
|
87
147
|
)
|
88
148
|
end
|
89
149
|
|
@@ -99,8 +159,6 @@ module HumanReadable
|
|
99
159
|
# Instead we attempt to optimize the number of bytes generated with each
|
100
160
|
# call to SecureRandom.
|
101
161
|
def generate_random(random_size)
|
102
|
-
return '' unless random_size.positive?
|
103
|
-
|
104
162
|
codepoints = []
|
105
163
|
|
106
164
|
while codepoints.size < random_size
|
@@ -123,11 +181,11 @@ module HumanReadable
|
|
123
181
|
|
124
182
|
# Compute check character using Luhn mod N algorithm
|
125
183
|
#
|
126
|
-
#
|
184
|
+
# CAUTION: Changing charset alters the output
|
127
185
|
def check_character(input)
|
128
186
|
array =
|
129
|
-
input.
|
130
|
-
d =
|
187
|
+
input.each_grapheme_cluster.to_a.reverse.each_with_index.map do |c, i|
|
188
|
+
d = charset_hash[c]
|
131
189
|
d *= 2 if i.even?
|
132
190
|
d / charset_size + d % charset_size
|
133
191
|
end
|
@@ -159,36 +217,39 @@ module HumanReadable
|
|
159
217
|
@charset_size ||= charset.size
|
160
218
|
end
|
161
219
|
|
162
|
-
def
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
array.map!(&:upcase)
|
169
|
-
end
|
220
|
+
def char_cleanup(array)
|
221
|
+
array.compact!
|
222
|
+
array.flatten!
|
223
|
+
array.map!(&:to_s)
|
224
|
+
array.map! { |element| element.gsub(SKIN_TONE_REGEXP, '') } if configuration.remove_skin_tones
|
225
|
+
array.map!(&:upcase)
|
170
226
|
end
|
171
227
|
|
172
|
-
def
|
173
|
-
@
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
end
|
228
|
+
def extend_chars
|
229
|
+
@extend_chars ||= char_cleanup(configuration.extend_chars)
|
230
|
+
end
|
231
|
+
|
232
|
+
def exclude_chars
|
233
|
+
@exclude_chars ||= char_cleanup(
|
234
|
+
configuration.exclude_chars + configuration.substitution_hash.each.map { |k, v| k if v.nil? }
|
235
|
+
)
|
181
236
|
end
|
182
237
|
|
183
|
-
|
184
|
-
|
238
|
+
# Flattened version of substitution_hash
|
239
|
+
def validation_hash
|
240
|
+
@validation_hash ||=
|
185
241
|
begin
|
186
|
-
array =
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
array
|
242
|
+
array =
|
243
|
+
configuration.substitution_hash.map do |k, v|
|
244
|
+
(k.is_a?(Array) ? k.map { |k1| [k1, v] } : [k, v]) unless v.nil?
|
245
|
+
end
|
246
|
+
array = char_cleanup(array)
|
247
|
+
Hash[*array]
|
191
248
|
end
|
192
249
|
end
|
250
|
+
|
251
|
+
def charset_hash
|
252
|
+
@charset_hash ||= Hash[charset.each_with_index.map { |char, i| [char, i] }]
|
253
|
+
end
|
193
254
|
end
|
194
255
|
end
|
metadata
CHANGED
@@ -1,20 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: human_readable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mack Earnhardt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |
|
14
|
-
Human readable random tokens
|
15
|
-
|
16
|
-
|
17
|
-
and validates using a checksum.
|
14
|
+
Human readable random tokens without ambiguous characters, and optional Emoji support.
|
15
|
+
Translates invalid characters to their most likely original value
|
16
|
+
and validates using a check character.
|
18
17
|
email:
|
19
18
|
- mack@agilereasoning.com
|
20
19
|
executables: []
|
@@ -41,15 +40,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
40
|
requirements:
|
42
41
|
- - ">="
|
43
42
|
- !ruby/object:Gem::Version
|
44
|
-
version: 2.
|
43
|
+
version: 2.5.0
|
45
44
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
45
|
requirements:
|
47
46
|
- - ">="
|
48
47
|
- !ruby/object:Gem::Version
|
49
48
|
version: '0'
|
50
49
|
requirements: []
|
51
|
-
rubygems_version: 3.
|
50
|
+
rubygems_version: 3.0.3
|
52
51
|
signing_key:
|
53
52
|
specification_version: 4
|
54
|
-
summary: Human readable random tokens
|
53
|
+
summary: Human readable random tokens without ambiguous characters, and optional Emoji
|
54
|
+
support.
|
55
55
|
test_files: []
|