activeid 0.6.1
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 +7 -0
- data/.editorconfig +21 -0
- data/.github/workflows/macos.yml +45 -0
- data/.github/workflows/ubuntu.yml +47 -0
- data/.github/workflows/windows.yml +40 -0
- data/.gitignore +195 -0
- data/.hound.yml +3 -0
- data/.rubocop.yml +18 -0
- data/Gemfile +7 -0
- data/LICENSE.md +19 -0
- data/README.adoc +411 -0
- data/Rakefile +27 -0
- data/activeid.gemspec +42 -0
- data/examples/name_based_uuids.rb +92 -0
- data/examples/registering_active_record_type.rb +74 -0
- data/examples/storing_uuids_as_binaries.rb +88 -0
- data/examples/storing_uuids_as_strings.rb +81 -0
- data/examples/storing_uuids_natively.rb +93 -0
- data/examples/time_based_uuids.rb +58 -0
- data/examples/using_migrations.rb +50 -0
- data/gemfiles/Rails-5_0.gemfile +8 -0
- data/gemfiles/Rails-5_1.gemfile +8 -0
- data/gemfiles/Rails-5_2.gemfile +8 -0
- data/gemfiles/Rails-head.gemfile +8 -0
- data/lib/active_id.rb +12 -0
- data/lib/active_id/all.rb +2 -0
- data/lib/active_id/connection_patches.rb +65 -0
- data/lib/active_id/model.rb +55 -0
- data/lib/active_id/railtie.rb +12 -0
- data/lib/active_id/type.rb +100 -0
- data/lib/active_id/utils.rb +77 -0
- data/lib/active_id/version.rb +3 -0
- data/spec/integration/examples_for_uuid_models.rb +92 -0
- data/spec/integration/examples_for_uuid_models_having_namespaces.rb +12 -0
- data/spec/integration/examples_for_uuid_models_having_natural_keys.rb +11 -0
- data/spec/integration/migrations_spec.rb +92 -0
- data/spec/integration/model_without_uuids_spec.rb +44 -0
- data/spec/integration/no_patches_spec.rb +26 -0
- data/spec/integration/storing_uuids_as_binaries_spec.rb +34 -0
- data/spec/integration/storing_uuids_as_strings_spec.rb +22 -0
- data/spec/spec_helper.rb +64 -0
- data/spec/support/0_logger.rb +2 -0
- data/spec/support/1_db_connection.rb +3 -0
- data/spec/support/2_db_cleaner.rb +14 -0
- data/spec/support/database.yml +12 -0
- data/spec/support/fabricators.rb +15 -0
- data/spec/support/models.rb +41 -0
- data/spec/support/schema.rb +38 -0
- data/spec/unit/attribute_type_spec.rb +70 -0
- data/spec/unit/utils_spec.rb +97 -0
- metadata +313 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: dc713ea54d39df97a832528211f697b13e45e3c744d4780fda842d39b213d48a
|
4
|
+
data.tar.gz: 3cf3366bbbfc5adb0cc5d16ce728145e91792dfbf62430be5a488764248772e9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c85cce975a703dd95546d8c7efb42747b4e8bdd7fe762e2e0f0d0cc80334293fd1d823210f00007073334e0acec39b8e45d5eafad5293caa377575cc784d69f0
|
7
|
+
data.tar.gz: 2df3ba3814f9c64d75222afccbe230e4eb754caaefb0d27ae0c607a26ca7339657f3be699df59524ca1d1fad838072bae7602c8fe8c262618d2e6eb019eab50f
|
data/.editorconfig
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# EditorConfig is awesome: http://EditorConfig.org
|
2
|
+
|
3
|
+
# top-most EditorConfig file
|
4
|
+
root = true
|
5
|
+
|
6
|
+
# Unix-style newlines with a newline ending every file
|
7
|
+
[*]
|
8
|
+
charset = utf-8
|
9
|
+
end_of_line = lf
|
10
|
+
|
11
|
+
[{*.{rb,ru,yml,rake,feature,gemfile},Rakefile,Gemfile}]
|
12
|
+
indent_style = space
|
13
|
+
indent_size = 2
|
14
|
+
insert_final_newline = true
|
15
|
+
trim_trailing_whitespace = true
|
16
|
+
|
17
|
+
[*.sh]
|
18
|
+
indent_style = tab
|
19
|
+
indent_size = 4
|
20
|
+
insert_final_newline = true
|
21
|
+
trim_trailing_whitespace = true
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Auto-generated by Cimas: Do not edit it manually!
|
2
|
+
# See https://github.com/metanorma/cimas
|
3
|
+
name: macos
|
4
|
+
|
5
|
+
on:
|
6
|
+
push:
|
7
|
+
branches: [ master ]
|
8
|
+
pull_request:
|
9
|
+
paths-ignore:
|
10
|
+
- .github/workflows/ubuntu.yml
|
11
|
+
- .github/workflows/windows.yml
|
12
|
+
jobs:
|
13
|
+
test-macos:
|
14
|
+
name: Test on Ruby ${{ matrix.ruby }} macOS
|
15
|
+
runs-on: macos-latest
|
16
|
+
continue-on-error: ${{ matrix.experimental }}
|
17
|
+
strategy:
|
18
|
+
fail-fast: false
|
19
|
+
matrix:
|
20
|
+
ruby: [ '2.6', '2.5', '2.4' ]
|
21
|
+
experimental: [false]
|
22
|
+
include:
|
23
|
+
- ruby: '2.7'
|
24
|
+
experimental: true
|
25
|
+
steps:
|
26
|
+
- uses: actions/checkout@master
|
27
|
+
- name: Use Ruby
|
28
|
+
uses: actions/setup-ruby@v1
|
29
|
+
with:
|
30
|
+
ruby-version: ${{ matrix.ruby }}
|
31
|
+
architecture: 'x64'
|
32
|
+
- name: Install mysql and pg
|
33
|
+
run: |
|
34
|
+
brew install mysql
|
35
|
+
mysql -e 'create database activeuuid_test;'
|
36
|
+
# psql -c 'create database activeuuid_test;' -U postgres
|
37
|
+
|
38
|
+
- name: Update gems
|
39
|
+
run: |
|
40
|
+
sudo gem install bundler --force
|
41
|
+
gem install mysql2 -- --with-ldflags=-L/usr/local/opt/openssl/lib --with-cppflags=-I/usr/local/opt/openssl/include
|
42
|
+
bundle install --jobs 4 --retry 3
|
43
|
+
- name: Run specs
|
44
|
+
run: |
|
45
|
+
bundle exec rake
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Auto-generated by Cimas: Do not edit it manually!
|
2
|
+
# See https://github.com/metanorma/cimas
|
3
|
+
name: ubuntu
|
4
|
+
|
5
|
+
on:
|
6
|
+
push:
|
7
|
+
branches: [ master ]
|
8
|
+
tags:
|
9
|
+
- '*'
|
10
|
+
pull_request:
|
11
|
+
paths-ignore:
|
12
|
+
- .github/workflows/macos.yml
|
13
|
+
- .github/workflows/windows.yml
|
14
|
+
jobs:
|
15
|
+
test-linux:
|
16
|
+
name: Test on Ruby ${{ matrix.ruby }} Ubuntu
|
17
|
+
runs-on: ubuntu-latest
|
18
|
+
continue-on-error: ${{ matrix.experimental }}
|
19
|
+
strategy:
|
20
|
+
fail-fast: false
|
21
|
+
matrix:
|
22
|
+
ruby: [ '2.6', '2.5', '2.4' ]
|
23
|
+
experimental: [false]
|
24
|
+
include:
|
25
|
+
- ruby: '2.7'
|
26
|
+
experimental: true
|
27
|
+
steps:
|
28
|
+
- uses: actions/checkout@master
|
29
|
+
- name: Use Ruby
|
30
|
+
uses: actions/setup-ruby@v1
|
31
|
+
with:
|
32
|
+
ruby-version: ${{ matrix.ruby }}
|
33
|
+
architecture: 'x64'
|
34
|
+
- name: Install mysql and pg
|
35
|
+
run: |
|
36
|
+
# sudo apt-get update
|
37
|
+
# sudo apt install -y mariadb-server
|
38
|
+
sudo apt-get install -y postgresql postgresql-contrib
|
39
|
+
mysql -e 'create database activeuuid_test;'
|
40
|
+
psql -c 'create database activeuuid_test;' -U postgres
|
41
|
+
- name: Update gems
|
42
|
+
run: |
|
43
|
+
gem install bundler
|
44
|
+
bundle install --jobs 4 --retry 3
|
45
|
+
- name: Run specs
|
46
|
+
run: |
|
47
|
+
bundle exec rake
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Auto-generated by Cimas: Do not edit it manually!
|
2
|
+
# See https://github.com/metanorma/cimas
|
3
|
+
name: windows
|
4
|
+
|
5
|
+
on:
|
6
|
+
push:
|
7
|
+
branches: [ master ]
|
8
|
+
pull_request:
|
9
|
+
paths-ignore:
|
10
|
+
- .github/workflows/macos.yml
|
11
|
+
- .github/workflows/ubuntu.yml
|
12
|
+
jobs:
|
13
|
+
test-windows:
|
14
|
+
name: Test on Ruby ${{ matrix.ruby }} Windows
|
15
|
+
runs-on: windows-latest
|
16
|
+
continue-on-error: ${{ matrix.experimental }}
|
17
|
+
strategy:
|
18
|
+
fail-fast: false
|
19
|
+
matrix:
|
20
|
+
ruby: [ '2.6', '2.5', '2.4' ]
|
21
|
+
experimental: [false]
|
22
|
+
include:
|
23
|
+
- ruby: '2.7'
|
24
|
+
experimental: true
|
25
|
+
steps:
|
26
|
+
- uses: actions/checkout@master
|
27
|
+
- name: Use Ruby
|
28
|
+
uses: actions/setup-ruby@v1
|
29
|
+
with:
|
30
|
+
ruby-version: ${{ matrix.ruby }}
|
31
|
+
architecture: 'x64'
|
32
|
+
- name: Update gems
|
33
|
+
shell: pwsh
|
34
|
+
run: |
|
35
|
+
gem install bundler
|
36
|
+
bundle config --local path vendor/bundle
|
37
|
+
bundle install --jobs 4 --retry 3
|
38
|
+
- name: Run specs
|
39
|
+
run: |
|
40
|
+
bundle exec rake
|
data/.gitignore
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
# Rubocop caches
|
2
|
+
/.rubocop-http--*
|
3
|
+
/.rubocop-https--*
|
4
|
+
|
5
|
+
# Combustion gem
|
6
|
+
*.log
|
7
|
+
*.sqlite
|
8
|
+
|
9
|
+
# rspec failure tracking
|
10
|
+
/.rspec_status
|
11
|
+
|
12
|
+
*.gem
|
13
|
+
*.orig
|
14
|
+
*.rbc
|
15
|
+
.rspec
|
16
|
+
/.config
|
17
|
+
/InstalledFiles
|
18
|
+
/coverage/
|
19
|
+
/db/*.sqlite3
|
20
|
+
/db/*.sqlite3-journal
|
21
|
+
/log
|
22
|
+
/pkg/
|
23
|
+
/public/system
|
24
|
+
/spec/examples.txt
|
25
|
+
/spec/reports/
|
26
|
+
/spec/tmp
|
27
|
+
/test/tmp/
|
28
|
+
/test/version_tmp/
|
29
|
+
/tmp
|
30
|
+
capybara-*.html
|
31
|
+
pickle-email-*.html
|
32
|
+
rerun.txt
|
33
|
+
|
34
|
+
|
35
|
+
## Specific to RubyMotion:
|
36
|
+
.dat*
|
37
|
+
.repl_history
|
38
|
+
build/
|
39
|
+
*.bridgesupport
|
40
|
+
build-iPhoneOS/
|
41
|
+
build-iPhoneSimulator/
|
42
|
+
|
43
|
+
## Specific to RubyMotion (use of CocoaPods):
|
44
|
+
#
|
45
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
46
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
47
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
48
|
+
#
|
49
|
+
# vendor/Pods/
|
50
|
+
|
51
|
+
## Documentation cache and generated files:
|
52
|
+
/.yardoc/
|
53
|
+
/_yardoc/
|
54
|
+
/doc/
|
55
|
+
/rdoc/
|
56
|
+
|
57
|
+
## Environment normalization:
|
58
|
+
/.bundle/
|
59
|
+
/vendor/bundle
|
60
|
+
/lib/bundler/man/
|
61
|
+
|
62
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
63
|
+
.rvmrc
|
64
|
+
|
65
|
+
# TODO Comment out this rule if you are OK with secrets being uploaded to the
|
66
|
+
# repo
|
67
|
+
config/initializers/secret_token.rb
|
68
|
+
|
69
|
+
# Only include if you have production secrets in this file, which is no longer a Rails default
|
70
|
+
# config/secrets.yml
|
71
|
+
|
72
|
+
# dotenv
|
73
|
+
# Used by dotenv library to load environment variables.
|
74
|
+
# TODO Comment out this rule if environment variables can be committed
|
75
|
+
# .env
|
76
|
+
|
77
|
+
# for a library or gem, you might want to ignore these files since the code is
|
78
|
+
# intended to run in multiple environments; otherwise, check them in:
|
79
|
+
Gemfile.lock
|
80
|
+
.ruby-version
|
81
|
+
.ruby-gemset
|
82
|
+
|
83
|
+
# if using bower-rails ignore default bower_components path bower.json files
|
84
|
+
/vendor/assets/bower_components
|
85
|
+
*.bowerrc
|
86
|
+
bower.json
|
87
|
+
|
88
|
+
# Ignore pow environment settings
|
89
|
+
.powenv
|
90
|
+
|
91
|
+
# Ignore Byebug command history file.
|
92
|
+
.byebug_history
|
93
|
+
|
94
|
+
|
95
|
+
# Created by https://www.gitignore.io/api/vim
|
96
|
+
|
97
|
+
### Vim ###
|
98
|
+
# swap
|
99
|
+
[._]*.s[a-v][a-z]
|
100
|
+
[._]*.sw[a-p]
|
101
|
+
[._]s[a-v][a-z]
|
102
|
+
[._]sw[a-p]
|
103
|
+
# session
|
104
|
+
Session.vim
|
105
|
+
# temporary
|
106
|
+
.netrwhist
|
107
|
+
*~
|
108
|
+
# auto-generated tag files
|
109
|
+
tags
|
110
|
+
|
111
|
+
# End of https://www.gitignore.io/api/vim
|
112
|
+
|
113
|
+
|
114
|
+
# Created by https://www.gitignore.io/api/emacs
|
115
|
+
|
116
|
+
### Emacs ###
|
117
|
+
# -*- mode: gitignore; -*-
|
118
|
+
*~
|
119
|
+
\#*\#
|
120
|
+
/.emacs.desktop
|
121
|
+
/.emacs.desktop.lock
|
122
|
+
*.elc
|
123
|
+
auto-save-list
|
124
|
+
tramp
|
125
|
+
.\#*
|
126
|
+
|
127
|
+
# Org-mode
|
128
|
+
.org-id-locations
|
129
|
+
*_archive
|
130
|
+
|
131
|
+
# flymake-mode
|
132
|
+
*_flymake.*
|
133
|
+
|
134
|
+
# eshell files
|
135
|
+
/eshell/history
|
136
|
+
/eshell/lastdir
|
137
|
+
|
138
|
+
# elpa packages
|
139
|
+
/elpa/
|
140
|
+
|
141
|
+
# reftex files
|
142
|
+
*.rel
|
143
|
+
|
144
|
+
# AUCTeX auto folder
|
145
|
+
/auto/
|
146
|
+
|
147
|
+
# cask packages
|
148
|
+
.cask/
|
149
|
+
dist/
|
150
|
+
|
151
|
+
# Flycheck
|
152
|
+
flycheck_*.el
|
153
|
+
|
154
|
+
# server auth directory
|
155
|
+
/server/
|
156
|
+
|
157
|
+
# projectiles files
|
158
|
+
.projectile
|
159
|
+
|
160
|
+
# directory configuration
|
161
|
+
.dir-locals.el
|
162
|
+
|
163
|
+
# End of https://www.gitignore.io/api/emacs
|
164
|
+
|
165
|
+
# Created by https://www.gitignore.io/api/macos
|
166
|
+
|
167
|
+
### macOS ###
|
168
|
+
*.DS_Store
|
169
|
+
.AppleDouble
|
170
|
+
.LSOverride
|
171
|
+
|
172
|
+
# Icon must end with two \r
|
173
|
+
Icon
|
174
|
+
|
175
|
+
# Thumbnails
|
176
|
+
._*
|
177
|
+
|
178
|
+
# Files that might appear in the root of a volume
|
179
|
+
.DocumentRevisions-V100
|
180
|
+
.fseventsd
|
181
|
+
.Spotlight-V100
|
182
|
+
.TemporaryItems
|
183
|
+
.Trashes
|
184
|
+
.VolumeIcon.icns
|
185
|
+
.com.apple.timemachine.donotpresent
|
186
|
+
|
187
|
+
# Directories potentially created on remote AFP share
|
188
|
+
.AppleDB
|
189
|
+
.AppleDesktop
|
190
|
+
Network Trash Folder
|
191
|
+
Temporary Items
|
192
|
+
.apdisk
|
193
|
+
|
194
|
+
|
195
|
+
# End of https://www.gitignore.io/api/macos
|
data/.hound.yml
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# All project-specific additions and overrides should be specified in this
|
2
|
+
# file.
|
3
|
+
|
4
|
+
inherit_from:
|
5
|
+
- https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
|
6
|
+
|
7
|
+
AllCops:
|
8
|
+
DisplayCopNames: false
|
9
|
+
StyleGuideCopsOnly: false
|
10
|
+
TargetRubyVersion: 2.4
|
11
|
+
|
12
|
+
Rails:
|
13
|
+
Enabled: true
|
14
|
+
|
15
|
+
# It's not a typical Rails application. There is no value in introducing
|
16
|
+
# the ApplicationRecord class.
|
17
|
+
Rails/ApplicationRecord:
|
18
|
+
Enabled: false
|
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.adoc
ADDED
@@ -0,0 +1,411 @@
|
|
1
|
+
= ActiveID: binary UUIDs for ActiveRecord
|
2
|
+
|
3
|
+
// Document setup
|
4
|
+
:toc:
|
5
|
+
:toc-placement!:
|
6
|
+
:source-language: ruby
|
7
|
+
:source-highlighter: pygments
|
8
|
+
:pygments-style: native
|
9
|
+
:pygments-linenums-mode: inline
|
10
|
+
|
11
|
+
// Admonition captions in GitHub (here Emoji)
|
12
|
+
// See: https://github.com/ikatyang/emoji-cheat-sheet/blob/master/README.md
|
13
|
+
ifdef::env-github[]
|
14
|
+
:tip-caption: :bulb:
|
15
|
+
:note-caption: :information_source:
|
16
|
+
:important-caption: :heavy_exclamation_mark:
|
17
|
+
:caution-caption: :fire:
|
18
|
+
:warning-caption: :warning:
|
19
|
+
endif::[]
|
20
|
+
|
21
|
+
// Links
|
22
|
+
:dce_uuids: https://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01
|
23
|
+
:gem_original: https://rubygems.org/gems/activeid
|
24
|
+
:gem_uuidtools: https://github.com/sporkmonger/uuidtools
|
25
|
+
:maria_jira_uuid_func: https://jira.mariadb.org/browse/MDEV-15854
|
26
|
+
:mit_lic: https://opensource.org/licenses/MIT
|
27
|
+
:mysql_uuid: https://mysqlserverteam.com/mysql-8-0-uuid-support/
|
28
|
+
:examples: https://github.com/riboseinc/activeid/tree/master/examples
|
29
|
+
:percona_blog: https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
|
30
|
+
:rails_api_type_register: https://api.rubyonrails.org/classes/ActiveRecord/Type.html#method-c-register
|
31
|
+
:rfc_uuids: https://tools.ietf.org/html/rfc4122
|
32
|
+
:ribose: https://www.ribose.com
|
33
|
+
:xkcd_comic: https://xkcd.com/927/
|
34
|
+
|
35
|
+
// Badges
|
36
|
+
ifdef::env-github[]
|
37
|
+
image:https://img.shields.io/travis/riboseinc/activeid/master.svg[
|
38
|
+
Build Status, link="https://travis-ci.org/riboseinc/activeid/branches"]
|
39
|
+
image:https://img.shields.io/codecov/c/github/riboseinc/activeid/master.svg[
|
40
|
+
Test Coverage, link="https://codecov.io/gh/riboseinc/activeid"]
|
41
|
+
image:https://img.shields.io/codeclimate/maintainability/riboseinc/activeid.svg[
|
42
|
+
"Code Climate", link="https://codeclimate.com/github/riboseinc/activeid"]
|
43
|
+
endif::[]
|
44
|
+
|
45
|
+
A modern, performant and database-agnostic solution for storing UUIDs
|
46
|
+
in ActiveRecord 5.0+, without any obligatory monkey patches.
|
47
|
+
|
48
|
+
toc::[]
|
49
|
+
|
50
|
+
== Rationale
|
51
|
+
|
52
|
+
If you search for "`uuid`" in RubyGems, you'll get
|
53
|
+
https://rubygems.org/search?utf8=%E2%9C%93&query=uuid[142 results] (as of
|
54
|
+
January 2019)… Most are outdated, all are far from our needs. Yes,
|
55
|
+
{xkcd_comic}[we think we need another one].
|
56
|
+
|
57
|
+
NOTE: ActiveID has evolved from a popular, but no longer maintained
|
58
|
+
{gem_original}[ActiveID] gem, and its forks.
|
59
|
+
From 2018 the gem was entirely rewritten to support newer Rails releases, and was finally detached as a fork in 2020 to prevent confusion from users. We thank Nate for bringing ActiveID to life!
|
60
|
+
|
61
|
+
=== Storing UUIDs as binaries in MySQL and SQLite3
|
62
|
+
|
63
|
+
UUIDs are 16 bytes long, however their human-readable string representation
|
64
|
+
takes 36 characters. As a consequence, storing UUIDs in a human-readable
|
65
|
+
format is space inefficient. What is worse, whereas table row size is seldom
|
66
|
+
a big concern, size of table indices is significant -- the bigger part of given
|
67
|
+
index fits in RAM, the faster it works. And UUID columns are commonly indexed…
|
68
|
+
|
69
|
+
[NOTE]
|
70
|
+
================================================================================
|
71
|
+
Another performance boost for UUIDs version 1 can be achieved by bits
|
72
|
+
rearrangement. This is not implemented yet, see
|
73
|
+
https://github.com/riboseinc/activeid/issues/43[issue #43].
|
74
|
+
================================================================================
|
75
|
+
|
76
|
+
This gem brings an easy-to-use ability to efficiently store UUIDs in databases
|
77
|
+
which do not provide a dedicated UUID data type (i.e. MySQL, MariaDB, SQLite3,
|
78
|
+
etc.).
|
79
|
+
|
80
|
+
=== Database-agnosticism
|
81
|
+
|
82
|
+
This gem provides a uniform API for storing UUIDs in database, be it MariaDB,
|
83
|
+
MySQL, SQLite3 (in binary or string format), or PostgreSQL (native data type).
|
84
|
+
This is especially important when using it as a dependency of another gem.
|
85
|
+
|
86
|
+
=== Monkey patching is optional
|
87
|
+
|
88
|
+
No core feature relies on Rails monkey patching. Monkey patches can interfere
|
89
|
+
with other gems, and lead to issues. Nevertheless, some convenient features
|
90
|
+
(currently, migration methods only) are provided via monkey patching. Enabling
|
91
|
+
them is entirely optional, and their absence can be workarounded easily.
|
92
|
+
|
93
|
+
=== Strings are not perfect for UUIDs
|
94
|
+
|
95
|
+
Although UUIDs are commonly represented as strings, it is beneficial to
|
96
|
+
introduce a dedicated class for following reasons:
|
97
|
+
|
98
|
+
- not every sequence of 16 bytes (or 32 hexadecimal digits) makes a valid UUID
|
99
|
+
- UUIDs are not opaque, they have their inner structure which can be accessed
|
100
|
+
(reading timestamp from time-based UUIDs is especially useful)
|
101
|
+
- using string equality operator for UUID comparison may give wrong results
|
102
|
+
(up-cased or lowercased strings, with dashes or without)
|
103
|
+
|
104
|
+
It is somewhat similar case to URIs, which also can be represented as plain
|
105
|
+
strings, but having a dedicated `URI` class is quite convenient.
|
106
|
+
|
107
|
+
ActiveID uses `UUID` class from {gem_uuidtools}[UUIDTools] gem to represent
|
108
|
+
UUIDs.
|
109
|
+
|
110
|
+
== Usage
|
111
|
+
|
112
|
+
[TIP]
|
113
|
+
================================================================================
|
114
|
+
You may want to explore {examples}[examples] directory, in which typical
|
115
|
+
use cases are covered in bit more detail.
|
116
|
+
================================================================================
|
117
|
+
|
118
|
+
=== Installation
|
119
|
+
|
120
|
+
Depending on you want to apply monkey patches or not, require either
|
121
|
+
`activeid/all` (with monkey patches) or `activeid` (without them).
|
122
|
+
|
123
|
+
For example, if you are using `Gemfile`:
|
124
|
+
|
125
|
+
[source]
|
126
|
+
--------------------------------------------------------------------------------
|
127
|
+
gem "activeid" # without monkey patches
|
128
|
+
# or
|
129
|
+
gem "activeid", require: "activeid/all" # with monkey patches
|
130
|
+
--------------------------------------------------------------------------------
|
131
|
+
|
132
|
+
Depending on your needs, you can also pick monkey patches selectively -- just
|
133
|
+
take a look at the contents of `lib/activeid/all.rb`. However, currently
|
134
|
+
it is not very useful, as there is very little to choose from.
|
135
|
+
|
136
|
+
=== Adding UUIDs to models
|
137
|
+
|
138
|
+
ActiveID relies on ActiveRecord's attributes API. Two attribute types are
|
139
|
+
defined: `StringUUID` and `BinaryUUID`.
|
140
|
+
|
141
|
+
StringUUID serializes UUIDs as 36 characters long strings. It is compatible
|
142
|
+
with textual SQL types, e.g. `VARCHAR(36)`, and more importantly, with
|
143
|
+
PostgreSQL-specific `UUID` type.
|
144
|
+
|
145
|
+
BinaryUUID serializes UUIDs as 16 bytes long binaries, which can be stored
|
146
|
+
in binary columns, e.g. `BLOB(16)` in SQLite3 or `VARBINARY(16)` in MySQL.
|
147
|
+
However, it is not compatible with PostgreSQL at all due to syntax differences.
|
148
|
+
See "<<Choosing between string and binary serialization>>" section for a brief
|
149
|
+
explanation of pros and cons of both approaches.
|
150
|
+
|
151
|
+
Whichever attribute type you prefer to use, an `ActiveID::Model` module must
|
152
|
+
be included in model.
|
153
|
+
|
154
|
+
For example, following example model stores two UUID attributes: `id`,
|
155
|
+
and `thread_id` as binaries.
|
156
|
+
|
157
|
+
[source]
|
158
|
+
--------------------------------------------------------------------------------
|
159
|
+
class Work < ActiveRecord::Base
|
160
|
+
include ActiveID::Model
|
161
|
+
attribute :id, ActiveID::Type::BinaryUUID.new
|
162
|
+
attribute :author_id, ActiveID::Type::BinaryUUID.new
|
163
|
+
belongs_to :author
|
164
|
+
end
|
165
|
+
--------------------------------------------------------------------------------
|
166
|
+
|
167
|
+
=== Database migrations
|
168
|
+
|
169
|
+
A convenience `#uuid` method is added via monkey patching to Active Record's
|
170
|
+
`Table` and `TableDefinition` classes.
|
171
|
+
|
172
|
+
- In MySQL adapter, it stands for a `VARBINARY(16)` column.
|
173
|
+
- In SQLite3 adapter, it stands for a `BLOB(16)` column.
|
174
|
+
- In PostgreSQL adapter, it is shadowed by a stock Rails method
|
175
|
+
`::ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnMethods:uuid`, which
|
176
|
+
stands for a `UUID` column.
|
177
|
+
|
178
|
+
If you want to use UUID column in your primary key, pass `:id => false` option
|
179
|
+
to `create_table` method and `:primary_key => true` to column definition.
|
180
|
+
|
181
|
+
For example:
|
182
|
+
|
183
|
+
[source]
|
184
|
+
--------------------------------------------------------------------------------
|
185
|
+
class CreateWorks < ActiveRecord::Migration
|
186
|
+
def change
|
187
|
+
create_table :works, id: false, force: true do |t|
|
188
|
+
t.uuid :id, primary_key: true
|
189
|
+
t.uuid :author_id, index: true
|
190
|
+
t.string :title
|
191
|
+
t.timestamps
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
--------------------------------------------------------------------------------
|
196
|
+
|
197
|
+
Alternatively, if monkey patches are disabled, `#uuid` method can be substituted
|
198
|
+
with `#binary` in MySQL and SQLite3 adapters. Following snippet is equivalent
|
199
|
+
to the above one in these two adapters. Please note `:limit => 16`, which is
|
200
|
+
passed as an option.
|
201
|
+
|
202
|
+
[source]
|
203
|
+
--------------------------------------------------------------------------------
|
204
|
+
class CreateWorks < ActiveRecord::Migration
|
205
|
+
def change
|
206
|
+
create_table :works, id: false, force: true do |t|
|
207
|
+
t.binary :id, limit: 16, primary_key: true
|
208
|
+
t.binary :author_id, limit: 16, index: true
|
209
|
+
t.string :title
|
210
|
+
t.timestamps
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
--------------------------------------------------------------------------------
|
215
|
+
|
216
|
+
=== Registering UUID types in Active Record's type registry
|
217
|
+
|
218
|
+
For convenience, Active UUID types can be added to Active Record's type
|
219
|
+
registry. Then you can reference them in your models with a symbol.
|
220
|
+
See {rails_api_type_register}[Rails API docs] for detailed information.
|
221
|
+
|
222
|
+
For example, following will register `ActiveID::Type::BinaryUUID` at `:uuid`
|
223
|
+
symbol for all adapters except for PostgreSQL, in which this symbol is already
|
224
|
+
taken:
|
225
|
+
|
226
|
+
[source]
|
227
|
+
--------------------------------------------------------------------------------
|
228
|
+
ActiveRecord::Type.register(
|
229
|
+
:uuid,
|
230
|
+
ActiveID::Type::BinaryUUID,
|
231
|
+
)
|
232
|
+
--------------------------------------------------------------------------------
|
233
|
+
|
234
|
+
With above set, only symbol needs to be specified in attribute declaration,
|
235
|
+
as in following example:
|
236
|
+
|
237
|
+
[source]
|
238
|
+
--------------------------------------------------------------------------------
|
239
|
+
class Author < ActiveRecord::Base
|
240
|
+
include ActiveID::Model
|
241
|
+
attribute :id, :uuid
|
242
|
+
end
|
243
|
+
--------------------------------------------------------------------------------
|
244
|
+
|
245
|
+
It is also possible to override `:uuid` in PostgreSQL adapter:
|
246
|
+
|
247
|
+
[source]
|
248
|
+
--------------------------------------------------------------------------------
|
249
|
+
ActiveRecord::Type.register(
|
250
|
+
:uuid,
|
251
|
+
ActiveID::Type::StringUUID,
|
252
|
+
adapter: :postgresql,
|
253
|
+
override: true,
|
254
|
+
)
|
255
|
+
--------------------------------------------------------------------------------
|
256
|
+
|
257
|
+
[CAUTION]
|
258
|
+
================================================================================
|
259
|
+
Overriding standard attribute types may cause other gems to behave abnormally.
|
260
|
+
================================================================================
|
261
|
+
|
262
|
+
=== Using UUIDs as primary keys
|
263
|
+
|
264
|
+
When model's primary key is a UUID, Active UUID automatically generates its
|
265
|
+
value as a version 1, 4, or 5 UUID:
|
266
|
+
|
267
|
+
- Version 1 UUIDs store timestamp of their creation, and are monotonically
|
268
|
+
increasing in time. This is very advantageous in some use cases.
|
269
|
+
- Version 4 UUIDs are pseudo-randomly generated.
|
270
|
+
- Version 5 UUIDs are generated deterministically via SHA-1 hashing from values
|
271
|
+
of specified attributes, and UUID namespace. They are well-suited for natural
|
272
|
+
keys.
|
273
|
+
|
274
|
+
UUIDs of all versions can be explicitly assigned to attributes.
|
275
|
+
|
276
|
+
==== Random primary keys (version 4 UUIDs)
|
277
|
+
|
278
|
+
If model's primary key is a UUID, a version 4 UUID is generated by default.
|
279
|
+
For example:
|
280
|
+
|
281
|
+
[source]
|
282
|
+
--------------------------------------------------------------------------------
|
283
|
+
class Author < ActiveRecord::Base
|
284
|
+
include ActiveID::Model
|
285
|
+
attribute :id, ActiveID::Type::StringUUID.new
|
286
|
+
end
|
287
|
+
--------------------------------------------------------------------------------
|
288
|
+
|
289
|
+
=== Time-based primary keys (version 1 UUIDs)
|
290
|
+
|
291
|
+
They are enabled for model's primary key with `#uuid_generator` method.
|
292
|
+
For example:
|
293
|
+
|
294
|
+
[source]
|
295
|
+
--------------------------------------------------------------------------------
|
296
|
+
class Author < ActiveRecord::Base
|
297
|
+
include ActiveID::Model
|
298
|
+
attribute :id, ActiveID::Type::StringUUID.new
|
299
|
+
uuid_generator :time
|
300
|
+
end
|
301
|
+
--------------------------------------------------------------------------------
|
302
|
+
|
303
|
+
=== Name-based primary keys a.k.a. natural keys (version 5 UUIDs)
|
304
|
+
|
305
|
+
They are enabled for model's primary key by passing attribute names to
|
306
|
+
`#natural_key` method, and namespace to `#uuid_namespace` method. The latter
|
307
|
+
method accepts only UUIDs, either in string format, or a `UUIDTools::UUID`
|
308
|
+
object. If `#uuid_namespace` method is omitted, then ISO OID namespace is used.
|
309
|
+
|
310
|
+
In following example, a natural key in `a6908e1e-5493-4c55-a11d-cd8445654de6`
|
311
|
+
namespace will be build of values of `author_id`, and `title` attributes.
|
312
|
+
|
313
|
+
[source]
|
314
|
+
--------------------------------------------------------------------------------
|
315
|
+
class Work < ActiveRecord::Base
|
316
|
+
include ActiveID::Model
|
317
|
+
attribute :id, ActiveID::Type::BinaryUUID.new
|
318
|
+
attribute :author_id, ActiveID::Type::BinaryUUID.new
|
319
|
+
belongs_to :author
|
320
|
+
natural_key :author_id, :title
|
321
|
+
uuid_namespace "a6908e1e-5493-4c55-a11d-cd8445654de6"
|
322
|
+
end
|
323
|
+
--------------------------------------------------------------------------------
|
324
|
+
|
325
|
+
== Choosing between string and binary serialization
|
326
|
+
|
327
|
+
ActiveID allows you to choose between two ways of UUID serialization:
|
328
|
+
as 36 characters long string, or as 16 bytes long binary.
|
329
|
+
|
330
|
+
In PostgreSQL, the answer is easy: you should always choose string
|
331
|
+
serialization. It perfectly works with native `UUID` data type, which is
|
332
|
+
a non-standard feature of PostgreSQL. It also works with textual data types
|
333
|
+
(i.e. `VARCHAR`, `TEXT`, etc.), but a `UUID` type seems to be a better choice
|
334
|
+
for performance reasons. Because of special syntax requirements in PostgreSQL,
|
335
|
+
it does not work with binary types (i.e. `BYTEA`), however it seems to be
|
336
|
+
a neglect-able issue, as `UUID` type is more suitable. Please open an issue
|
337
|
+
if you disagree.
|
338
|
+
|
339
|
+
In other RDBSs, either human-readability, or performance must be sacrificed.
|
340
|
+
|
341
|
+
With binary serialization, UUIDs are stored in a space-efficient way as 16 bytes
|
342
|
+
long binaries. This is especially beneficial when column is indexed, which is
|
343
|
+
a very common case. Smaller value size means that a bigger piece of index can
|
344
|
+
be kept in RAM, which often leads to a significant performance boost.
|
345
|
+
The downside is that this representation is difficult to read for humans, who
|
346
|
+
access serialized values outside Rails (e.g. in a database console, or in
|
347
|
+
database logs). See also an excellent article "link:{percona_blog}[Store UUID
|
348
|
+
in an optimized way]" in Percona blog for more information about storing UUIDs
|
349
|
+
as binaries.
|
350
|
+
|
351
|
+
With string serialization, UUIDs are stored as 36 characters long strings, which
|
352
|
+
consist only of lowercase hexadecimal digits, and dashes
|
353
|
+
(`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`). They are easy to read for humans, but
|
354
|
+
may hamper performance of indices, especially in case of large tables.
|
355
|
+
|
356
|
+
=== Reading binary UUIDs in a database console
|
357
|
+
|
358
|
+
MySQL features a {mysql_uuid}[`BIN_TO_UUID()`] function, which converts binary
|
359
|
+
UUIDs to their human-readable string representation. There is
|
360
|
+
{maria_jira_uuid_func}[a feature request] to add a similar feature to MariaDB.
|
361
|
+
|
362
|
+
== Contributing
|
363
|
+
|
364
|
+
First, thank you for contributing! We love pull requests from everyone.
|
365
|
+
By participating in this project, you hereby grant
|
366
|
+
https://www.ribose.com[Ribose Inc.] the right to grant or transfer an
|
367
|
+
unlimited number of non exclusive licenses or sub-licenses to third
|
368
|
+
parties, under the copyright covering the contribution to use the
|
369
|
+
contribution by all means.
|
370
|
+
|
371
|
+
Here are a few technical guidelines to follow:
|
372
|
+
|
373
|
+
1. Open an https://github.com/riboseinc/enmail/issues[issue] to discuss
|
374
|
+
a new feature.
|
375
|
+
2. Write tests to support your new feature.
|
376
|
+
3. Make sure the entire test suite passes locally and on CI.
|
377
|
+
4. Open a Pull Request.
|
378
|
+
5. After receiving feedback, perform
|
379
|
+
https://help.github.com/articles/about-git-rebase/[an interactive rebase]
|
380
|
+
on your branch, in order to create a series of cohesive commits with
|
381
|
+
descriptive messages.
|
382
|
+
6. Party!
|
383
|
+
|
384
|
+
== Credits
|
385
|
+
|
386
|
+
This gem is developed, maintained and funded by {ribose}[Ribose Inc.]
|
387
|
+
|
388
|
+
The {gem_original}[ActiveID] gem which ActiveID was based on has been developed by Nate Murray
|
389
|
+
with notable help of:
|
390
|
+
|
391
|
+
* pyromaniac
|
392
|
+
* Andrew Kane
|
393
|
+
* Devin Foley
|
394
|
+
* Arkadiy Zabazhanov
|
395
|
+
* Jean-Denis Koeck
|
396
|
+
* Florian Staudacher
|
397
|
+
* Schuyler Erle
|
398
|
+
* Florian Schwab
|
399
|
+
* Thomas Guillory
|
400
|
+
* Daniel Blanco Rojas
|
401
|
+
* Olivier Amblet
|
402
|
+
|
403
|
+
== License
|
404
|
+
|
405
|
+
The gem is available as open source under the terms of the {mit_lic}[MIT
|
406
|
+
License].
|
407
|
+
|
408
|
+
== See also
|
409
|
+
|
410
|
+
* {rfc_uuids}[RFC 4122] "A Universally Unique IDentifier (UUID) URN Namespace"
|
411
|
+
* {gem_original}[ActiveID] gem (supports Rails < 5)
|