youyouaidi-revised 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.rubocop.yml +80 -0
- data/.travis.yml +9 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +22 -0
- data/README.md +111 -0
- data/Rakefile +7 -0
- data/lib/youyouaidi/converter.rb +68 -0
- data/lib/youyouaidi/invalid_uuid.rb +6 -0
- data/lib/youyouaidi/uuid.rb +75 -0
- data/lib/youyouaidi/version.rb +5 -0
- data/lib/youyouaidi.rb +10 -0
- data/spec/kernel_patch_spec.rb +11 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/youyouaidi/converter_spec.rb +103 -0
- data/spec/youyouaidi/uuid_spec.rb +261 -0
- data/youyouaidi-revised.gemspec +25 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0d00be139d3bf97f8c7e3dacd0877003cd2eb582c8ce08397dcd12a4f7dc7df7
|
4
|
+
data.tar.gz: 6b0a86e0cb1a3e1e416a3167fa00a0fcb99f2f8b4bab381f48def1fda72daa8e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b1c691c8e485b3954e1bc79316f70147c9207b56ff378ab1eaceba6faf06f478b7dcc51b866b884098e053b463baef709471206ec8501764f30245d5e119e747
|
7
|
+
data.tar.gz: b7049ffa8d4c7c805b0edc1426ff63dc5fe3d60a70389f333967fd6086346dfc94374cf034888f3e739a4d99a3dd171e545bca062a367eb328cc6b2ba7d743e8
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# .rubocop.yml
|
2
|
+
AllCops:
|
3
|
+
TargetRubyVersion: 2.7
|
4
|
+
|
5
|
+
Style/Documentation:
|
6
|
+
Enabled: false
|
7
|
+
|
8
|
+
|
9
|
+
Gemspec/DateAssignment: # (new in 1.10)
|
10
|
+
Enabled: true
|
11
|
+
Layout/LineEndStringConcatenationIndentation: # (new in 1.18)
|
12
|
+
Enabled: true
|
13
|
+
Layout/SpaceBeforeBrackets: # (new in 1.7)
|
14
|
+
Enabled: true
|
15
|
+
Lint/AmbiguousAssignment: # (new in 1.7)
|
16
|
+
Enabled: true
|
17
|
+
Lint/DeprecatedConstants: # (new in 1.8)
|
18
|
+
Enabled: true
|
19
|
+
Lint/DuplicateBranch: # (new in 1.3)
|
20
|
+
Enabled: true
|
21
|
+
Lint/DuplicateRegexpCharacterClassElement: # (new in 1.1)
|
22
|
+
Enabled: true
|
23
|
+
Lint/EmptyBlock: # (new in 1.1)
|
24
|
+
Enabled: true
|
25
|
+
Lint/EmptyClass: # (new in 1.3)
|
26
|
+
Enabled: true
|
27
|
+
Lint/EmptyInPattern: # (new in 1.16)
|
28
|
+
Enabled: true
|
29
|
+
Lint/LambdaWithoutLiteralBlock: # (new in 1.8)
|
30
|
+
Enabled: true
|
31
|
+
Lint/NoReturnInBeginEndBlocks: # (new in 1.2)
|
32
|
+
Enabled: true
|
33
|
+
Lint/NumberedParameterAssignment: # (new in 1.9)
|
34
|
+
Enabled: true
|
35
|
+
Lint/OrAssignmentToConstant: # (new in 1.9)
|
36
|
+
Enabled: true
|
37
|
+
Lint/RedundantDirGlobSort: # (new in 1.8)
|
38
|
+
Enabled: true
|
39
|
+
Lint/SymbolConversion: # (new in 1.9)
|
40
|
+
Enabled: true
|
41
|
+
Lint/ToEnumArguments: # (new in 1.1)
|
42
|
+
Enabled: true
|
43
|
+
Lint/TripleQuotes: # (new in 1.9)
|
44
|
+
Enabled: true
|
45
|
+
Lint/UnexpectedBlockArity: # (new in 1.5)
|
46
|
+
Enabled: true
|
47
|
+
Lint/UnmodifiedReduceAccumulator: # (new in 1.1)
|
48
|
+
Enabled: true
|
49
|
+
Naming/InclusiveLanguage: # (new in 1.18)
|
50
|
+
Enabled: true
|
51
|
+
Style/ArgumentsForwarding: # (new in 1.1)
|
52
|
+
Enabled: true
|
53
|
+
Style/CollectionCompact: # (new in 1.2)
|
54
|
+
Enabled: true
|
55
|
+
Style/DocumentDynamicEvalDefinition: # (new in 1.1)
|
56
|
+
Enabled: true
|
57
|
+
Style/EndlessMethod: # (new in 1.8)
|
58
|
+
Enabled: true
|
59
|
+
Style/HashConversion: # (new in 1.10)
|
60
|
+
Enabled: true
|
61
|
+
Style/HashExcept: # (new in 1.7)
|
62
|
+
Enabled: true
|
63
|
+
Style/IfWithBooleanLiteralBranches: # (new in 1.9)
|
64
|
+
Enabled: true
|
65
|
+
Style/InPatternThen: # (new in 1.16)
|
66
|
+
Enabled: true
|
67
|
+
Style/MultilineInPatternThen: # (new in 1.16)
|
68
|
+
Enabled: true
|
69
|
+
Style/NegatedIfElseCondition: # (new in 1.2)
|
70
|
+
Enabled: true
|
71
|
+
Style/NilLambda: # (new in 1.3)
|
72
|
+
Enabled: true
|
73
|
+
Style/QuotedSymbols: # (new in 1.16)
|
74
|
+
Enabled: true
|
75
|
+
Style/RedundantArgument: # (new in 1.4)
|
76
|
+
Enabled: true
|
77
|
+
Style/StringChars: # (new in 1.12)
|
78
|
+
Enabled: true
|
79
|
+
Style/SwapValues: # (new in 1.1)
|
80
|
+
Enabled: true
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
group :test do
|
6
|
+
gem 'coveralls', require: false
|
7
|
+
gem 'pry'
|
8
|
+
gem 'rake', '~> 10.1'
|
9
|
+
gem 'rspec'
|
10
|
+
gem 'rspec-collection_matchers'
|
11
|
+
gem 'rspec-its'
|
12
|
+
end
|
13
|
+
|
14
|
+
# Specify your gem's dependencies in youyouaidi.gemspec
|
15
|
+
gemspec
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Nicolas Fricke
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# Youyouaidi revised
|
2
|
+
|
3
|
+
`Youyouaidi revised` is a Ruby Gem that offers a UUID class for **generating**, **parsing**, **validating** and **converting** UUIDs into / from **shorter representations**.
|
4
|
+
|
5
|
+
This gem is a fork of the unmaintained repo at http://github.com/nicolas-fricke/youyouaidi. Many thanks to the original author! 😊
|
6
|
+
|
7
|
+
While a **UUID consists of 36 characters** – 32 hexadecimal characters, divided by four dashes into five subgroups – the **short representation** (invoked via `#to_short_string`) consists of exactly **22 digit and lower- and uppercase characters**.
|
8
|
+
|
9
|
+
This is what a valid, random (version 4) UUID looks like:
|
10
|
+
```
|
11
|
+
version either 8, 9
|
12
|
+
number a, or b
|
13
|
+
▼ ▼
|
14
|
+
caed3f49-b0ca-454b-adf8-5ee2a1764759
|
15
|
+
# chars in group: 8 | 4 | 4 | 4 | 12
|
16
|
+
```
|
17
|
+
As shown, the first digit of the third group indicates the UUID version.
|
18
|
+
The first digit of the fourth group always has to be one of either `8`, `9`, `a`, or `b`.
|
19
|
+
All other digits are randomly assigned hexadecimals.
|
20
|
+
|
21
|
+
And this is the same UUID in its short format: `6aUS5foeLu2VGDspRPc7bz`.
|
22
|
+
|
23
|
+
For UUID generation, the `SecureRandom.uuid` method is used which generates valid, random *version 4* UUIDs.
|
24
|
+
|
25
|
+
Find out more about UUIDs and the different versions on [Wikipedia](https://en.wikipedia.org/wiki/Uuid).
|
26
|
+
|
27
|
+
## Installation
|
28
|
+
|
29
|
+
Add this line to your application's Gemfile:
|
30
|
+
|
31
|
+
gem 'youyouaidi-revised'
|
32
|
+
|
33
|
+
And then execute:
|
34
|
+
|
35
|
+
$ bundle
|
36
|
+
|
37
|
+
Or install it yourself as:
|
38
|
+
|
39
|
+
$ gem install youyouaidi-revised
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
`UUID(...)` **was** patched in the kernel. Not anymore. We don't patch the kernel unless we have no other choice.
|
44
|
+
|
45
|
+
### Initializing UUIDs
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
uuid_string = '550e8400-e29b-41d4-a716-446655440000' # A valid UUID in string format, has exactly 32 hexadecimal characters in 5 groups
|
49
|
+
uuid_short = '2AuYQJcZeiIeCymkJ7tzTW' # Same UUID in its short format, has exactly 22 characters of [0-9a-zA-Z]
|
50
|
+
|
51
|
+
uuid = Youyouaidi::UUID uuid_string # creates new Youyouaidi::UUID object
|
52
|
+
# => #<Youyouaidi::UUID:0x000001021f2590 @converter=Youyouaidi::Converter, @uuid="550e8400-e29b-41d4-a716-446655440000">
|
53
|
+
|
54
|
+
# Alternatively a short UUID can be passed:
|
55
|
+
uuid = Youyouaidi::UUID uuid_short # creates similar Youyouaidi::UUID object
|
56
|
+
# => #<Youyouaidi::UUID:0x00000102201b80 @converter=Youyouaidi::Converter, @uuid="550e8400-e29b-41d4-a716-446655440000">
|
57
|
+
|
58
|
+
# To generate a new random UUID simply do not pass a parameter:
|
59
|
+
new_uuid = Youyouaidi::UUID.new # generates a random UUID version 4 using the SecureRandom.uuid method
|
60
|
+
# => #<Youyouaidi::UUID:0x00000102201b80 @converter=Youyouaidi::Converter, @uuid="27f8bc29-be8e-4dc7-ab30-0295b2a5e902">
|
61
|
+
```
|
62
|
+
|
63
|
+
|
64
|
+
### Validity check and conversions
|
65
|
+
|
66
|
+
The validity check `Youyouaidi::UUID.valid? uuid_string` checks, if UUID contains exactly 32 hexadecimal characters which are divided by four dashes ('-') into five groups of sizes 8, 4, 4, 4, and 12.
|
67
|
+
Also, it validates that the first character of the fourth group is either a `8`, `9`, an `a`, or a `b`.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
uuid_string = '550e8400-e29b-41d4-a716-446655440000' # A valid UUID in string format
|
71
|
+
uuid = Youyouaidi::UUID uuid_string
|
72
|
+
|
73
|
+
Youyouaidi::UUID.valid? uuid_string # Checks if `uuid_string' is a valid UUID, same as Youyouaidi::UUID.valid? uuid_string
|
74
|
+
# => true
|
75
|
+
|
76
|
+
uuid.to_s # Returns the string representation of the UUID object
|
77
|
+
# => '550e8400-e29b-41d4-a716-446655440000'
|
78
|
+
|
79
|
+
uuid.to_short_string # Returns the short string representation of the UUID object, #to_param is an alias for this method
|
80
|
+
# => '2AuYQJcZeiIeCymkJ7tzTW'
|
81
|
+
```
|
82
|
+
|
83
|
+
|
84
|
+
### Comparing UUIDs
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
uuid_string = '550e8400-e29b-41d4-a716-446655440000' # A valid UUID in string format
|
88
|
+
uuid = Youyouaidi::UUID.parse uuid_string
|
89
|
+
similar_uuid = Youyouaidi::UUID.parse uuid_string
|
90
|
+
other_uuid = Youyouaidi::UUID.parse '00000000-1111-2222-aaaa-eeeeeeeeeeee'
|
91
|
+
|
92
|
+
uuid == similar_uuid # Two UUID objects representing same UUID (#=== behaves similar for this)
|
93
|
+
# => true
|
94
|
+
|
95
|
+
uuid == other_uuid # Two UUID objects representing different UUIDs (#=== behaves similar for this)
|
96
|
+
# => false
|
97
|
+
|
98
|
+
uuid == uuid_string # Comparing a UUID object and its string representation with `=='
|
99
|
+
# => false
|
100
|
+
|
101
|
+
uuid === uuid_string # Comparing a UUID object and its string representation with `===' (case insensetive)
|
102
|
+
# => true
|
103
|
+
```
|
104
|
+
|
105
|
+
## Contributing
|
106
|
+
|
107
|
+
1. Fork it ( http://github.com/lorankloeze/youyouaidi-revised/fork )
|
108
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
109
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
110
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
111
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Based on Base62 module by sinefunc
|
4
|
+
# => https://github.com/sinefunc/base62
|
5
|
+
module Youyouaidi
|
6
|
+
class Converter
|
7
|
+
class << self
|
8
|
+
def encode(uuid)
|
9
|
+
base_encode uuid.to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
def decode(encoded_uuid)
|
13
|
+
encoded_uuid = encoded_uuid.to_s
|
14
|
+
unless encoded_uuid.length == ENCODED_LENGTH
|
15
|
+
raise Youyouaidi::InvalidUUIDError,
|
16
|
+
"`#{encoded_uuid}' needs to have exactly #{ENCODED_LENGTH} characters (has #{encoded_uuid.length})"
|
17
|
+
end
|
18
|
+
|
19
|
+
begin
|
20
|
+
Youyouaidi::UUID.new convert_bignum_to_uuid_string base_decode encoded_uuid
|
21
|
+
rescue Youyouaidi::InvalidUUIDError => e
|
22
|
+
raise Youyouaidi::InvalidUUIDError, "`#{encoded_uuid}' could not be decoded to a valid UUID (#{e.message})"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
BASE = ('0'..'9').to_a + ('a'..'z').to_a + ('A'..'Z').to_a
|
29
|
+
ENCODED_LENGTH = 22 # Needs to be greater than `(Math.log 2**128, BASE.length).floor + 1`
|
30
|
+
|
31
|
+
def base_encode(numeric)
|
32
|
+
raise Youyouaidi::InvalidUUIDError, "`#{numeric}' needs to be a Numeric" unless numeric.is_a? Numeric
|
33
|
+
|
34
|
+
return '0' if numeric.zero?
|
35
|
+
|
36
|
+
s = String.new
|
37
|
+
|
38
|
+
while numeric.positive?
|
39
|
+
s << BASE[numeric.modulo(BASE.size)]
|
40
|
+
numeric /= BASE.size
|
41
|
+
end
|
42
|
+
s << BASE[0] while s.length < ENCODED_LENGTH
|
43
|
+
s.reverse
|
44
|
+
end
|
45
|
+
|
46
|
+
def base_decode(encoded_numeric)
|
47
|
+
s = encoded_numeric.to_s.reverse.chars
|
48
|
+
|
49
|
+
total = 0
|
50
|
+
s.each_with_index do |char, index|
|
51
|
+
ord = BASE.index(char)
|
52
|
+
unless ord
|
53
|
+
(raise Youyouaidi::InvalidUUIDError, "`#{encoded_numeric}' has `#{char}' which is not a valid character")
|
54
|
+
end
|
55
|
+
|
56
|
+
total += ord * (BASE.size**index)
|
57
|
+
end
|
58
|
+
total
|
59
|
+
end
|
60
|
+
|
61
|
+
def convert_bignum_to_uuid_string(decoded_uuid_bignum)
|
62
|
+
decoded_uuid = decoded_uuid_bignum.to_i.to_s(16).rjust(32, '0')
|
63
|
+
"#{decoded_uuid[0,
|
64
|
+
8]}-#{decoded_uuid[8, 4]}-#{decoded_uuid[12, 4]}-#{decoded_uuid[16, 4]}-#{decoded_uuid[20, 12]}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Youyouaidi
|
6
|
+
# Main class
|
7
|
+
class UUID
|
8
|
+
attr_reader :uuid
|
9
|
+
|
10
|
+
def initialize(uuid_string = nil, options = {})
|
11
|
+
@converter = options[:converter] || Youyouaidi::Converter
|
12
|
+
if uuid_string
|
13
|
+
initialize_with_uuid_string uuid_string
|
14
|
+
else
|
15
|
+
initialize_without_param
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
return false unless other.is_a? self.class
|
21
|
+
|
22
|
+
to_s == other.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def ===(other_object)
|
26
|
+
return true if self == other_object
|
27
|
+
|
28
|
+
to_s == other_object.to_s.downcase
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_i
|
32
|
+
@uuid.gsub('-', '').hex
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
@uuid
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_short_string
|
40
|
+
@converter.encode self
|
41
|
+
end
|
42
|
+
alias to_param to_short_string
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def initialize_without_param
|
47
|
+
@uuid = SecureRandom.uuid
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize_with_uuid_string(uuid_string)
|
51
|
+
unless self.class.valid? uuid_string
|
52
|
+
raise Youyouaidi::InvalidUUIDError, "`#{uuid_string}' does not look like a valid UUID"
|
53
|
+
end
|
54
|
+
|
55
|
+
@uuid = uuid_string.to_s.downcase
|
56
|
+
end
|
57
|
+
|
58
|
+
class << self
|
59
|
+
def parse(uuid_param = nil, options = {})
|
60
|
+
@converter = options[:converter] || Youyouaidi::Converter
|
61
|
+
if valid? uuid_param
|
62
|
+
new uuid_param.to_s, options
|
63
|
+
elsif uuid_param.nil?
|
64
|
+
new nil, options
|
65
|
+
else
|
66
|
+
@converter.decode uuid_param
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def valid?(uuid_canidate)
|
71
|
+
!(uuid_canidate.to_s =~ /^[\da-f]{8}(-[\da-f]{4}){2}-[89ab][\da-f]{3}-[\da-f]{12}$/i).nil?
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/youyouaidi.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'rspec/its'
|
5
|
+
require 'rspec/collection_matchers'
|
6
|
+
require 'pry'
|
7
|
+
|
8
|
+
# Code coverage statistics at coveralls.io: https://coveralls.io/r/nicolas-fricke/youyouaidi
|
9
|
+
# Do not generate coverage when using Rubinius, see: https://github.com/lemurheavy/coveralls-public/issues/144
|
10
|
+
if ENV['CI'] || (defined?(:RUBY_ENGINE) && RUBY_ENGINE != 'rbx')
|
11
|
+
require 'coveralls'
|
12
|
+
Coveralls.wear! do
|
13
|
+
add_filter 'spec'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'bundler'
|
18
|
+
Bundler.require
|
19
|
+
|
20
|
+
require 'youyouaidi'
|
21
|
+
|
22
|
+
RSpec.configure do |config|
|
23
|
+
# ## Mock Framework
|
24
|
+
#
|
25
|
+
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
|
26
|
+
#
|
27
|
+
# config.mock_with :mocha
|
28
|
+
# config.mock_with :flexmock
|
29
|
+
# config.mock_with :rr
|
30
|
+
|
31
|
+
# Run specs in random order to surface order dependencies. If you find an
|
32
|
+
# order dependency and want to debug it, you can fix the order by providing
|
33
|
+
# the seed, which is printed after each run.
|
34
|
+
# --seed 1234
|
35
|
+
config.order = 'random'
|
36
|
+
|
37
|
+
# Raise error when using old :should expectation syntax.
|
38
|
+
# config.raise_errors_for_deprecations!
|
39
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
# rubocop:disable Metrics/BlockLength
|
6
|
+
describe Youyouaidi::Converter do
|
7
|
+
shared_examples_for 'a call that raises a Youyouaidi::InvalidUUIDError with a meaningful error message' do
|
8
|
+
describe 'raises error' do
|
9
|
+
subject { -> { action } }
|
10
|
+
it { should raise_error Youyouaidi::InvalidUUIDError }
|
11
|
+
describe 'error message' do
|
12
|
+
let(:caught_error) do
|
13
|
+
action
|
14
|
+
return nil
|
15
|
+
rescue Youyouaidi::InvalidUUIDError => e
|
16
|
+
return e
|
17
|
+
end
|
18
|
+
subject { caught_error }
|
19
|
+
it { should_not be_nil }
|
20
|
+
its(:message) { should include(*error_message_includes) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:uuid) { Youyouaidi::UUID.new uuid_string }
|
26
|
+
let(:uuid_string) { '550e8400-e29b-41d4-a716-446655440000' }
|
27
|
+
let(:encoded_uuid) { '2AuYQJcZeiIeCymkJ7tzTW' }
|
28
|
+
|
29
|
+
uuids_with_encoding = {
|
30
|
+
'550e8400-e29b-41d4-a716-446655440000' => '2AuYQJcZeiIeCymkJ7tzTW',
|
31
|
+
'00000000-bbbb-2222-8888-000000000000' => '000001dyObGywDRlcScExy'
|
32
|
+
}
|
33
|
+
|
34
|
+
describe 'use case' do
|
35
|
+
subject { described_class.decode described_class.encode(uuid) }
|
36
|
+
|
37
|
+
it { should be_a Youyouaidi::UUID }
|
38
|
+
its(:to_s) { should eq uuid_string }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'methods' do
|
42
|
+
describe '.encode' do
|
43
|
+
subject { described_class.encode uuid }
|
44
|
+
|
45
|
+
uuids_with_encoding.each do |valid_uuid, encoded_uuid|
|
46
|
+
context "for uuid `#{valid_uuid}'" do
|
47
|
+
let(:uuid_string) { valid_uuid }
|
48
|
+
it { should have(22).characters }
|
49
|
+
it { should eq encoded_uuid }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '.decode' do
|
55
|
+
let(:action) { described_class.decode encoded_param }
|
56
|
+
|
57
|
+
context 'with valid param' do
|
58
|
+
subject { action }
|
59
|
+
|
60
|
+
uuids_with_encoding.each do |valid_uuid, encoded_uuid|
|
61
|
+
context "for encoded uuid `#{encoded_uuid}'" do
|
62
|
+
let(:encoded_param) { encoded_uuid }
|
63
|
+
it { should be_a Youyouaidi::UUID }
|
64
|
+
its(:to_s) { should eq valid_uuid }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with invalid param' do
|
70
|
+
subject { -> { action } }
|
71
|
+
context 'looking correct but converting to a non-valid UUID' do
|
72
|
+
let(:encoded_param) { 'qEsRTcgdJFVugbBqtaEoNf' }
|
73
|
+
let(:decoded_invalid_uuid) { '36bb9153-3a5a-1570-de9d-6faed4f1ceb0' }
|
74
|
+
let(:error_message_includes) { ["`#{encoded_param}'", "`#{decoded_invalid_uuid}'"] }
|
75
|
+
it_behaves_like 'a call that raises a Youyouaidi::InvalidUUIDError with a meaningful error message'
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'with invalid characters' do
|
79
|
+
let(:invalid_char) { '_' }
|
80
|
+
let(:encoded_param) { "#{invalid_char}#{encoded_uuid[1..]}" }
|
81
|
+
let(:error_message_includes) { ["`#{encoded_param}'", "`#{invalid_char}'"] }
|
82
|
+
it_behaves_like 'a call that raises a Youyouaidi::InvalidUUIDError with a meaningful error message'
|
83
|
+
end
|
84
|
+
|
85
|
+
shared_examples_for 'a call with incorrect param string length' do
|
86
|
+
let(:error_message_includes) { ["`#{encoded_param}'", '22', encoded_param.length.to_s] }
|
87
|
+
it_behaves_like 'a call that raises a Youyouaidi::InvalidUUIDError with a meaningful error message'
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'with too long encoded param' do
|
91
|
+
let(:encoded_param) { "#{encoded_uuid}abc" }
|
92
|
+
it_behaves_like 'a call with incorrect param string length'
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'with too short encoded param' do
|
96
|
+
let(:encoded_param) { encoded_uuid[0..-3].to_s }
|
97
|
+
it_behaves_like 'a call with incorrect param string length'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
# rubocop:enable Metrics/BlockLength
|
@@ -0,0 +1,261 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
# rubocop:disable Metrics/BlockLength
|
6
|
+
describe Youyouaidi::UUID do
|
7
|
+
shared_examples_for 'a call that raises a Youyouaidi::InvalidUUIDError with a meaningful error message' do
|
8
|
+
describe 'raises error' do
|
9
|
+
subject { -> { action } }
|
10
|
+
it { should raise_error Youyouaidi::InvalidUUIDError }
|
11
|
+
describe 'error message' do
|
12
|
+
let(:caught_error) do
|
13
|
+
action
|
14
|
+
return nil
|
15
|
+
rescue Youyouaidi::InvalidUUIDError => e
|
16
|
+
return e
|
17
|
+
end
|
18
|
+
subject { caught_error }
|
19
|
+
it { should_not be_nil }
|
20
|
+
its(:message) { should include(*error_message_includes) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '.new' do
|
26
|
+
let(:param) { '' }
|
27
|
+
let(:action) { Youyouaidi::UUID.new param }
|
28
|
+
subject { -> { action } }
|
29
|
+
|
30
|
+
context 'without a param' do
|
31
|
+
let(:action) { Youyouaidi::UUID.new }
|
32
|
+
subject { action }
|
33
|
+
|
34
|
+
it { should be_a Youyouaidi::UUID }
|
35
|
+
describe 'having a valid UUID version 4' do
|
36
|
+
subject { action.to_s }
|
37
|
+
|
38
|
+
it { should be_a String }
|
39
|
+
it { should have(36).characters }
|
40
|
+
it { should match(/[\da-f]{8}-[\da-f]{4}-4[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}/i) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'with valid uuid string' do
|
45
|
+
subject { action }
|
46
|
+
|
47
|
+
valid_uuids = %w[550e8400-e29b-41d4-a716-446655440000
|
48
|
+
00000000-bbbb-2222-8888-000000000000]
|
49
|
+
|
50
|
+
valid_uuids.each do |valid_uuid|
|
51
|
+
context "for uuid `#{valid_uuid}'" do
|
52
|
+
let(:param) { valid_uuid }
|
53
|
+
it { should be_a Youyouaidi::UUID }
|
54
|
+
its(:to_s) { should eq param }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'with valid uuid in short format' do
|
60
|
+
let(:param) { '2AuYQJcZeiIeCymkJ7tzTW' }
|
61
|
+
let(:error_message_includes) { "`#{param}'" }
|
62
|
+
it_behaves_like 'a call that raises a Youyouaidi::InvalidUUIDError with a meaningful error message'
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'with invalid uuid string' do
|
66
|
+
let(:param) { 'Kekse' }
|
67
|
+
let(:error_message_includes) { "`#{param}'" }
|
68
|
+
it_behaves_like 'a call that raises a Youyouaidi::InvalidUUIDError with a meaningful error message'
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with non-uuid-string input' do
|
72
|
+
let(:param) { 1234 }
|
73
|
+
let(:error_message_includes) { "`#{param}'" }
|
74
|
+
it_behaves_like 'a call that raises a Youyouaidi::InvalidUUIDError with a meaningful error message'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '.parse' do
|
79
|
+
let(:param) { '' }
|
80
|
+
let(:action) { Youyouaidi::UUID.parse param }
|
81
|
+
subject { action }
|
82
|
+
|
83
|
+
context 'with valid uuid string' do
|
84
|
+
let(:param) { '550e8400-e29b-41d4-a716-446655440000' }
|
85
|
+
|
86
|
+
it { should be_a Youyouaidi::UUID }
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'with uuid in short format' do
|
90
|
+
let(:param) { '2AuYQJcZeiIeCymkJ7tzTW' }
|
91
|
+
let(:decoded_uuid) { '550e8400-e29b-41d4-a716-446655440000' }
|
92
|
+
|
93
|
+
it { should be_a Youyouaidi::UUID }
|
94
|
+
its(:to_s) { should eq decoded_uuid }
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'with invalid uuid string' do
|
98
|
+
let(:param) { 'Kekse' }
|
99
|
+
let(:error_message_includes) { "`#{param}'" }
|
100
|
+
it_behaves_like 'a call that raises a Youyouaidi::InvalidUUIDError with a meaningful error message'
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'with non-string input' do
|
104
|
+
let(:param) { 1234 }
|
105
|
+
let(:error_message_includes) { "`#{param}'" }
|
106
|
+
it_behaves_like 'a call that raises a Youyouaidi::InvalidUUIDError with a meaningful error message'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
shared_examples_for 'equality check for two UUID objects' do
|
111
|
+
let(:first_uuid_string) { 'aaaaaaaa-eeee-4444-aaaa-444444444444' }
|
112
|
+
let(:second_uuid_string) { '00000000-bbbb-2222-8888-000000000000' }
|
113
|
+
let(:first_uuid) { Youyouaidi::UUID.new first_uuid_string }
|
114
|
+
let(:second_uuid) { Youyouaidi::UUID.new second_uuid_string }
|
115
|
+
|
116
|
+
subject { action }
|
117
|
+
|
118
|
+
context 'passing a UUID object' do
|
119
|
+
context 'when comparing same instance' do
|
120
|
+
let(:action) { first_uuid.send described_method, first_uuid }
|
121
|
+
it { should be_truthy }
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'when comparing different instances' do
|
125
|
+
let(:action) { first_uuid.send described_method, second_uuid }
|
126
|
+
context 'with same UUID strings' do
|
127
|
+
let(:second_uuid_string) { first_uuid_string }
|
128
|
+
it { should be_truthy }
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'with different UUID strings' do
|
132
|
+
it { should be_falsey }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#== (equal operator)' do
|
139
|
+
let(:uuid_string) { 'aaaaaaaa-eeee-4444-aaaa-444444444444' }
|
140
|
+
let(:uuid) { Youyouaidi::UUID.new uuid_string }
|
141
|
+
|
142
|
+
let(:described_method) { :== }
|
143
|
+
it_behaves_like 'equality check for two UUID objects'
|
144
|
+
|
145
|
+
subject { action }
|
146
|
+
|
147
|
+
context 'passing a non-UUID object' do
|
148
|
+
let(:action) { uuid == test_object }
|
149
|
+
|
150
|
+
context 'when this is the UUID as string' do
|
151
|
+
let(:test_object) { uuid_string }
|
152
|
+
it { should be_falsey }
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'when this is a random other object' do
|
156
|
+
let(:test_object) { '123' }
|
157
|
+
it { should be_falsey }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe '#=== (equal operator)' do
|
163
|
+
let(:uuid_string) { 'aaaaaaaa-eeee-4444-aaaa-444444444444' }
|
164
|
+
let(:uuid) { Youyouaidi::UUID.new uuid_string }
|
165
|
+
|
166
|
+
let(:described_method) { :=== }
|
167
|
+
it_behaves_like 'equality check for two UUID objects'
|
168
|
+
|
169
|
+
subject { action }
|
170
|
+
|
171
|
+
context 'passing a non-UUID object' do
|
172
|
+
# rubocop:disable Style/CaseEquality
|
173
|
+
let(:action) { uuid === test_object }
|
174
|
+
# rubocop:enable Style/CaseEquality
|
175
|
+
|
176
|
+
context 'when this is the same UUID as string' do
|
177
|
+
context 'when string is upcase' do
|
178
|
+
let(:test_object) { uuid_string.upcase }
|
179
|
+
it { should be_truthy }
|
180
|
+
end
|
181
|
+
context 'when string is downcase' do
|
182
|
+
let(:test_object) { uuid_string.downcase }
|
183
|
+
it { should be_truthy }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context 'when this is a random other object' do
|
188
|
+
let(:test_object) { '123' }
|
189
|
+
it { should be_falsey }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe '#to_s' do
|
195
|
+
let(:uuid_string) { '550e8400-e29b-41d4-a716-446655440000' }
|
196
|
+
let(:uuid) { Youyouaidi::UUID.new uuid_string }
|
197
|
+
subject { uuid.to_s }
|
198
|
+
|
199
|
+
it { should be_a String }
|
200
|
+
it { should eq uuid_string }
|
201
|
+
end
|
202
|
+
|
203
|
+
describe '#to_i' do
|
204
|
+
let(:uuid_string) { '550e8400-e29b-41d4-a716-446655440000' }
|
205
|
+
let(:uuid) { Youyouaidi::UUID.new uuid_string }
|
206
|
+
subject { uuid.to_i }
|
207
|
+
|
208
|
+
it { should be_a Integer }
|
209
|
+
it { should eq 113_059_749_145_936_325_402_354_257_176_981_405_696 }
|
210
|
+
end
|
211
|
+
|
212
|
+
shared_examples_for 'method for short format' do
|
213
|
+
let(:uuid_string) { '550e8400-e29b-41d4-a716-446655440000' }
|
214
|
+
let(:encoded_uuid) { '2AuYQJcZeiIeCymkJ7tzTW' }
|
215
|
+
let(:uuid) { Youyouaidi::UUID.new uuid_string }
|
216
|
+
|
217
|
+
let(:action) { uuid.send method }
|
218
|
+
subject { action }
|
219
|
+
|
220
|
+
it { should be_a String }
|
221
|
+
it { should eq encoded_uuid }
|
222
|
+
end
|
223
|
+
describe '#to_short_string' do
|
224
|
+
let(:method) { :to_short_string }
|
225
|
+
it_behaves_like 'method for short format'
|
226
|
+
end
|
227
|
+
describe '#to_param' do
|
228
|
+
let(:method) { :to_param }
|
229
|
+
it_behaves_like 'method for short format'
|
230
|
+
end
|
231
|
+
|
232
|
+
describe '.valid?' do
|
233
|
+
let(:param) { '' }
|
234
|
+
subject { Youyouaidi::UUID.valid? param }
|
235
|
+
|
236
|
+
context 'with valid uuid' do
|
237
|
+
valid_uuids = %w[550e8400-e29b-41d4-a716-446655440000
|
238
|
+
27f8bc29-be8e-4dc7-8b30-0295b2a5e902]
|
239
|
+
|
240
|
+
valid_uuids.each do |valid_uuid|
|
241
|
+
it "should return true for `#{valid_uuid}`" do
|
242
|
+
expect(Youyouaidi::UUID.valid?(valid_uuid)).to eq true
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context 'with invalid uuid' do
|
248
|
+
uuid_string = '550e8400-e29b-41d4-a716-446655440000'
|
249
|
+
encoded_uuid = '2AuYQJcZeiIeCymkJ7tzTW'
|
250
|
+
invalid_uuids = ['Kekse', "aa#{uuid_string}", "#{uuid_string}bb",
|
251
|
+
encoded_uuid.to_s, '550e8400-e29b-41d4-2716-446655440000']
|
252
|
+
|
253
|
+
invalid_uuids.each do |invalid_uuid|
|
254
|
+
it "should return false for `#{invalid_uuid}`" do
|
255
|
+
expect(Youyouaidi::UUID.valid?(invalid_uuid)).to eq false
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
# rubocop:enable Metrics/BlockLength
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'youyouaidi/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'youyouaidi-revised'
|
9
|
+
spec.version = Youyouaidi::VERSION
|
10
|
+
spec.authors = ['Nicolas Fricke', 'Loran Kloeze']
|
11
|
+
spec.email = ['mail@nicolasfricke.de', 'loran@freedomnet.nl']
|
12
|
+
spec.summary = 'UUID class'
|
13
|
+
spec.description = 'Youyouaidi offers a UUID class for parsing, validating and encoding UUIDs'
|
14
|
+
spec.homepage = 'https://github.com/LoranKloeze/youyouaidi-revised'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
spec.required_ruby_version = '>= 2.7.4'
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.5'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: youyouaidi-revised
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nicolas Fricke
|
8
|
+
- Loran Kloeze
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2021-11-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '2.0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '2.0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '10.5'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '10.5'
|
42
|
+
description: Youyouaidi offers a UUID class for parsing, validating and encoding UUIDs
|
43
|
+
email:
|
44
|
+
- mail@nicolasfricke.de
|
45
|
+
- loran@freedomnet.nl
|
46
|
+
executables: []
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- ".gitignore"
|
51
|
+
- ".rspec"
|
52
|
+
- ".rubocop.yml"
|
53
|
+
- ".travis.yml"
|
54
|
+
- Gemfile
|
55
|
+
- LICENSE.txt
|
56
|
+
- README.md
|
57
|
+
- Rakefile
|
58
|
+
- lib/youyouaidi.rb
|
59
|
+
- lib/youyouaidi/converter.rb
|
60
|
+
- lib/youyouaidi/invalid_uuid.rb
|
61
|
+
- lib/youyouaidi/uuid.rb
|
62
|
+
- lib/youyouaidi/version.rb
|
63
|
+
- spec/kernel_patch_spec.rb
|
64
|
+
- spec/spec_helper.rb
|
65
|
+
- spec/youyouaidi/converter_spec.rb
|
66
|
+
- spec/youyouaidi/uuid_spec.rb
|
67
|
+
- youyouaidi-revised.gemspec
|
68
|
+
homepage: https://github.com/LoranKloeze/youyouaidi-revised
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 2.7.4
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubygems_version: 3.1.6
|
88
|
+
signing_key:
|
89
|
+
specification_version: 4
|
90
|
+
summary: UUID class
|
91
|
+
test_files:
|
92
|
+
- spec/kernel_patch_spec.rb
|
93
|
+
- spec/spec_helper.rb
|
94
|
+
- spec/youyouaidi/converter_spec.rb
|
95
|
+
- spec/youyouaidi/uuid_spec.rb
|