human_readable 0.8.0 → 0.9.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.
- 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
|
+
[](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: []
|