uuid-ncname 0.2.5 → 0.4.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: 0eaed682c9e65dd5d15a576fcc62b0f234cc70481017aaac64eef84afe3bb6ea
4
- data.tar.gz: f55eadabe095625c795d7cefda9e2cedc9643d383d0e1ecd17b375f3897b99ac
3
+ metadata.gz: 471f6d4d6e5124454b7bcc1a7ab46fc3f6fbf6951f4ba988c80a9b04f0051be1
4
+ data.tar.gz: 03265d39a6f8b4c38ad9d86ddab9753563216c4ac3421a5cff9f38f1e536d4e1
5
5
  SHA512:
6
- metadata.gz: 9f060166823eb018f830787177ad331d2e530d5036341802e8ae5b49c6f4a2e661ee8e8a273573bca44ab24904a33b754f7efcbe196e174c998a9a97958514fa
7
- data.tar.gz: e9fbb9c79c496bf639eef5d500e07f9b8f7207739a04c36aea1d6035fb56ddbd5581d8f33b63058a074f4fa35e5d23a9982315f2b14d3533b939f2f9fee89d5c
6
+ metadata.gz: 311e0f9856467d875782d37ac759ec2479d74276ad2ec427588954ab22335e233d69f2d32e6ceaf20a24e91eba8a2cd780721f89f5a829c7acf346ab78b20401
7
+ data.tar.gz: d9458bc9b998ac9fb0d5450e66a73d49bbc5bd546250d221045c4b17361cc9a02fa9a0c9b2dbf012686e4bc0c60336635b6946e7f531a8ba80900336e24affa9
data/.gitignore CHANGED
@@ -8,6 +8,8 @@ syntax: glob
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  *.gem
11
+ \#*
12
+ .\#*
11
13
 
12
14
  # rspec failure tracking
13
15
  .rspec_status
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --protected
2
+ --no-private
3
+ --hide-void-return
4
+ --markup markdown
5
+ --readme README.md
data/README.md CHANGED
@@ -7,19 +7,17 @@ require 'uuidtools'
7
7
  uu = UUIDTools::UUID.random_create
8
8
  # => #<UUID:0x3fff0e597ef8 UUID:df521e0a-9d57-4f04-9a95-fc2888decc5a>
9
9
 
10
- # see below about this :version parameter
11
-
12
- nc64 = UUID::NCName.to_ncname uu, version: 1
10
+ nc64 = UUID::NCName.to_ncname uu
13
11
  # => "E31IeCp1X8EqV_CiI3sxaJ"
14
12
 
15
- nc32 = UUID::NCName.to_ncname_32 uu, version: 1
13
+ nc32 = UUID::NCName.to_ncname_32 uu
16
14
  # => "E35jb4cu5k7yevfp4fcen5tc2j"
17
15
 
18
- orig = UUID::NCName.from_ncname nc64, version: 1
16
+ orig = UUID::NCName.from_ncname nc64
19
17
  # => "df521e0a-9d57-4f04-9a95-fc2888decc5a"
20
18
 
21
- orig == UUID::NCName.from_ncname nc32, version: 1 # => true
22
- orig == uu.to_s # => true
19
+ orig == UUID::NCName.from_ncname nc32 # => true
20
+ orig == uu.to_s # => true
23
21
 
24
22
  # then you can turn it back into an object or whatever
25
23
  uu == UUIDTools::UUID.parse(orig) # => true
@@ -27,69 +25,12 @@ uu == UUIDTools::UUID.parse(orig) # => true
27
25
 
28
26
  ## Description
29
27
 
