uuid-ncname 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6d0c55013befe4854c49979a826188f10feb869e24a589a0611ae1f97a2ea600
4
+ data.tar.gz: e78b411fa76e5cfe2c971a6904a0404e8f58ec3e5b64b9fa99620146c62d3b72
5
+ SHA512:
6
+ metadata.gz: 63c11981cbf9cc492fbdf1dbba02aca38c9afbefef4b839ce70019395d7a6cbca5c77060efe814f1e2b0a68388bd0ced59d3a1defabaa3bb4210b76fa59d45e3
7
+ data.tar.gz: c0c7c3d145c1245f2aa77184dba53b9cad9157689a2ee3d259c3ffc7d3b6175b990036a8058ebc1ebef97716f3cac27ce0b975685da0a4914e51775e1d864b86
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ syntax: glob
2
+ /.bundle/
3
+ /.yardoc
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
13
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.1
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in uuid-ncname.gemspec
6
+ gemspec
data/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # UUID::NCName: Turn UUIDs into NCNames (and back)
2
+
3
+ ```ruby
4
+ require 'uuid-ncname'
5
+ require 'uuidtools'
6
+
7
+ uu = UUIDTools::UUID.random_create
8
+ # => #<UUID:0x3fff0e597ef8 UUID:df521e0a-9d57-4f04-9a95-fc2888decc5a>
9
+
10
+ nc64 = UUID::NCName.to_ncname uu # => "E31IeCp1X8EmpX8KIjezFK"
11
+ nc32 = UUID::NCName.to_ncname_32 uu # => "E35jb4cu5k7yetkk7ykei33gfk"
12
+
13
+ orig = UUID::NCName.from_ncname nc64
14
+ # => "df521e0a-9d57-4f04-9a95-fc2888decc5a"
15
+
16
+ orig == UUID::NCName.from_ncname nc32 # => true
17
+ orig == uu.to_s # => true
18
+
19
+ # then you can turn it back into an object or whatever
20
+ uu == UUIDTools::UUID.parse(orig) # => true
21
+ ```
22
+
23
+ ## Description
24
+
25
+ The purpose of this module is to devise an alternative representation
26
+ of the [UUID](http://tools.ietf.org/html/rfc4122) which conforms to
27
+ the constraints of various other identifiers such as NCName, and create an
28
+ [isomorphic](http://en.wikipedia.org/wiki/Isomorphism) mapping between
29
+ them.
30
+
31
+ ## Rationale & Method
32
+
33
+ The UUID is a generic identifier which is large enough to be globally
34
+ unique. This makes it useful as a canonical name for data objects in
35
+ distributed systems, especially those that cross administrative
36
+ jurisdictions, such as the World-Wide Web. The
37
+ [representation](http://tools.ietf.org/html/rfc4122#section-3),
38
+ however, of the UUID, precludes it from being used in many places
39
+ where it would be useful to do so.
40
+
41
+ In particular, there are grammars for many types of identifiers which
42
+ must not begin with a digit. Others are case-insensitive, or
43
+ prohibited from containing hyphens (present in both the standard
44
+ notation and Base64URL), or indeed anything outside of
45
+ `^[A-Za-z_][0-9A-Za-z_]*$`.
46
+
47
+ The hexadecimal notation of the UUID has a 5/8 chance of beginning
48
+ with a digit, Base64 has a 5/32 chance, and Base32 has a 3/16
49
+ chance. As such, the identifier must be modified in such a way as to
50
+ guarantee beginning with an alphabetic letter (or underscore `_`, but
51
+ some grammars even prohibit that, so we omit it as well).
52
+
53
+ While it is conceivable to simply add a padding character, there are a
54
+ few considerations which make it more appealing to derive the initial
55
+ character from the content of the UUID itself:
56
+
57
+ * UUIDs are large (128-bit) identifiers as it is, and it is
58
+ undesirable to add meaningless syntax to them if we can avoid doing
59
+ so.
60
+
61
+ * 128 bits is an inconvenient number for aligning to both Base32 (130)
62
+ and Base64 (132), though 120 divides cleanly into 5, 6 and 8.
63
+
64
+ * The 13th quartet, or higher four bits of the
65
+ `time_hi_and_version_field` of the UUID is constant, as it indicates
66
+ the UUID's version. If we encode this value using the scheme common
67
+ to both Base64 and Base32, we get values between `A` and `P`, with
68
+ the valid subset between `B` and `F`.
69
+
70
+ **Therefore:** extract the UUID's version quartet, shift all
71
+ subsequent data 4 bits to the left, zero-pad to the octet, encode with
72
+ either _base64url_ or _base32_, truncate, and finally prepend the
73
+ encoded version character. Voilà, one token-safe UUID.
74
+
75
+ ## Applications
76
+
77
+ ### XML IDs
78
+
79
+ The `ID` production appears to have been constricted, inadvertently or
80
+ otherwise, from [Name](http://www.w3.org/TR/xml11/#NT-Name) in both
81
+ the XML 1.0 and 1.1 specifications,
82
+ to [NCName](http://www.w3.org/TR/xml-names/#NT-NCName)
83
+ by [XML Schema Part 2](http://www.w3.org/TR/xmlschema-2/#ID). This
84
+ removes the colon character `:` from the grammar. The net effect is
85
+ that
86
+
87
+ <foo id="urn:uuid:b07caf81-baae-449d-8a2e-48c0f5fa5538"/>
88
+
89
+ while being a _well-formed_ ID _and_ valid under DTD validation, is
90
+ _not_ valid per XML Schema Part 2 or anything that uses it (e.g. Relax
91
+ NG).
92
+
93
+ ### RDF blank node identifiers
94
+
95
+ Blank node identifiers in RDF are intended for serialization, to act
96
+ as a handle so that multiple RDF statements can refer to the same
97
+ blank
98
+ node. The
99
+ [RDF abstract syntax specifies](http://www.w3.org/TR/rdf-concepts/#section-URI-Vocabulary) that
100
+ the validity constraints of blank node identifiers be delegated to the
101
+ concrete syntax
102
+ specifications. The
103
+ [RDF/XML syntax specification](http://www.w3.org/TR/rdf-syntax-grammar/#rdf-id) lists
104
+ the blank node identifier as NCName. However, according
105
+ to [the Turtle spec](http://www.w3.org/TR/turtle/#BNodes), this is a
106
+ valid blank node identifier:
107
+
108
+ _:42df00ec-30a2-431f-be9e-e3a612b325db
109
+
110
+ despite
111
+ [an older version](http://www.w3.org/TeamSubmission/turtle/#nodeID)
112
+ listing a production equivalent to the more conservative
113
+ NCName. NTriples syntax is
114
+ [even more constrained](http://www.w3.org/TR/rdf-testcases/#ntriples),
115
+ given as `^[A-Za-z][0-9A-Za-z]*$`.
116
+
117
+ ### Generated symbols
118
+
119
+ > There are only two hard things in computer science: cache
120
+ > invalidation and naming things [and off-by-one errors].
121
+ >
122
+ > -- Phil Karlton [extension of unknown origin]
123
+
124
+ Suppose you wanted to create a [literate
125
+ programming](http://en.wikipedia.org/wiki/Literate_programming) system
126
+ (I do). One of your (my) stipulations is that the symbols get defined
127
+ in the *prose*, rather than the _code_. However, you (I) still want
128
+ to be able to validate the code's syntax, and potentially even run the
129
+ code, without having to commit to naming anything. You are (I am) also
130
+ interested in creating a global map of classes, datatypes and code
131
+ fragments, which can be operated on and tested in isolation, ported to
132
+ other languages, or transplanted into the more conventional packages
133
+ of programs, libraries and frameworks. The Base32 UUID NCName
134
+ representation should be adequate for placeholder symbols in just
135
+ about any programming language, save for those which do not permit
136
+ identifiers as long as 26 characters (which are extremely scarce).
137
+
138
+ ## Documentation
139
+
140
+ Generated and deposited
141
+ [in the usual place](http://www.rubydoc.info/gems/uuid-ncname/).
142
+
143
+ ## Installation
144
+
145
+ You know how to do this:
146
+
147
+ $ gem install uuid-ncname
148
+
149
+ Or, [download it off rubygems.org](https://rubygems.org/gems/uuid-ncname).
150
+
151
+ ## Contributing
152
+
153
+ Bug reports and pull requests are welcome at
154
+ [the GitHub repository](https://github.com/doriantaylor/rb-uuid-ncname).
155
+
156
+ ## License
157
+
158
+ This software is provided under
159
+ the [Apache License, 2.0](https://www.apache.org/licenses/LICENSE-2.0).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "uuid/ncname"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,218 @@
1
+ require "uuid/ncname/version"
2
+
3
+ require 'base64'
4
+ require 'base32'
5
+
6
+ module UUID::NCName
7
+
8
+ private
9
+
10
+ ENCODE = {
11
+ 32 => -> bin {
12
+ bin = bin.unpack 'C*'
13
+ bin[-1] >>= 1
14
+ out = ::Base32.encode bin.pack('C*')
15
+
16
+ out.downcase[0, 25]
17
+ },
18
+ 64 => -> bin {
19
+ bin = bin.unpack 'C*'
20
+ bin[-1] >>= 2
21
+ out = ::Base64.urlsafe_encode64 bin.pack('C*')
22
+
23
+ out[0, 21]
24
+ },
25
+ }
26
+
27
+ DECODE = {
28
+ 32 => -> str {
29
+ str = str.upcase[0, 25] + 'A======'
30
+ out = ::Base32.decode(str).unpack 'C*'
31
+ out[-1] <<= 1
32
+
33
+ out.pack 'C*'
34
+ },
35
+ 64 => -> str {
36
+ str = str[0, 21] + 'A=='
37
+ out = ::Base64.urlsafe_decode64(str).unpack 'C*'
38
+ out[-1] <<= 2
39
+
40
+ out.pack 'C*'
41
+ },
42
+ }
43
+
44
+ UUF = (['%02x' * 4] + ['%02x' * 2] * 3 + ['%02x' * 6]).join '-'
45
+
46
+ FORMAT = {
47
+ str: -> bin { UUF % bin.unpack('C*') },
48
+ hex: -> bin { bin.unpack 'H*' },
49
+ b64: -> bin { ::Base64.strict_encode64 bin },
50
+ bin: -> bin { bin },
51
+ }
52
+
53
+ def self.bin_uuid_to_pair data
54
+ list = data.unpack 'N4'
55
+ version = (list[1] & 0x0000f000) >> 12
56
+ list[1] = (list[1] & 0xffff0000) |
57
+ ((list[1] & 0x00000fff) << 4) | (list[2] >> 28)
58
+ list[2] = (list[2] & 0x0fffffff) << 4 | (list[3] >> 28)
59
+ list[3] <<= 4
60
+
61
+ return version, list.pack('N4')
62
+ end
63
+
64
+ def self.pair_to_bin_uuid version, data
65
+ version &= 0xf
66
+
67
+ list = data.unpack 'N4'
68
+ list[3] >>= 4
69
+ list[3] |= ((list[2] & 0xf) << 28)
70
+ list[2] >>= 4
71
+ list[2] |= ((list[1] & 0xf) << 28)
72
+ list[1] = (
73
+ list[1] & 0xffff0000) | (version << 12) | ((list[1] >> 4) & 0xfff)
74
+
75
+ list.pack 'N4'
76
+ end
77
+
78
+ def self.encode_version version
79
+ ((version & 15) + 65).chr
80
+ end
81
+
82
+ def self.decode_version version
83
+ (version.upcase.ord - 65) % 16
84
+ end
85
+
86
+ public
87
+
88
+ # Converts a UUID (or object that when converted to a string looks
89
+ # like a UUID) to an NCName. By default it produces the Base64
90
+ # variant.
91
+ #
92
+ # @param uuid [#to_s] whatever it is, it had better look like a
93
+ # UUID. This includes UUID objects, URNs, 32-character hex strings,
94
+ # 16-byte binary strings, etc.
95
+ #
96
+ # @param radix [32, 64] either the number 32 or the number 64.
97
+ #
98
+ # @return [String] The NCName-formatted UUID.
99
+
100
+ def self.to_ncname uuid, radix: 64
101
+ raise 'Radix must be either 32 or 64' unless [32, 64].any? radix
102
+ raise 'UUID must be something stringable' if uuid.nil? or
103
+ not uuid.respond_to? :to_s
104
+
105
+ uuid = uuid.to_s
106
+
107
+ bin = nil
108
+
109
+ if uuid.length == 16
110
+ bin = uuid
111
+ else
112
+ uuid.gsub!(/\s+/, '')
113
+ if (m = /^(?:urn:uuid:)?([0-9A-Fa-f-]{32,})$/.match(uuid))
114
+ bin = [m[1].tr('-', '')].pack 'H*'
115
+ elsif (m = /^([0-9A-Za-z+\/_-]+=*)$/.match(uuid))
116
+ match= m[1].tr('-_', '+/')
117
+ bin = ::Base64.decode64(match)
118
+ else
119
+ raise "Not sure what to do with #{uuid}"
120
+ end
121
+ end
122
+
123
+ raise 'Binary representation of UUID is shorter than 16 bytes' if
124
+ bin.length < 16
125
+
126
+ version, content = bin_uuid_to_pair bin[0, 16]
127
+
128
+ encode_version(version) + ENCODE[radix].call(content)
129
+ end
130
+
131
+ # Converts an NCName-encoded UUID back to its canonical
132
+ # representation. Will return nil if the input doesn't match the
133
+ # radix (if supplied) or is otherwise malformed. doesn't match
134
+ #
135
+ # @param ncname [#to_s] an NCName-encoded UUID, either a
136
+ # 22-character (Base64) variant, or a 26-character (Base32) variant.
137
+ #
138
+ # @param radix [nil, 32, 64] Optional radix; will use heuristic if omitted.
139
+ #
140
+ # @param format [:str, :hex, :b64, :bin] An optional formatting
141
+ # parameter; defaults to `:str`, the canonical string representation.
142
+ #
143
+ # @return [String, nil] The corresponding UUID or nil if the input
144
+ # is malformed.
145
+
146
+ def self.from_ncname ncname, radix: nil, format: :str
147
+ raise 'Format must be symbol-able' unless format.respond_to? :to_sym
148
+ raise "Invalid format #{format}" unless FORMAT[format]
149
+
150
+ return unless ncname and ncname.respond_to? :to_s
151
+
152
+ ncname = ncname.to_s.strip.gsub(/\s+/, '')
153
+ match = /^([A-Za-z])([0-9A-Za-z_-]{21,})$/.match(ncname) or return
154
+
155
+ if radix
156
+ raise "Radix must be 32 or 64, not #{radix}" unless [32, 64].any? radix
157
+ return unless { 32 => 26, 64 => 22 }[radix] == ncname.length
158
+ else
159
+ len = ncname.length
160
+
161
+ if ncname =~ /[_-]/
162
+ radix = 64
163
+ elsif len >= 26
164
+ radix = 32
165
+ elsif len >= 22
166
+ radix = 64
167
+ else
168
+ # uh will this ever get executed now that i put in that return?
169
+ raise "Not sure what to do with an identifier of length #{len}."
170
+ end
171
+ end
172
+
173
+ version, content = match.captures
174
+ version = decode_version version
175
+ content = DECODE[radix].call content
176
+
177
+ bin = pair_to_bin_uuid version, content
178
+
179
+ FORMAT[format].call bin
180
+ end
181
+
182
+ # Shorthand for conversion to the Base64 version
183
+ #
184
+ # @param uuid [#to_s] The UUID
185
+ # @return [String] The Base64-encoded NCName
186
+
187
+ def self.to_ncname_64 uuid
188
+ to_ncname uuid
189
+ end
190
+
191
+ # Shorthand for conversion from the Base64 version
192
+ #
193
+ # @param ncname [#to_s] The Base64 variant of the NCName-encoded UUID
194
+ # @param format [:str, :hex, :b64, :bin] The format
195
+
196
+ def self.from_ncname_64 ncname, format: :str
197
+ from_ncname ncname, radix: 64, format: format
198
+ end
199
+
200
+ # Shorthand for conversion to the Base32 version
201
+ #
202
+ # @param uuid [#to_s] The UUID
203
+ # @return [String] The Base32-encoded NCName
204
+
205
+ def self.to_ncname_32 uuid
206
+ to_ncname uuid, radix: 32
207
+ end
208
+
209
+ # Shorthand for conversion from the Base32 version
210
+ #
211
+ # @param ncname [#to_s] The Base32 variant of the NCName-encoded UUID
212
+ # @param format [:str, :hex, :b64, :bin] The format
213
+
214
+ def self.from_ncname_32 ncname, format: :str
215
+ from_ncname ncname, radix: 32, format: format
216
+ end
217
+
218
+ end
@@ -0,0 +1,5 @@
1
+ module UUID
2
+ module NCName
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,40 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "uuid/ncname/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "uuid-ncname"
8
+ spec.version = UUID::NCName::VERSION
9
+ spec.authors = ["Dorian Taylor"]
10
+ spec.email = ["code@doriantaylor.com"]
11
+
12
+ spec.homepage = "https://github.com/doriantaylor/rb-uuid-ncname"
13
+ spec.summary = %q{Format a UUID as a valid NCName.}
14
+ spec.description = <<DESC
15
+ This module creates an isomorphic representation of a UUID which is
16
+ guaranteed to fit into the grammar of the XML NCName construct, which
17
+ also happens to exhibit (modulo case and hyphens) the same constraints
18
+ as identifiers in nearly all programming languages. Provides case
19
+ sensitive (Base64) and case-insensitive (Base32) variants.
20
+ DESC
21
+
22
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
23
+ f.match(%r{^(test|spec|features)/})
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ # surprisingly do not need this
30
+ # spec.add_runtime_dependency 'uuidtools', '~> 2.1.5'
31
+ spec.add_runtime_dependency 'base32', '~> 0.3.2'
32
+
33
+ spec.add_development_dependency 'bundler', '~> 1.16'
34
+ spec.add_development_dependency 'rake', '~> 10.0'
35
+ spec.add_development_dependency 'rspec', '~> 3.0'
36
+
37
+ # only need it for testing, who knew
38
+ # spec.add_development_dependency 'uuidtools', '~> 2.1.5'
39
+ # actually don't even need it for that
40
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: uuid-ncname
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dorian Taylor
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-05-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base32
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.16'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: |
70
+ This module creates an isomorphic representation of a UUID which is
71
+ guaranteed to fit into the grammar of the XML NCName construct, which
72
+ also happens to exhibit (modulo case and hyphens) the same constraints
73
+ as identifiers in nearly all programming languages. Provides case
74
+ sensitive (Base64) and case-insensitive (Base32) variants.
75
+ email:
76
+ - code@doriantaylor.com
77
+ executables: []
78
+ extensions: []
79
+ extra_rdoc_files: []
80
+ files:
81
+ - ".gitignore"
82
+ - ".rspec"
83
+ - ".travis.yml"
84
+ - Gemfile
85
+ - README.md
86
+ - Rakefile
87
+ - bin/console
88
+ - bin/setup
89
+ - lib/uuid/ncname.rb
90
+ - lib/uuid/ncname/version.rb
91
+ - uuid-ncname.gemspec
92
+ homepage: https://github.com/doriantaylor/rb-uuid-ncname
93
+ licenses: []
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.7.6
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Format a UUID as a valid NCName.
115
+ test_files: []