uuid-ncname 0.1.3 → 0.2.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/README.md +59 -0
- data/lib/uuid/ncname.rb +165 -53
- data/lib/uuid/ncname/version.rb +1 -1
- data/uuid-ncname.gemspec +3 -0
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 709119ee194cd0d230580aa34906af6ffcf2ff1d271ec431396d5ab8395a150b
|
|
4
|
+
data.tar.gz: e21a0cdd45a3d905c85d20469b63f0978a154bd8e7e791b2780b575c8e8a994f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7885b27a99f48e644a1f4152bdd8919e9dacac8a3109c466d8b87ba61d4934aa1761f381c17563c7c228083ec9ede908154c16572f5291f05f5b3c87d8977875
|
|
7
|
+
data.tar.gz: f2c6b86bfc74481ba896fa6e8504c7f6f312ab39831775e92465d7f0b09cd3087950ba768e0828d4a8e24e69cb43caf71e70b0125dc420475bf053abf31eba79
|
data/README.md
CHANGED
|
@@ -28,6 +28,65 @@ the constraints of various other identifiers such as NCName, and create an
|
|
|
28
28
|
[isomorphic](http://en.wikipedia.org/wiki/Isomorphism) mapping between
|
|
29
29
|
them.
|
|
30
30
|
|
|
31
|
+
## _FORMAT DEPRECATION NOTICE_
|
|
32
|
+
|
|
33
|
+
After careful consideration, I have decided to change the UUID-NCName
|
|
34
|
+
format in a minor yet incompatible way. In particular, I have moved
|
|
35
|
+
the nybble containing
|
|
36
|
+
the [`variant`](https://tools.ietf.org/html/rfc4122#section-4.1.1) to
|
|
37
|
+
the very end of the identifier, whereas it previously was mixed into
|
|
38
|
+
the middle somewhere.
|
|
39
|
+
|
|
40
|
+
This can be considered an application
|
|
41
|
+
of [Postel's Law](https://en.wikipedia.org/wiki/Postel%27s_law), based
|
|
42
|
+
on the assumption that these identifiers will be generated through
|
|
43
|
+
other methods, and potentially naïvely. Like the `version` field, the
|
|
44
|
+
`variant` field has a limited acceptable range of values. If, for
|
|
45
|
+
example, one were to attempt to generate a conforming identifier by
|
|
46
|
+
simply generating a random Base32 or Base64 string, it will be
|
|
47
|
+
difficult to ensure that the `variant` field will indeed conform when
|
|
48
|
+
the identifier is converted to a standard UUID. By moving the
|
|
49
|
+
`variant` field out to the end of the identifier, everything between
|
|
50
|
+
the `version` and `variant` bookends can be generated randomly without
|
|
51
|
+
any further consideration, like so:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
B64_ALPHA = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + %w(- _)
|
|
55
|
+
|
|
56
|
+
def make_cheapo_b64_uuid_ncname
|
|
57
|
+
vals = (1..20).map { rand 64 } # generate the content
|
|
58
|
+
vals.push(rand(4) + 8) # last digit is special
|
|
59
|
+
'E' + vals.map { |v| B64_ALPHA[v] }.join('') # 'E' for UUID v4
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# voilà:
|
|
63
|
+
|
|
64
|
+
cheap = make_cheapo_b64_uuid_ncname
|
|
65
|
+
# => "EXSVv8ezPbSKWoKOkBNWKL"
|
|
66
|
+
|
|
67
|
+
# now try changing it to a standard UUID:
|
|
68
|
+
|
|
69
|
+
UUID::NCName.from_ncname cheap, version: 1
|
|
70
|
+
# => "5d256ff1-eccf-46d2-b296-a0a3a404d58a"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Furthermore, since the default behaviour is to align the bits of the
|
|
74
|
+
last byte to the size of the encoding symbol, and since the `variant`
|
|
75
|
+
bits are masked, a compliant RFC4122 UUID will _always_ end with `I`,
|
|
76
|
+
`J`, `K`, or `L`, in _both_ Base32 (case-insensitive) and Base64
|
|
77
|
+
variants.
|
|
78
|
+
|
|
79
|
+
Since I have already released this gem prior to this format change, I
|
|
80
|
+
have added a `:version` parameter to both `to_ncname` and
|
|
81
|
+
`from_ncname`. The version currently defaults to `0`, the old one, but
|
|
82
|
+
will issue a warning if not explicitly set. Later I will change the
|
|
83
|
+
default to `1`, while keeping the warning, then later still, finally
|
|
84
|
+
remove the warning with 1 as the default. This should ensure that any
|
|
85
|
+
code written during the transition produces the correct results.
|
|
86
|
+
|
|
87
|
+
> Unless you have to support identifiers generated from version 0.1.3
|
|
88
|
+
> or newer, you should be running these methods with `version: 1`.
|
|
89
|
+
|
|
31
90
|
## Rationale & Method
|
|
32
91
|
|
|
33
92
|
The UUID is a generic identifier which is large enough to be globally
|
data/lib/uuid/ncname.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
1
2
|
require "uuid/ncname/version"
|
|
2
3
|
|
|
3
4
|
require 'base64'
|
|
@@ -8,34 +9,42 @@ module UUID::NCName
|
|
|
8
9
|
private
|
|
9
10
|
|
|
10
11
|
ENCODE = {
|
|
11
|
-
32 => -> bin {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
32 => -> (bin, align = true) {
|
|
13
|
+
if align
|
|
14
|
+
bin = bin.unpack 'C*'
|
|
15
|
+
bin[-1] >>= 1
|
|
16
|
+
bin = bin.pack 'C*'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
out = ::Base32.encode bin
|
|
15
20
|
|
|
16
21
|
out.downcase[0, 25]
|
|
17
22
|
},
|
|
18
|
-
64 => -> bin {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
64 => -> (bin, align = true) {
|
|
24
|
+
if align
|
|
25
|
+
bin = bin.unpack 'C*'
|
|
26
|
+
bin[-1] >>= 2
|
|
27
|
+
bin = bin.pack 'C*'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
out = ::Base64.urlsafe_encode64 bin
|
|
22
31
|
|
|
23
32
|
out[0, 21]
|
|
24
33
|
},
|
|
25
34
|
}
|
|
26
35
|
|
|
27
36
|
DECODE = {
|
|
28
|
-
32 => -> str {
|
|
37
|
+
32 => -> (str, align = true) {
|
|
29
38
|
str = str.upcase[0, 25] + 'A======'
|
|
30
39
|
out = ::Base32.decode(str).unpack 'C*'
|
|
31
|
-
out[-1] <<= 1
|
|
40
|
+
out[-1] <<= 1 if align
|
|
32
41
|
|
|
33
42
|
out.pack 'C*'
|
|
34
43
|
},
|
|
35
|
-
64 => -> str {
|
|
44
|
+
64 => -> (str, align = true) {
|
|
36
45
|
str = str[0, 21] + 'A=='
|
|
37
46
|
out = ::Base64.urlsafe_decode64(str).unpack 'C*'
|
|
38
|
-
out[-1] <<= 2
|
|
47
|
+
out[-1] <<= 2 if align
|
|
39
48
|
|
|
40
49
|
out.pack 'C*'
|
|
41
50
|
},
|
|
@@ -50,30 +59,62 @@ module UUID::NCName
|
|
|
50
59
|
bin: -> bin { bin },
|
|
51
60
|
}
|
|
52
61
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
62
|
+
TRANSFORM = [
|
|
63
|
+
# old version prior to shifting out the variant nybble
|
|
64
|
+
[
|
|
65
|
+
-> data {
|
|
66
|
+
list = data.unpack 'N4'
|
|
67
|
+
version = (list[1] & 0x0000f000) >> 12
|
|
68
|
+
list[1] = (list[1] & 0xffff0000) |
|
|
69
|
+
((list[1] & 0x00000fff) << 4) | (list[2] >> 28)
|
|
70
|
+
list[2] = (list[2] & 0x0fffffff) << 4 | (list[3] >> 28)
|
|
71
|
+
list[3] <<= 4
|
|
72
|
+
|
|
73
|
+
return version, list.pack('N4')
|
|
74
|
+
},
|
|
75
|
+
-> (version, data) {
|
|
76
|
+
version &= 0xf
|
|
77
|
+
|
|
78
|
+
list = data.unpack 'N4'
|
|
79
|
+
list[3] >>= 4
|
|
80
|
+
list[3] |= ((list[2] & 0xf) << 28)
|
|
81
|
+
list[2] >>= 4
|
|
82
|
+
list[2] |= ((list[1] & 0xf) << 28)
|
|
83
|
+
list[1] = (
|
|
84
|
+
list[1] & 0xffff0000) | (version << 12) | ((list[1] >> 4) & 0xfff)
|
|
85
|
+
|
|
86
|
+
list.pack 'N4'
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
# current version
|
|
90
|
+
[
|
|
91
|
+
-> data {
|
|
92
|
+
list = data.unpack 'N4'
|
|
93
|
+
version = (list[1] & 0x0000f000) >> 12
|
|
94
|
+
variant = (list[2] & 0xf0000000) >> 24
|
|
95
|
+
list[1] = (list[1] & 0xffff0000) |
|
|
96
|
+
((list[1] & 0x00000fff) << 4) | ((list[2] & 0x0fffffff) >> 24)
|
|
97
|
+
list[2] = (list[2] & 0x00ffffff) << 8 | (list[3] >> 24)
|
|
98
|
+
list[3] = (list[3] << 8) | variant
|
|
99
|
+
|
|
100
|
+
return version, list.pack('N4')
|
|
101
|
+
},
|
|
102
|
+
-> (version, data) {
|
|
103
|
+
version &= 0xf
|
|
104
|
+
|
|
105
|
+
list = data.unpack 'N4'
|
|
106
|
+
variant = (list[3] & 0xf0) << 24
|
|
107
|
+
list[3] >>= 8
|
|
108
|
+
list[3] |= ((list[2] & 0xff) << 24)
|
|
109
|
+
list[2] >>= 8
|
|
110
|
+
list[2] |= ((list[1] & 0xf) << 24) | variant
|
|
111
|
+
list[1] = (
|
|
112
|
+
list[1] & 0xffff0000) | (version << 12) | ((list[1] >> 4) & 0xfff)
|
|
113
|
+
|
|
114
|
+
list.pack 'N4'
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
]
|
|
77
118
|
|
|
78
119
|
def self.encode_version version
|
|
79
120
|
((version & 15) + 65).chr
|
|
@@ -83,6 +124,17 @@ module UUID::NCName
|
|
|
83
124
|
(version.upcase.ord - 65) % 16
|
|
84
125
|
end
|
|
85
126
|
|
|
127
|
+
def self.warn_version version
|
|
128
|
+
if version.nil?
|
|
129
|
+
warn 'Set an explicit :version to remove this warning. See documentation.'
|
|
130
|
+
version = 0
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
raise 'Version must be 0 or 1' unless [0, 1].include? version
|
|
134
|
+
|
|
135
|
+
version
|
|
136
|
+
end
|
|
137
|
+
|
|
86
138
|
public
|
|
87
139
|
|
|
88
140
|
# Converts a UUID (or object that when converted to a string looks
|
|
@@ -95,16 +147,36 @@ module UUID::NCName
|
|
|
95
147
|
#
|
|
96
148
|
# @param radix [32, 64] either the number 32 or the number 64.
|
|
97
149
|
#
|
|
150
|
+
# @param version [0, 1] An optional formatting version, where 0 is
|
|
151
|
+
# the naïve original version and 1 moves the `variant` nybble out
|
|
152
|
+
# to the end of the identifier. You will be warned if you do not
|
|
153
|
+
# set this parameter explicitly. The default is currently 0, but
|
|
154
|
+
# will change in the next version.
|
|
155
|
+
#
|
|
156
|
+
# @param align [true, false] Optional directive to treat the
|
|
157
|
+
# terminating character as aligned to the numerical base of the
|
|
158
|
+
# representation. Since the version nybble is removed from the
|
|
159
|
+
# string and the first 120 bits divide evenly into both Base32 and
|
|
160
|
+
# Base64, the overhang is only ever 4 bits. This means that when
|
|
161
|
+
# the terminating character is aligned, it will always be in the
|
|
162
|
+
# range of the letters A through P in (the RFC 3548/4648
|
|
163
|
+
# representations of) both Base32 and Base64. When `version` is 1
|
|
164
|
+
# and the terminating character is aligned, RFC4122-compliant UUIDs
|
|
165
|
+
# will always terminate with I, J, K, or L. Defaults to `true`.
|
|
166
|
+
#
|
|
98
167
|
# @return [String] The NCName-formatted UUID.
|
|
99
168
|
|
|
100
|
-
def self.to_ncname uuid, radix: 64
|
|
169
|
+
def self.to_ncname uuid, radix: 64, version: nil, align: true
|
|
101
170
|
raise 'Radix must be either 32 or 64' unless [32, 64].include? radix
|
|
102
171
|
raise 'UUID must be something stringable' if uuid.nil? or
|
|
103
172
|
not uuid.respond_to? :to_s
|
|
173
|
+
raise 'Align must be true or false' unless [true, false].include? align
|
|
104
174
|
|
|
105
|
-
|
|
175
|
+
# XXX remove this when appropriate
|
|
176
|
+
version = warn_version(version)
|
|
106
177
|
|
|
107
|
-
|
|
178
|
+
uuid = uuid.to_s
|
|
179
|
+
bin = nil
|
|
108
180
|
|
|
109
181
|
if uuid.length == 16
|
|
110
182
|
bin = uuid
|
|
@@ -113,7 +185,7 @@ module UUID::NCName
|
|
|
113
185
|
if (m = /^(?:urn:uuid:)?([0-9A-Fa-f-]{32,})$/.match(uuid))
|
|
114
186
|
bin = [m[1].tr('-', '')].pack 'H*'
|
|
115
187
|
elsif (m = /^([0-9A-Za-z+\/_-]+=*)$/.match(uuid))
|
|
116
|
-
match= m[1].tr('-_', '+/')
|
|
188
|
+
match = m[1].tr('-_', '+/')
|
|
117
189
|
bin = ::Base64.decode64(match)
|
|
118
190
|
else
|
|
119
191
|
raise "Not sure what to do with #{uuid}"
|
|
@@ -123,9 +195,9 @@ module UUID::NCName
|
|
|
123
195
|
raise 'Binary representation of UUID is shorter than 16 bytes' if
|
|
124
196
|
bin.length < 16
|
|
125
197
|
|
|
126
|
-
|
|
198
|
+
uuidver, content = TRANSFORM[version][0].call bin[0, 16]
|
|
127
199
|
|
|
128
|
-
encode_version(
|
|
200
|
+
encode_version(uuidver) + ENCODE[radix].call(content, align)
|
|
129
201
|
end
|
|
130
202
|
|
|
131
203
|
# Converts an NCName-encoded UUID back to its canonical
|
|
@@ -139,13 +211,25 @@ module UUID::NCName
|
|
|
139
211
|
#
|
|
140
212
|
# @param format [:str, :hex, :b64, :bin] An optional formatting
|
|
141
213
|
# parameter; defaults to `:str`, the canonical string representation.
|
|
214
|
+
#
|
|
215
|
+
# @param version [0, 1] See `to_ncname`. Defaults (for now) to 0.
|
|
142
216
|
#
|
|
217
|
+
# @param align [true, false, nil] See `to_ncname` for details.
|
|
218
|
+
# Setting this parameter to `nil`, the default, will cause the
|
|
219
|
+
# decoder to detect the alignment state from the identifier.
|
|
220
|
+
#
|
|
143
221
|
# @return [String, nil] The corresponding UUID or nil if the input
|
|
144
222
|
# is malformed.
|
|
145
223
|
|
|
146
|
-
def self.from_ncname ncname,
|
|
224
|
+
def self.from_ncname ncname,
|
|
225
|
+
radix: nil, format: :str, version: nil, align: nil
|
|
147
226
|
raise 'Format must be symbol-able' unless format.respond_to? :to_sym
|
|
148
227
|
raise "Invalid format #{format}" unless FORMAT[format]
|
|
228
|
+
raise 'Align must be true, false, or nil' unless
|
|
229
|
+
[true, false, nil].include? align
|
|
230
|
+
|
|
231
|
+
# XXX remove this when appropriate
|
|
232
|
+
version = warn_version version
|
|
149
233
|
|
|
150
234
|
return unless ncname and ncname.respond_to? :to_s
|
|
151
235
|
|
|
@@ -170,11 +254,13 @@ module UUID::NCName
|
|
|
170
254
|
end
|
|
171
255
|
end
|
|
172
256
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
content
|
|
257
|
+
uuidver, content = match.captures
|
|
258
|
+
|
|
259
|
+
align = !!(content =~ /[A-Pa-p]$/) if align.nil?
|
|
260
|
+
uuidver = decode_version uuidver
|
|
261
|
+
content = DECODE[radix].call content, align
|
|
176
262
|
|
|
177
|
-
bin =
|
|
263
|
+
bin = TRANSFORM[version][1].call uuidver, content
|
|
178
264
|
|
|
179
265
|
FORMAT[format].call bin
|
|
180
266
|
end
|
|
@@ -182,36 +268,62 @@ module UUID::NCName
|
|
|
182
268
|
# Shorthand for conversion to the Base64 version
|
|
183
269
|
#
|
|
184
270
|
# @param uuid [#to_s] The UUID
|
|
271
|
+
#
|
|
272
|
+
# @param version [0, 1] See `to_ncname`.
|
|
273
|
+
#
|
|
274
|
+
# @param align [true, false] See `to_ncname`.
|
|
275
|
+
#
|
|
185
276
|
# @return [String] The Base64-encoded NCName
|
|
186
277
|
|
|
187
|
-
def self.to_ncname_64 uuid
|
|
188
|
-
to_ncname uuid
|
|
278
|
+
def self.to_ncname_64 uuid, version: nil, align: true
|
|
279
|
+
to_ncname uuid, version: version, align: align
|
|
189
280
|
end
|
|
190
281
|
|
|
191
282
|
# Shorthand for conversion from the Base64 version
|
|
192
283
|
#
|
|
193
284
|
# @param ncname [#to_s] The Base64 variant of the NCName-encoded UUID
|
|
285
|
+
#
|
|
194
286
|
# @param format [:str, :hex, :b64, :bin] The format
|
|
287
|
+
#
|
|
288
|
+
# @param version [0, 1] See `to_ncname`.
|
|
289
|
+
#
|
|
290
|
+
# @param align [true, false] See `to_ncname`.
|
|
291
|
+
#
|
|
292
|
+
# @return [String, nil] The corresponding UUID or nil if the input
|
|
293
|
+
# is malformed.
|
|
195
294
|
|
|
196
|
-
def self.from_ncname_64 ncname, format: :str
|
|
295
|
+
def self.from_ncname_64 ncname, format: :str, version: nil, align: nil
|
|
197
296
|
from_ncname ncname, radix: 64, format: format
|
|
198
297
|
end
|
|
199
298
|
|
|
200
299
|
# Shorthand for conversion to the Base32 version
|
|
201
300
|
#
|
|
202
301
|
# @param uuid [#to_s] The UUID
|
|
302
|
+
#
|
|
303
|
+
# @param version [0, 1] See `to_ncname`.
|
|
304
|
+
#
|
|
305
|
+
# @param align [true, false] See `to_ncname`.
|
|
306
|
+
#
|
|
203
307
|
# @return [String] The Base32-encoded NCName
|
|
204
308
|
|
|
205
|
-
def self.to_ncname_32 uuid
|
|
206
|
-
to_ncname uuid, radix: 32
|
|
309
|
+
def self.to_ncname_32 uuid, version: nil, align: true
|
|
310
|
+
to_ncname uuid, radix: 32, version: version, align: align
|
|
207
311
|
end
|
|
208
312
|
|
|
209
313
|
# Shorthand for conversion from the Base32 version
|
|
210
314
|
#
|
|
211
315
|
# @param ncname [#to_s] The Base32 variant of the NCName-encoded UUID
|
|
316
|
+
#
|
|
212
317
|
# @param format [:str, :hex, :b64, :bin] The format
|
|
318
|
+
#
|
|
319
|
+
# @param version [0, 1] See `to_ncname`.
|
|
320
|
+
#
|
|
321
|
+
# @param align [true, false] See `to_ncname`.
|
|
322
|
+
#
|
|
323
|
+
# @return [String, nil] The corresponding UUID or nil if the input
|
|
324
|
+
# is malformed.
|
|
213
325
|
|
|
214
|
-
def self.from_ncname_32 ncname, format: :str
|
|
326
|
+
def self.from_ncname_32 ncname, format: :str, version: nil, align: nil
|
|
215
327
|
from_ncname ncname, radix: 32, format: format
|
|
216
328
|
end
|
|
217
329
|
|
data/lib/uuid/ncname/version.rb
CHANGED
data/uuid-ncname.gemspec
CHANGED
|
@@ -26,6 +26,9 @@ DESC
|
|
|
26
26
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
27
27
|
spec.require_paths = ["lib"]
|
|
28
28
|
|
|
29
|
+
# we use named parameters
|
|
30
|
+
spec.required_ruby_version = '~> 2.0'
|
|
31
|
+
|
|
29
32
|
# surprisingly do not need this
|
|
30
33
|
# spec.add_runtime_dependency 'uuidtools', '~> 2.1.5'
|
|
31
34
|
spec.add_runtime_dependency 'base32', '~> 0.3.2'
|
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.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dorian Taylor
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-
|
|
11
|
+
date: 2018-08-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base32
|
|
@@ -101,9 +101,9 @@ require_paths:
|
|
|
101
101
|
- lib
|
|
102
102
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
103
|
requirements:
|
|
104
|
-
- - "
|
|
104
|
+
- - "~>"
|
|
105
105
|
- !ruby/object:Gem::Version
|
|
106
|
-
version: '0'
|
|
106
|
+
version: '2.0'
|
|
107
107
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
108
|
requirements:
|
|
109
109
|
- - ">="
|