30
- The purpose of this module is to devise an alternative representation
28
+ The purpose of this module is to [devise an alternative
29
+ representation](https://datatracker.ietf.org/doc/html/draft-taylor-uuid-ncname)
31
30
  of the [UUID](http://tools.ietf.org/html/rfc4122) which conforms to
32
- the constraints of various other identifiers such as NCName, and create an
33
- [isomorphic](http://en.wikipedia.org/wiki/Isomorphism) mapping between
34
- them.
35
-
36
- ## _FORMAT DEPRECATION NOTICE_
37
-
38
- After careful consideration, I have decided to change the UUID-NCName
39
- format in a minor yet incompatible way. In particular, I have moved
40
- the nybble containing
41
- the [`variant`](https://tools.ietf.org/html/rfc4122#section-4.1.1) to
42
- the very end of the identifier, whereas it previously was mixed into
43
- the middle somewhere.
44
-
45
- This can be considered an application
46
- of [Postel's Law](https://en.wikipedia.org/wiki/Postel%27s_law), based
47
- on the assumption that these identifiers will be generated through
48
- other methods, and potentially naïvely. Like the `version` field, the
49
- `variant` field has a limited acceptable range of values. If, for
50
- example, one were to attempt to generate a conforming identifier by
51
- simply generating a random Base32 or Base64 string, it will be
52
- difficult to ensure that the `variant` field will indeed conform when
53
- the identifier is converted to a standard UUID. By moving the
54
- `variant` field out to the end of the identifier, everything between
55
- the `version` and `variant` bookends can be generated randomly without
56
- any further consideration, like so:
57
-
58
- ```ruby
59
- B64_ALPHA = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + %w(- _)
60
-
61
- def make_cheapo_b64_uuid_ncname
62
- vals = (1..20).map { rand 64 } # generate the content
63
- vals.push(rand(4) + 8) # last digit is special
64
- 'E' + vals.map { |v| B64_ALPHA[v] }.join('') # 'E' for UUID v4
65
- end
66
-
67
- # voilà:
68
-
69
- cheap = make_cheapo_b64_uuid_ncname
70
- # => "EXSVv8ezPbSKWoKOkBNWKL"
71
-
72
- # now try changing it to a standard UUID:
73
-
74
- UUID::NCName.from_ncname cheap, version: 1
75
- # => "5d256ff1-eccf-46d2-b296-a0a3a404d58a"
76
- ```
77
-
78
- Furthermore, since the default behaviour is to align the bits of the
79
- last byte to the size of the encoding symbol, and since the `variant`
80
- bits are masked, a compliant RFC4122 UUID will _always_ end with `I`,
81
- `J`, `K`, or `L`, in _both_ Base32 (case-insensitive) and Base64
82
- variants.
83
-
84
- Since I have already released this gem prior to this format change, I
85
- have added a `:version` parameter to both `to_ncname` and
86
- `from_ncname`. The version, as of 0.2.4, now defaults to `1`, the
87
- current one, but will still issue a warning if not explicitly
88
- set. Later I will finally remove the warning. This should ensure that
89
- any code written during the transition produces the correct results.
90
-
91
- > Unless you have to support identifiers generated from version 0.1.3
92
- > or older, you should be running these methods with `version: 1`.
31
+ the constraints of various other identifiers such as NCName, and
32
+ create an [isomorphic](http://en.wikipedia.org/wiki/Isomorphism)
33
+ mapping between them.
93
34
 
94
35
  ## Rationale & Method
95
36
 
@@ -198,6 +139,62 @@ representation should be adequate for placeholder symbols in just
198
139
  about any programming language, save for those which do not permit
199
140
  identifiers as long as 26 characters (which are extremely scarce).
200
141
 
142
+ ## _FORMAT DEPRECATION NOTICE_
143
+
144
+ After careful consideration, I have decided to change the UUID-NCName
145
+ format in a minor yet incompatible way. In particular, I have moved
146
+ the nybble containing
147
+ the [`variant`](https://tools.ietf.org/html/rfc4122#section-4.1.1) to
148
+ the very end of the identifier, whereas it previously was mixed into
149
+ the middle somewhere.
150
+
151
+ This can be considered an application
152
+ of [Postel's Law](https://en.wikipedia.org/wiki/Postel%27s_law), based
153
+ on the assumption that these identifiers will be generated through
154
+ other methods, and potentially naïvely. Like the `version` field, the
155
+ `variant` field has a limited acceptable range of values. If, for
156
+ example, one were to attempt to generate a conforming identifier by
157
+ simply generating a random Base32 or Base64 string, it will be
158
+ difficult to ensure that the `variant` field will indeed conform when
159
+ the identifier is converted to a standard UUID. By moving the
160
+ `variant` field out to the end of the identifier, everything between
161
+ the `version` and `variant` bookends can be generated randomly without
162
+ any further consideration, like so:
163
+
164
+ ```ruby
165
+ B64_ALPHA = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + %w(- _)
166
+
167
+ def make_cheapo_b64_uuid_ncname
168
+ vals = (1..20).map { rand 64 } # generate the content
169
+ vals.push(rand(4) + 8) # last digit is special
170
+ 'E' + vals.map { |v| B64_ALPHA[v] }.join('') # 'E' for UUID v4
171
+ end
172
+
173
+ # voilà:
174
+
175
+ cheap = make_cheapo_b64_uuid_ncname
176
+ # => "EXSVv8ezPbSKWoKOkBNWKL"
177
+
178
+ # now try changing it to a standard UUID:
179
+
180
+ UUID::NCName.from_ncname cheap, version: 1
181
+ # => "5d256ff1-eccf-46d2-b296-a0a3a404d58a"
182
+ ```
183
+
184
+ Furthermore, since the default behaviour is to align the bits of the
185
+ last byte to the size of the encoding symbol, and since the `variant`
186
+ bits are masked, a compliant RFC4122 UUID will _always_ end with `I`,
187
+ `J`, `K`, or `L`, in _both_ Base32 (case-insensitive) and Base64
188
+ variants.
189
+
190
+ Since I have already released this gem prior to this format change, I
191
+ have added a `:version` parameter to both `to_ncname` and
192
+ `from_ncname`. This parameter, which controls the compact UUID spec
193
+ behaviour, defaults to `1`, as of the _module_ version 0.2.4.
194
+
195
+ > Unless you have to support identifiers generated from version 0.1.3
196
+ > or older, you should be running these methods with `version: 1`.
197
+
201
198
  ## Documentation
202
199
 
203
200
  Generated and deposited
@@ -6,5 +6,5 @@ unless Module.const_defined? 'UUID'
6
6
  end
7
7
 
8
8
  module UUID::NCName
9
- VERSION = "0.2.5"
9
+ VERSION = "0.4.0"
10
10
  end
data/lib/uuid/ncname.rb CHANGED
@@ -3,11 +3,20 @@ require "uuid/ncname/version"
3
3
 
4
4
  require 'base64'
5
5
  require 'base32'
6
+ require 'base58'
6
7
 
7
8
  module UUID::NCName
8
9
 
9
10
  private
10
11
 
12
+ MATCH = /^([A-Pa-p]) # zero-width boundary and version bookend
13
+ ([2-7A-Za-z]{24}|[-0-9A-Z_a-z]{20}| # base32 and 64
14
+ (?:[1-9A-HJ-NP-Za-km-z]{15}_{6}|[1-9A-HJ-NP-Za-km-z]{16}_{5}|
15
+ [1-9A-HJ-NP-Za-km-z]{17}_{4}|[1-9A-HJ-NP-Za-km-z]{18}___|
16
+ [1-9A-HJ-NP-Za-km-z]{19}__|[1-9A-HJ-NP-Za-km-z]{20}_|
17
+ [1-9A-HJ-NP-Za-km-z]{21})) # base58 with underscore pad
18
+ ([-0-9A-Z_a-z])$/x.freeze # lax variant bookend and zero-width boundary
19
+
11
20
  ENCODE = {
12
21
  32 => -> (bin, align = true) {
13
22
  if align
@@ -18,7 +27,15 @@ module UUID::NCName
18
27
 
19
28
  out = ::Base32.encode bin
20
29
 
21
- out.downcase[0, 25]
30
+ out.downcase[0, 25] # clip off the padding
31
+ },
32
+ 58 => -> (bin, _) {
33
+ variant = bin[-1].ord >> 4
34
+ # note the bitcoin alphabet is the one used in draft-msporny-base58
35
+ out = ::Base58.binary_to_base58(bin.chop, :bitcoin)
36
+ # we need to pad base58 with underscores because it is variable length
37
+ out + (?_ * (21 - out.length)) +
38
+ encode_version(variant) # encode_version does variant too
22
39
  },
23
40
  64 => -> (bin, align = true) {
24
41
  if align
@@ -29,10 +46,11 @@ module UUID::NCName
29
46
 
30
47
  out = ::Base64.urlsafe_encode64 bin
31
48
 
32
- out[0, 21]
49
+ out[0, 21] # clip off the padding
33
50
  },
34
51
  }
35
52
 
53
+ # note the version symbol is already removed
36
54
  DECODE = {
37
55
  32 => -> (str, align = true) {
38
56
  str = str.upcase[0, 25] + 'A======'
@@ -41,6 +59,14 @@ module UUID::NCName
41
59
 
42
60
  out.pack 'C*'
43
61
  },
62
+ 58 => -> (str, _) {
63
+ variant = decode_version(str[-1]) << 4
64
+ # warn str
65
+ str = str.chop.tr ?_, ''
66
+ # warn str
67
+ # warn ::Base58.base58_to_binary(str, :bitcoin).length
68
+ ::Base58.base58_to_binary(str, :bitcoin) + variant.chr.b
69
+ },
44
70
  64 => -> (str, align = true) {
45
71
  str = str[0, 21] + 'A=='
46
72
  out = ::Base64.urlsafe_decode64(str).unpack 'C*'
@@ -54,6 +80,7 @@ module UUID::NCName
54
80
 
55
81
  FORMAT = {
56
82
  str: -> bin { UUF % bin.unpack('C*') },
83
+ urn: -> bin { "urn:uuid:#{UUF % bin.unpack('C*')}" },
57
84
  hex: -> bin { bin.unpack 'H*' },
58
85
  b64: -> bin { ::Base64.strict_encode64 bin },
59
86
  bin: -> bin { bin },
@@ -102,7 +129,10 @@ module UUID::NCName
102
129
  -> (version, data) {
103
130
  version &= 0xf
104
131
 
132
+ # warn data.length
133
+
105
134
  list = data.unpack 'N4'
135
+ # warn list.inspect
106
136
  variant = (list[3] & 0xf0) << 24
107
137
  list[3] >>= 8
108
138
  list[3] |= ((list[2] & 0xff) << 24)
@@ -116,14 +146,25 @@ module UUID::NCName
116
146
  ],
117
147
  ]
118
148
 
119
- def self.encode_version version
120
- ((version & 15) + 65).chr
149
+ def self.encode_version version, radix = 64
150
+ offset = radix == 32 ? 97 : 65
151
+ ((version & 15) + offset).chr
121
152
  end
122
153
 
123
154
  def self.decode_version version
124
155
  (version.upcase.ord - 65) % 16
125
156
  end
126
157
 
158
+ def self.assert_version version
159
+ version = 1 unless version
160
+ raise ArgumentError, "version #{version.inspect} is not an integer" unless
161
+ version.respond_to? :to_i
162
+ version = version.to_i
163
+ raise ArgumentError, "there is no version #{version}" unless
164
+ TRANSFORM[version]
165
+ version
166
+ end
167
+
127
168
  def self.warn_version version
128
169
  if version.nil?
129
170
  warn 'Set an explicit :version to remove this warning. See documentation.'
@@ -137,6 +178,27 @@ module UUID::NCName
137
178
 
138
179
  public
139
180
 
181
+ # This error gets thrown when a UUID-NCName token can't be
182
+ # positively determined to be one version or the other.
183
+ class AmbiguousToken < ArgumentError
184
+
185
+ # @return [String] The ambiguous token
186
+ attr_reader :token
187
+ # @return [String] The UUID when decoded using version 0
188
+ attr_reader :v0
189
+ # @return [String] The UUID when decoded using version 1
190
+ attr_reader :v1
191
+
192
+ # @param token [#to_s] The token in question
193
+ # @param v0 [#to_s] UUID decoded with decoding scheme version 0
194
+ # @param v1 [#to_s] UUID decoded with decoding scheme version 1
195
+
196
+ def initialize token, v0: nil, v1: nil
197
+ @v0 = v0 || from_ncname(token, version: 0)
198
+ @v1 = v1 || from_ncname(token, version: 1)
199
+ end
200
+ end
201
+
140
202
  # Converts a UUID (or object that when converted to a string looks
141
203
  # like a UUID) to an NCName. By default it produces the Base64
142
204
  # variant.
@@ -149,9 +211,7 @@ module UUID::NCName
149
211
  #
150
212
  # @param version [0, 1] An optional formatting version, where 0 is
151
213
  # the naïve original version and 1 moves the `variant` nybble out
152
- # to the end of the identifier. You will be warned for the time
153
- # being if you do not set this parameter explicitly. The default
154
- # version is 1.
214
+ # to the end of the identifier. The default version is 1.
155
215
  #
156
216
  # @param align [true, false] Optional directive to treat the
157
217
  # terminating character as aligned to the numerical base of the
@@ -162,18 +222,21 @@ module UUID::NCName
162
222
  # range of the letters A through P in (the RFC 3548/4648
163
223
  # representations of) both Base32 and Base64. When `version` is 1
164
224
  # and the terminating character is aligned, RFC4122-compliant UUIDs
165
- # will always terminate with I, J, K, or L. Defaults to `true`.
225
+ # will always terminate with `I`, `J`, `K`, or `L`. Defaults to
226
+ # `true`.
166
227
  #
167
228
  # @return [String] The NCName-formatted UUID.
168
-
229
+ #
169
230
  def self.to_ncname uuid, radix: 64, version: nil, align: true
170
- raise 'Radix must be either 32 or 64' unless [32, 64].include? radix
231
+ raise 'Radix must be either 32, 58, or 64' unless
232
+ [32, 58, 64].include? radix
171
233
  raise 'UUID must be something stringable' if uuid.nil? or
172
234
  not uuid.respond_to? :to_s
173
- raise 'Align must be true or false' unless [true, false].include? align
235
+ align = !!align # coerce to a boolean
174
236
 
175
237
  # XXX remove this when appropriate
176
- version = warn_version(version)
238
+ # version = warn_version(version)
239
+ version = assert_version version
177
240
 
178
241
  uuid = uuid.to_s
179
242
  bin = nil
@@ -195,9 +258,9 @@ module UUID::NCName
195
258
  raise 'Binary representation of UUID is shorter than 16 bytes' if
196
259
  bin.length < 16
197
260
 
198
- uuidver, content = TRANSFORM[version][0].call bin[0, 16]
261
+ uuidver, content = TRANSFORM[version].first.call bin[0, 16]
199
262
 
200
- encode_version(uuidver) + ENCODE[radix].call(content, align)
263
+ encode_version(uuidver, radix) + ENCODE[radix].call(content, align)
201
264
  end
202
265
 
203
266
  # Converts an NCName-encoded UUID back to its canonical
@@ -207,9 +270,10 @@ module UUID::NCName
207
270
  # @param ncname [#to_s] an NCName-encoded UUID, either a
208
271
  # 22-character (Base64) variant, or a 26-character (Base32) variant.
209
272
  #
210
- # @param radix [nil, 32, 64] Optional radix; will use heuristic if omitted.
273
+ # @param radix [nil, 32, 58, 64] Optional radix; will use a heuristic
274
+ # if omitted.
211
275
  #
212
- # @param format [:str, :hex, :b64, :bin] An optional formatting
276
+ # @param format [:str, :urn, :hex, :b64, :bin] An optional formatting
213
277
  # parameter; defaults to `:str`, the canonical string representation.
214
278
  #
215
279
  # @param version [0, 1] See ::to_ncname. Defaults to 1.
@@ -218,12 +282,12 @@ module UUID::NCName
218
282
  # Setting this parameter to `nil`, the default, will cause the
219
283
  # decoder to detect the alignment state from the identifier.
220
284
  #
221
- # @param validate [false, true] Check that the ninth octet is
222
- # correctly masked _after_ decoding.
285
+ # @param validate [false, true] Check that the ninth (the variant)
286
+ # octet is correctly masked _after_ decoding.
223
287
  #
224
288
  # @return [String, nil] The corresponding UUID or nil if the input
225
289
  # is malformed.
226
-
290
+ #
227
291
  def self.from_ncname ncname,
228
292
  radix: nil, format: :str, version: nil, align: nil, validate: false
229
293
  raise 'Format must be symbol-able' unless format.respond_to? :to_sym
@@ -232,34 +296,31 @@ module UUID::NCName
232
296
  [true, false, nil].include? align
233
297
 
234
298
  # XXX remove this when appropriate
235
- version = warn_version version
299
+ # version = warn_version version
300
+ version = assert_version version
236
301
 
237
302
  return unless ncname and ncname.respond_to? :to_s
238
303
 
239
304
  ncname = ncname.to_s.strip.gsub(/\s+/, '')
240
- match = /^([A-Za-z])([0-9A-Za-z_-]{21,})$/.match(ncname) or return
305
+ match = MATCH.match(ncname) or return
306
+ return if align and !/[A-Pa-p]$/.match? ncname # MATCH is lax
241
307
 
308
+ # determine the radix from the input
242
309
  if radix
243
- raise "Radix must be 32 or 64, not #{radix}" unless [32, 64].any? radix
244
- return unless { 32 => 26, 64 => 22 }[radix] == ncname.length
310
+ raise ArgumentError, "Radix must be 32, 58, or 64, not #{radix}" unless
311
+ [32, 58, 64].any? radix
312
+ return unless { 32 => 26, 58 => 23, 64 => 22 }[radix] == ncname.length
245
313
  else
246
- len = ncname.length
247
-
248
- if ncname =~ /[_-]/
249
- radix = 64
250
- elsif len >= 26
251
- radix = 32
252
- elsif len >= 22
253
- radix = 64
254
- else
255
- # uh will this ever get executed now that i put in that return?
256
- raise "Not sure what to do with an identifier of length #{len}."
257
- end
314
+ radix = { 26 => 32, 23 => 58, 22 => 64}[ncname.length] or
315
+ raise ArgumentError,
316
+ "Not sure what to do with an identifier of length #{ncname.length}."
258
317
  end
259
318
 
260
- uuidver, content = match.captures
319
+ # note MATCH separates the variant
320
+ uuidver, *content = match.captures
321
+ content = content.join
261
322
 
262
- align = !!(content =~ /[A-Pa-p]$/) if align.nil?
323
+ align = !!(/[A-Pa-p]$/.match? content) if align.nil?
263
324
  uuidver = decode_version uuidver
264
325
  content = DECODE[radix].call content, align
265
326
 
@@ -280,7 +341,7 @@ module UUID::NCName
280
341
  # @param align [true, false] See ::to_ncname.
281
342
  #
282
343
  # @return [String] The Base64-encoded NCName
283
-
344
+ #
284
345
  def self.to_ncname_64 uuid, version: nil, align: true
285
346
  to_ncname uuid, version: version, align: align
286
347
  end
@@ -297,9 +358,42 @@ module UUID::NCName
297
358
  #
298
359
  # @return [String, nil] The corresponding UUID or nil if the input
299
360
  # is malformed.
300
-
361
+ #
301
362
  def self.from_ncname_64 ncname, format: :str, version: nil, align: nil
302
- from_ncname ncname, radix: 64, format: format
363
+ from_ncname ncname,
364
+ radix: 64, format: format, version: version, align: align
365
+ end
366
+
367
+ # Shorthand for conversion to the Base58 version
368
+ #
369
+ # @param uuid [#to_s] The UUID
370
+ #
371
+ # @param version [0, 1] See ::to_ncname.
372
+ #
373
+ # @param align [true, false] See ::to_ncname.
374
+ #
375
+ # @return [String] The Base58-encoded NCName
376
+ #
377
+ def self.to_ncname_58 uuid, version: nil, align: true
378
+ to_ncname uuid, radix: 58, version: version, align: align
379
+ end
380
+
381
+ # Shorthand for conversion from the Base58 version
382
+ #
383
+ # @param ncname [#to_s] The Base58 variant of the NCName-encoded UUID
384
+ #
385
+ # @param format [:str, :hex, :b64, :bin] The format
386
+ #
387
+ # @param version [0, 1] See ::to_ncname.
388
+ #
389
+ # @param align [true, false] See ::to_ncname.
390
+ #
391
+ # @return [String, nil] The corresponding UUID or nil if the input
392
+ # is malformed.
393
+ #
394
+ def self.from_ncname_58 ncname, format: :str, version: nil, align: nil
395
+ from_ncname ncname,
396
+ radix: 58, format: format, version: version, align: align
303
397
  end
304
398
 
305
399
  # Shorthand for conversion to the Base32 version
@@ -311,7 +405,7 @@ module UUID::NCName
311
405
  # @param align [true, false] See ::to_ncname.
312
406
  #
313
407
  # @return [String] The Base32-encoded NCName
314
-
408
+ #
315
409
  def self.to_ncname_32 uuid, version: nil, align: true
316
410
  to_ncname uuid, radix: 32, version: version, align: align
317
411
  end
@@ -328,9 +422,10 @@ module UUID::NCName
328
422
  #
329
423
  # @return [String, nil] The corresponding UUID or nil if the input
330
424
  # is malformed.
331
-
425
+ #
332
426
  def self.from_ncname_32 ncname, format: :str, version: nil, align: nil
333
- from_ncname ncname, radix: 32, format: format
427
+ from_ncname ncname,
428
+ radix: 32, format: format, version: version, align: align
334
429
  end
335
430
 
336
431
  # Test if the given token is a UUID NCName, with a hint to its
@@ -340,30 +435,37 @@ module UUID::NCName
340
435
  # `false` if the token is invalid, otherwise it returns `0` or `1`
341
436
  # for the guessed version.
342
437
  #
343
- # @note Version 1 tokens always end with I, J, K, or L (with base32
344
- # being case-insensitive), so tokens that end in something else will
345
- # be version 0.
438
+ # @note Version 1 tokens always end with `I`, `J`, `K`, or `L` (with
439
+ # base32 being case-insensitive), so tokens that end in something
440
+ # else will always be version 0.
346
441
  #
347
442
  # @param token [#to_s] The token to test
348
443
  #
444
+ # @param strict [false, true]
445
+ #
349
446
  # @return [false, 0, 1]
350
- def self.valid? token
447
+ #
448
+ def self.valid? token, strict: false
351
449
  token = token.to_s
352
- if /^[A-Pa-p](?:[0-9A-Za-z_-]{21}|[2-7A-Za-z]{25})$/.match token
450
+ if MATCH.match? token
353
451
  # false is definitely version zero but true is only maybe version 1
354
452
  version = /^(?:.{21}[I-L]|.{25}[I-Li-l])$/.match(token) ? 1 : 0
355
453
 
356
454
  # try decoding with validation on
357
455
  uu = from_ncname token, version: version, validate: true
358
456
 
359
- if version == 1 and !uu
360
- # try version zero
361
- uu = from_ncname token, version: 0, validate: true
362
- # either zero or invalid
363
- uu ? 0 : false
364
- else
365
- version
457
+ # note that version 1 will always return something because the
458
+ # method of detecting it is a version 1 also happens to be the
459
+ # method of determining whether or not it is valid.
460
+ return false unless uu
461
+
462
+ if version == 1 and strict
463
+ # but we can also check if the input is a valid version 0
464
+ u0 = from_ncname token, version: 0, validate: true
465
+ raise AmbiguousToken.new(token, v0: u0, v1: uu) if u0
366
466
  end
467
+
468
+ version
367
469
  else
368
470
  false
369
471
  end
data/uuid-ncname.gemspec CHANGED
@@ -27,15 +27,16 @@ DESC
27
27
  spec.require_paths = ["lib"]
28
28
 
29
29
  # we use named parameters
30
- spec.required_ruby_version = '~> 2.0'
30
+ spec.required_ruby_version = '>= 2.0'
31
31
 
32
32
  # surprisingly do not need this
33
33
  # spec.add_runtime_dependency 'uuidtools', '~> 2.1.5'
34
34
  spec.add_runtime_dependency 'base32', '~> 0.3.2'
35
+ spec.add_runtime_dependency 'base58', '~> 0.2.3'
35
36
 
36
- spec.add_development_dependency 'bundler', '~> 1.16'
37
- spec.add_development_dependency 'rake', '~> 10.0'
38
- spec.add_development_dependency 'rspec', '~> 3.0'
37
+ spec.add_development_dependency 'bundler', '~> 2.2'
38
+ spec.add_development_dependency 'rake', '~> 13.0'
39
+ spec.add_development_dependency 'rspec', '~> 3.10'
39
40
 
40
41
  # only need it for testing, who knew
41
42
  # spec.add_development_dependency 'uuidtools', '~> 2.1.5'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uuid-ncname
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Taylor
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-19 00:00:00.000000000 Z
11
+ date: 2021-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base32
@@ -24,48 +24,62 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.3.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: base58
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.3
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '1.16'
47
+ version: '2.2'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: '1.16'
54
+ version: '2.2'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '10.0'
61
+ version: '13.0'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '10.0'
68
+ version: '13.0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: '3.0'
75
+ version: '3.10'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: '3.0'
82
+ version: '3.10'
69
83
  description: |
70
84
  This module creates an isomorphic representation of a UUID which is
71
85
  guaranteed to fit into the grammar of the XML NCName construct, which
@@ -81,6 +95,7 @@ files:
81
95
  - ".gitignore"
82
96
  - ".rspec"
83
97
  - ".travis.yml"
98
+ - ".yardopts"
84
99
  - Gemfile
85
100
  - LICENSE
86
101
  - README.md
@@ -95,13 +110,13 @@ homepage: https://github.com/doriantaylor/rb-uuid-ncname
95
110
  licenses:
96
111
  - Apache-2.0
97
112
  metadata: {}
98
- post_install_message:
113
+ post_install_message:
99
114
  rdoc_options: []
100
115
  require_paths:
101
116
  - lib
102
117
  required_ruby_version: !ruby/object:Gem::Requirement
103
118
  requirements:
104
- - - "~>"
119
+ - - ">="
105
120
  - !ruby/object:Gem::Version
106
121
  version: '2.0'
107
122
  required_rubygems_version: !ruby/object:Gem::Requirement
@@ -110,8 +125,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
125
  - !ruby/object:Gem::Version
111
126
  version: '0'
112
127
  requirements: []
113
- rubygems_version: 3.0.3
114
- signing_key:
128
+ rubygems_version: 3.2.22
129
+ signing_key:
115
130
  specification_version: 4
116
131
  summary: Format a UUID as a valid NCName.
117
132
  test_files: []