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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5307eeacfd4961a43325a641b37da77388aedcb419f1c2bd0ae1021eec63417b
4
- data.tar.gz: 44414551a3e4333ab02beb913c69bf8afddce406b2e1701c2f7d79e1150a464d
3
+ metadata.gz: c0c42d3ef19502845eba7a407bd1b341c10598cf4625a748c740eec35467e572
4
+ data.tar.gz: 3b1f3ea6ce717b68854f5f7c30e43a27c988fa6c4f863140c36f5cab66d962d7
5
5
  SHA512:
6
- metadata.gz: 1662e91b1671caf3ac01da210016eb0544d6b82a9eec0f93279eaa9460472bea09fa8ba078d5204551dbb948c81c0a9f874a62d4001c41cfe9cd8c7c2dda38a0
7
- data.tar.gz: b728158418c8b4ea875dc80cccc4055107fbf48014821c325b8e76dfbe6c63f38394d57544c8f13909c376e47f45b19e812fc3ca100994ca2a0a42a2d12eaef9
6
+ metadata.gz: 78ab8e290c1cf235581efb86d42357a55f1c8972fd5a1d642c908291dcd6b7f8656ec4988579017c13bb20bd7487a75e30b9963fdc2ffef83d80da30b40fa25b
7
+ data.tar.gz: bec6d603d7670e5fd47e5f8ccea02d5a65fc6fda31dbc78df0dd3d86302516cacb265ab8758505550b39654ae62f61bfe800d14d592dcb74a4c44cf3f313e2ac
@@ -1,5 +1,10 @@
1
1
  Release History
2
2
  ===============
3
+ # 0.9.0
4
+ * Emoji support!!
5
+ * Minimum Ruby version bumped to 2.5.0 for Emoji support
6
+ * Extend or exclude available characters using config arrays
7
+ * Allow arrays in substitution keys
3
8
 
4
9
  # 0.8.0
5
10
  * Initial release
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 with limited ambiguous characters.
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
- * Change available characters and substitution by manipulating `substitution_hash`
36
- * To include non-default characters, add a self-reference to the hash
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 the hash and are translated to characters during usage
41
+ * For convenience, numbers and symbols are allowed in `substitution_hash` and are translated to characters during usage
39
42
 
40
- **CAUTION:** Changing `substitution_hash` keys alters the check character, invalidating previous tokens.
43
+ **CAUTION:** Changing available characters alters the check character, invalidating previous tokens.
41
44
 
42
45
 
43
46
  HumanReadable.configure do |c|
44
- # Default: substitution_hash = { I: 1, L: 1, O: 0, U: :V }
47
+ c.substitution_hash = { %w[I L] => 1, O: 0, U: :V } # Default
48
+ c.output_size = 10 # Default
45
49
 
46
- # Modifications
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: 1, L: 1, O: 0, U: nil, B: 8, '$' => '$'}
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
@@ -2,11 +2,11 @@
2
2
 
3
3
  # Copyright 2020 Mack Earnhardt
4
4
 
5
- require 'human_readable/version'
5
+ require_relative 'human_readable/version'
6
6
  require 'ostruct'
7
7
  require 'securerandom'
8
8
 
9
- # Human readable random tokens with no ambiguous characters
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: 1, L: 1, O: 0, U: nil, B: 8, '$' => '$'}
23
- # end
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
- # DEFAULT:
26
- # substitution_hash: { I: 1, L: 1, O: 0, U: :V }
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 use a different character set. For convenience, numbers
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
- # *CAUTION:* Changing substitution_hash keys alters the check character, invalidating previous tokens.
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
- def generate(output_size: 10)
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
- (token = generate_random(output_size - 1)) + check_character(token)
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
- # Return value: Valid token or nil
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 = input.upcase.tr(trans_from, trans_to).chars.map! { |c| charset.index(c) }
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
- # Manipulate by configuring +substitution_hash+
100
+ # DEFAULT: Digits 0-9 and uppercase letters A-Z except for ILOU
76
101
  #
77
- # DEFAULT: All number and uppercase letters except for ILOU
102
+ # @note Manipulate via {#configure}
103
+ # @return [Array] of available characters
78
104
  def charset
79
- @charset ||= (('0'..'9').to_a + ('A'..'Z').to_a - trans_from.chars + trans_to.chars - nil_substitutions).uniq
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 ||= OpenStruct.new(
86
- substitution_hash: { I: 1, L: 1, O: 0, U: :V }
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
- # *CAUTION:* Changing substitution_hash keys alters the output
184
+ # CAUTION: Changing charset alters the output
127
185
  def check_character(input)
128
186
  array =
129
- input.chars.reverse.each_with_index.map do |c, i|
130
- d = charset.index(c)
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 nil_substitutions
163
- @nil_substitutions ||=
164
- begin
165
- array = configuration.substitution_hash.each.map { |k, v| k if v.nil? }
166
- array.compact!
167
- array.map!(&:to_s)
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 trans_from
173
- @trans_from ||=
174
- begin
175
- array = configuration.substitution_hash.each.map { |k, v| k unless v.nil? }
176
- array.compact!
177
- array.map!(&:to_s)
178
- array.map!(&:upcase)
179
- array.join
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
- def trans_to
184
- @trans_to ||=
238
+ # Flattened version of substitution_hash
239
+ def validation_hash
240
+ @validation_hash ||=
185
241
  begin
186
- array = configuration.substitution_hash.values
187
- array.compact!
188
- array.map!(&:to_s)
189
- array.map!(&:upcase)
190
- array.join
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
@@ -4,6 +4,6 @@
4
4
 
5
5
  module HumanReadable
6
6
  # Gem version
7
- VERSION = '0.8.0'
7
+ VERSION = '0.9.0'
8
8
  public_constant :VERSION
9
9
  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.8.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-15 00:00:00.000000000 Z
11
+ date: 2020-08-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
- Human readable random tokens with no ambiguous characters
15
-
16
- Tranlates invalid characters to their most likely original value
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.4.1
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.1.2
50
+ rubygems_version: 3.0.3
52
51
  signing_key:
53
52
  specification_version: 4
54
- summary: Human readable random tokens with no ambiguous characters
53
+ summary: Human readable random tokens without ambiguous characters, and optional Emoji
54
+ support.
55
55
  test_files: []