lazy-uuid 0.5.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.
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2
4
+ - jruby-9
5
+ before_install: gem install bundler
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Gem dependencies and information defined in lazy-uuid.gemspec
4
+ gemspec
@@ -0,0 +1,17 @@
1
+ The MIT License (MIT)
2
+ =====================
3
+
4
+ *Copyright (c) 2016*
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
8
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
9
+ persons to whom the Software is furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
+
@@ -0,0 +1,100 @@
1
+ Lazy UUID
2
+ =========
3
+
4
+ [![Travis CI](https://travis-ci.org/bluepixelmike/lazy-uuid.svg)](https://travis-ci.org/bluepixelmike/lazy-uuid)
5
+ [![Code Climate](https://codeclimate.com/github/bluepixelmike/lazy-uuid/badges/gpa.svg)](https://codeclimate.com/github/bluepixelmike/lazy-uuid)
6
+ [![Test Coverage](https://codeclimate.com/github/bluepixelmike/lazy-uuid/badges/coverage.svg)](https://codeclimate.com/github/bluepixelmike/lazy-uuid/coverage)
7
+ [![Issue Count](https://codeclimate.com/github/bluepixelmike/lazy-uuid/badges/issue_count.svg)](https://codeclimate.com/github/bluepixelmike/lazy-uuid)
8
+ [![Documentation](https://inch-ci.org/github/bluepixelmike/lazy-uuid.svg?branch=master)](http://www.rubydoc.info/github/bluepixelmike/lazy-uuid/master)
9
+
10
+ Small gem for creating and using UUIDs (universally unique identifier).
11
+ It has no external dependencies.
12
+
13
+ Installation
14
+ ------------
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'lazy-uuid'
20
+ ```
21
+
22
+ Or to your gemspec:
23
+
24
+ ```ruby
25
+ spec.add_dependency 'lazy-uuid'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ $ bundle
31
+
32
+ Or install it yourself as:
33
+
34
+ $ gem install lazy-uuid
35
+
36
+ Usage
37
+ -----
38
+
39
+ ### Script Usage
40
+
41
+ Include the `Uuid` class by doing:
42
+
43
+ ```ruby
44
+ require 'lazy-uuid'
45
+ ```
46
+
47
+ To generate a new UUID:
48
+
49
+ ```ruby
50
+ uuid = Uuid.generate
51
+ ```
52
+
53
+ To create a new UUID from a value:
54
+
55
+ ```ruby
56
+ value = "\xde\x30\x5d\x54\x75\xb4\x43\x1b\xad\xb2\xeb\x6b\x9e\x54\x60\x14"
57
+ uuid = Uuid.new(value)
58
+ ```
59
+
60
+ To parse an existing UUID string:
61
+ ```ruby
62
+ str = 'de305d54-75b4-431b-adb2-eb6b9e546014'
63
+ uuid = Uuid.parse(str)
64
+ ```
65
+
66
+ Use `#to_s` to generate a human-readable representation of the UUID.
67
+
68
+ ### Command-line Usage
69
+
70
+ Run the `generate-uuid` script to generate and output a UUID.
71
+
72
+ $ generate-uuid
73
+ c06b98c9-4cd6-4265-97ef-3299027cde88
74
+
75
+ Or pass a number as an argument to generate multiple UUIDs.
76
+
77
+ $ generate-uuid 5
78
+ cf803c2a-a04b-45ff-be3b-294b70a5d8d5
79
+ 4e5a5172-79f4-4cd2-9649-fc5f6b682679
80
+ 62b4226d-23fb-4e5f-856b-1dfbf0448719
81
+ 2d259f9d-bd4d-4c56-b3d2-71305f5f73d9
82
+ 6d5755b0-78fa-4bfe-a8e5-0b16fb941e8a
83
+
84
+ Development
85
+ -----------
86
+
87
+ After checking out the repo, run `bin/setup` to install developer dependencies.
88
+ Then, run `bundle exec rake test` to run the tests.
89
+ It's recommended that you run `bundle exec rake inspect` to run inspections.
90
+ You can also run `bin/console` for an interactive prompt that will allow you to experiment.
91
+ `bundle exec rake doc` will generate documentation.
92
+
93
+ To install this gem onto your local machine, run `bundle exec rake install`.
94
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`.
95
+ This will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
96
+
97
+ Contributing
98
+ ------------
99
+
100
+ Bug reports and pull requests are welcome on [GitHub](https://github.com/bluepixelmike/lazy-uuid).
@@ -0,0 +1,28 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'reek/rake/task'
3
+ require 'rubocop/rake_task'
4
+ require 'rubocop'
5
+ require 'rspec/core/rake_task'
6
+ require 'yard'
7
+
8
+ task :default => [:build, :test]
9
+ task :inspect => [:reek, :rubocop]
10
+
11
+ RSpec::Core::RakeTask.new(:spec)
12
+ task :test => :spec
13
+
14
+ Reek::Rake::Task.new do |task| # :reek
15
+ task.name = :reek
16
+ task.source_files = 'lib/**/*.rb'
17
+ task.fail_on_error = false
18
+ end
19
+
20
+ RuboCop::RakeTask.new(:rubocop) do |task|
21
+ task.patterns = ['lib/**/*.rb']
22
+ task.fail_on_error = false
23
+ end
24
+
25
+ YARD::Rake::YardocTask.new do |task| # :yard
26
+ task.files = %w(lib/**/*.rb - *.md)
27
+ end
28
+ task :doc => :yard
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'lazy-uuid'
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
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'lazy-uuid'
5
+
6
+ count = ARGV.any? ? ARGV.first.to_i : 1
7
+ count.times { puts Uuid.generate.to_s }
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install --path=vendor/bundle
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lazy-uuid/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'lazy-uuid'
8
+ spec.version = LazyUuid::VERSION
9
+ spec.authors = ['Michael Miller']
10
+ spec.email = ['bluepixelmike@gmail.com']
11
+
12
+ spec.summary = %q{Small gem for creating and using UUIDs (universally unique identifier).}
13
+ spec.description = %q{Generates RFC 4122 compliant UUIDs.
14
+ These UUIDs can be treated as their raw byte representation or as human-readable strings.}
15
+ spec.homepage = 'https://github.com/bluepixelmike/lazy-uuid'
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.bindir = 'bin'
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.executables = spec.files.grep(%r{^bin/.*uuid}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_development_dependency 'bundler'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rspec', '~> 3.0'
27
+ spec.add_development_dependency 'factory_girl', '~> 4.0'
28
+ spec.add_development_dependency 'reek', '~> 3.0'
29
+ spec.add_development_dependency 'rubocop', '~> 0.38.0'
30
+ spec.add_development_dependency 'codeclimate-test-reporter' unless RUBY_PLATFORM == 'java'
31
+ spec.add_development_dependency 'yard'
32
+ end
@@ -0,0 +1,6 @@
1
+ # Simple UUID functionality.
2
+ module LazyUuid
3
+ end
4
+
5
+ require_relative 'lazy-uuid/version'
6
+ require_relative 'lazy-uuid/uuid'
@@ -0,0 +1,251 @@
1
+ require 'securerandom' # For generating new UUIDs.
2
+
3
+ # Universally unique identifier.
4
+ # Items with distinct different UUIDs should be different entities.
5
+ # Items with the same UUID should be the same entity.
6
+ # @note The naming scheme used for this class is as follows:
7
+ # * +uuid+ - An instance of this class.
8
+ # * +value+ - Packed string, not human readable. 16 bytes in length.
9
+ # * +uuid_str+ - Formatted string, human readable. Formatted as xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12).
10
+ class Uuid
11
+ include Comparable
12
+
13
+ # Raw, packed byte string containing the UUID's value.
14
+ # @return [String] Packed string, 16 bytes in length.
15
+ attr_reader :value
16
+
17
+ # Creates a UUID or one from an existing value.
18
+ # @param value [String] Packed bytes with the UUID's value.
19
+ # The value must be a string of 16 bytes (128 bits).
20
+ # @see generate
21
+ def initialize(value)
22
+ value_str = value.to_s
23
+ fail ArgumentError, 'Packed UUID value must be 16 bytes.' unless value_str.length == 16
24
+
25
+ # Get a copy to prevent external processes modifying the value.
26
+ @value = value_str.dup
27
+
28
+ # Prevent modification.
29
+ @value.freeze
30
+ end
31
+
32
+ class << self
33
+
34
+ # Generates a new (and random) UUID
35
+ # @return [UUID] Newly generated UUID.
36
+ def generate
37
+ # A built-in method from Ruby to generate a valid UUID is SecureRandom.uuid.
38
+ # However, it returns it as a formatted string.
39
+ # The formatted string has to be converted to a packed string before it can be used.
40
+ uuid_str = SecureRandom.uuid
41
+ value = pack_uuid_str(uuid_str)
42
+ Uuid.new(value)
43
+ end
44
+
45
+ # Determines whether a string contains a valid formatted UUID.
46
+ # @param uuid_str [String] String to check.
47
+ # @return [true] The string contains a valid formatted UUID.
48
+ # @return [false] The string does not contain a valid UUID.
49
+ def valid_str?(uuid_str)
50
+ if uuid_str.is_a? String
51
+ # Check the formatting.
52
+ # Note that the validity of the UUID isn't checked.
53
+ !!/^\A[0-9a-f]{8}(-?)[0-9a-f]{4}\1[0-9a-f]{4}\1[0-9a-f]{4}\1[0-9a-f]{12}\z$/i.match(uuid_str)
54
+ else
55
+ # A string wasn't passed in.
56
+ fail ArgumentError
57
+ end
58
+ end
59
+
60
+ # Converts a UUID formatted as a string into its packed byte representation.
61
+ # @param uuid_str [String] UUID in string form.
62
+ # @return [String] Packed string, 16 bytes in length.
63
+ # @!visibility private
64
+ def pack_uuid_str(uuid_str)
65
+ # 1) Strip hyphens.
66
+ # 2) Collect 2 characters.
67
+ # 3) Convert 2 characters from hex to numeric.
68
+ # 4) Pack the numeric values into bytes in a string.
69
+ uuid_str.delete('-').scan(/../).map(&:hex).pack('C16')
70
+ end
71
+
72
+ # Creates a UUID object from its string representation.
73
+ # @param uuid_str [String] UUID in string form.
74
+ # The string must be in the form: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)
75
+ # @note Casing does not matter for a-f.
76
+ # @return [Uuid] Parsed UUID from the string.
77
+ # @return [nil] The string doesn't have a properly formatted UUID.
78
+ # @note Dashes may be omitted from the string.
79
+ # This allows strings of 32 hexadecimal characters.
80
+ def parse(uuid_str)
81
+ if valid_str?(uuid_str)
82
+ # Properly formatted UUID string.
83
+ # Pack the UUID into a byte string and return a new instance.
84
+ value = pack_uuid_str(uuid_str)
85
+ Uuid.new(value)
86
+ else
87
+ # Not properly formatted.
88
+ nil
89
+ end
90
+ end
91
+ end
92
+
93
+ # Checks for hash equality of two UUIDs.
94
+ # @param other [Uuid] Other UUID to compare against.
95
+ # @return [true] The UUIDs are equal.
96
+ # @return [false] The UUIDs are different.
97
+ # @note This method compares only {Uuid} instances.
98
+ # +false+ will be returned if +other+ is not a {Uuid}.
99
+ def eql?(other)
100
+ other.is_a?(Uuid) && eq_packed(other.value)
101
+ end
102
+
103
+ # Checks if two UUIDs have the same value.
104
+ # @param other [Uuid, String] Other object to check against.
105
+ # @return [true] The UUIDs are equal.
106
+ # @return [false] The UUIDs are different.
107
+ def ==(other)
108
+ case other
109
+ when Uuid
110
+ # Compare two UUID instances.
111
+ eq_packed(other.value)
112
+
113
+ when String
114
+ if other.length == 16
115
+ # Compare against a packed string
116
+ eq_packed(other)
117
+
118
+ else
119
+ # Compare against a formatted string
120
+ eq_formatted(other)
121
+ end
122
+
123
+ else
124
+ # Everything else can't be equated.
125
+ false
126
+ end
127
+ end
128
+
129
+ # Compares two UUIDs to each other to determine which is lower.
130
+ # @param other [Uuid, String] Other object to compare against.
131
+ # @return [-1] The left UUID is smaller.
132
+ # @return [0] The UUIDs are the same.
133
+ # @return [1] The right UUID is smaller.
134
+ # @return [nil] The right side isn't a UUID.
135
+ def <=>(other)
136
+ case other
137
+ when Uuid
138
+ # Compare two UUID instances.
139
+ cmp_packed(other.value)
140
+
141
+ when String
142
+ if other.length == 16
143
+ # Compare against a packed string
144
+ cmp_packed(other)
145
+
146
+ else
147
+ # Compare against a formatted string
148
+ cmp_formatted(other)
149
+ end
150
+
151
+ else
152
+ # Everything else can't be compared.
153
+ nil
154
+ end
155
+ end
156
+
157
+ # Produces a hash value of the UUID.
158
+ # @return [Fixnum]
159
+ def hash
160
+ @value.hash
161
+ end
162
+
163
+ # Produces the string representation of the UUID.
164
+ # @param dashes [Boolean] +true+ to put dashes in the output (standard format),
165
+ # +false+ to not put dashes.
166
+ # @return [String] UUID in string form: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)
167
+ def to_s(dashes = true)
168
+ bytes = @value.bytes
169
+ if dashes
170
+ sprintf('%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x', *bytes)
171
+ else
172
+ sprintf('%02x' * 16, *bytes)
173
+ end
174
+ end
175
+
176
+ # Empty UUID.
177
+ # @return [Uuid] UUID with the value 00000000-0000-0000-0000-000000000000.
178
+ DEFAULT = Uuid.new("\x0" * 16).freeze
179
+
180
+ private
181
+
182
+ # Checks for equality with a packed string.
183
+ # @param value [String] Packed string.
184
+ # @return [true] The packed string contains an identical value.
185
+ # @return [false] The packed string contains a different value.
186
+ def eq_packed(value)
187
+ # Compare the bytes in each string.
188
+ # Byte comparison *must* be used, since == uses character comparison.
189
+ # Bytes != characters when encoding is involved.
190
+ @value.each_byte.with_index do |byte_a, index|
191
+ byte_b = value.getbyte(index)
192
+ break false if byte_a != byte_b # Abort enumeration with false value.
193
+ break true if index == 15 # Return true on last iteration when bytes are the same.
194
+ end
195
+ end
196
+
197
+ # Checks for equality with a UUID formatted as a string.
198
+ # @param uuid_str [String] UUID formatted as a string.
199
+ # @return [true] The formatted string contains an identical value.
200
+ # @return [false] The formatted string contains a different value.
201
+ def eq_formatted(uuid_str)
202
+ klass = self.class
203
+ if klass.valid_str?(uuid_str)
204
+ # Format is valid.
205
+ # Compare it to packed string.
206
+ value = klass.pack_uuid_str(uuid_str)
207
+ eq_packed(value)
208
+ else
209
+ # Invalid format can't be equal.
210
+ false
211
+ end
212
+ end
213
+
214
+ # Compares the ordering with a packed string.
215
+ # @param value [String] Packed string.
216
+ # @return [-1] The left UUID is smaller.
217
+ # @return [0] The UUIDs are the same.
218
+ # @return [1] The right UUID is smaller.
219
+ # @return [nil] The right side isn't a UUID.
220
+ def cmp_packed(value)
221
+ # Compare the bytes in each string.
222
+ # Byte comparison *must* be used, since <=> uses character comparison.
223
+ # Bytes != characters when encoding is involved.
224
+ @value.bytes.each_with_index do |byte_a, index|
225
+ byte_b = value.getbyte(index)
226
+ result = byte_a <=> byte_b
227
+ break result if result != 0 # Abort enumeration with result.
228
+ break 0 if index == 15 # Return 0 on last byte if they're the same.
229
+ end
230
+ end
231
+
232
+ # Checks for equality with a UUID formatted as a string.
233
+ # @param uuid_str [String] UUID formatted as a string.
234
+ # @return [-1] The left UUID is smaller.
235
+ # @return [0] The UUIDs are the same.
236
+ # @return [1] The right UUID is smaller.
237
+ # @return [nil] The right side isn't a UUID.
238
+ def cmp_formatted(uuid_str)
239
+ klass = self.class
240
+ if klass.valid_str?(uuid_str)
241
+ # Format is valid.
242
+ # Compare it to packed string.
243
+ value = klass.pack_uuid_str(uuid_str)
244
+ cmp_packed(value)
245
+ else
246
+ # Invalid format can't be equal.
247
+ nil
248
+ end
249
+ end
250
+
251
+ end