activeuuid 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.mkd +123 -0
- data/Rakefile +1 -0
- data/activeuuid.gemspec +21 -0
- data/lib/activeuuid.rb +6 -0
- data/lib/activeuuid/railtie.rb +21 -0
- data/lib/activeuuid/uuid.rb +60 -0
- data/lib/activeuuid/version.rb +3 -0
- metadata +65 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.mkd
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# activeuuid
|
2
|
+
|
3
|
+
Add `binary(16)` UUIDs to ActiveRecord.
|
4
|
+
|
5
|
+
## Example
|
6
|
+
|
7
|
+
### Create a Migration
|
8
|
+
|
9
|
+
`activeuuid` adds the `uuid` type to your migrations. Example:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
class CreateEmails < ActiveRecord::Migration
|
13
|
+
def self.up
|
14
|
+
create_table :emails, :id => false do |t|
|
15
|
+
t.uuid :id, :unique => true
|
16
|
+
t.uuid :sender_id # belongs_to :sender
|
17
|
+
|
18
|
+
t.string :subject
|
19
|
+
t.text :body
|
20
|
+
|
21
|
+
t.timestamp :sent_at
|
22
|
+
t.timestamps
|
23
|
+
end
|
24
|
+
add_index :emails, :id
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.down
|
28
|
+
drop_table :emails
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
### include ActiveUUID::UUID in your model
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
class Email < ActiveRecord::Base
|
37
|
+
include ActiveUUID::UUID
|
38
|
+
belongs_to :sender
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
### use it:
|
43
|
+
Here are some example specs:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
require 'spec_helper'
|
47
|
+
|
48
|
+
describe Email do
|
49
|
+
|
50
|
+
context "when using uuid's as keys" do
|
51
|
+
before(:each) do
|
52
|
+
Email.delete_all
|
53
|
+
@guid = "1dd74dd0-d116-11e0-99c7-5ac5d975667e"
|
54
|
+
@e = Email.new(:subject => "hello", :body => "world") {|e| e.id = UUIDTools::UUID.parse(@guid) }
|
55
|
+
@e.save
|
56
|
+
end
|
57
|
+
|
58
|
+
it "the id guid should be equal to the uuid" do
|
59
|
+
@e.id.to_s.should eql(@guid)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should be able to find an email by the uuid" do
|
63
|
+
f = Email.find(UUIDTools::UUID.parse(@guid))
|
64
|
+
f.id.to_s.should eql(@guid)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
## Motivation
|
72
|
+
|
73
|
+
From [2]:
|
74
|
+
|
75
|
+
> [Here is a] UUID: 1e8ef774-581c-102c-bcfe-f1ab81872213
|
76
|
+
>
|
77
|
+
> A UUID like the one above is 36 characters long, including dashes. If you store this VARCHAR(36), you're going to decrease compare performance dramatically. This is your primary key, you don't want it to be slow.
|
78
|
+
>
|
79
|
+
> At its bit level, a UUID is 128 bits, which means it will fit into
|
80
|
+
> 16 bytes, note this is not very human readable, but it will keep
|
81
|
+
> storage low, and is only 4 times larger than a 32-bit int, or 2
|
82
|
+
> times larger than a 64-bit int.
|
83
|
+
|
84
|
+
Many of the existing examples of how to use UUIDs as primary keys
|
85
|
+
in Rails use strings rather than bytes (e.g. [3]).
|
86
|
+
|
87
|
+
However, this plugin stores the primary keys as bytes. To the
|
88
|
+
application the keys are represented by a UUIDTools::UUID object.
|
89
|
+
|
90
|
+
## Benefits of UUIDs as primary key
|
91
|
+
|
92
|
+
* allows multi-master write
|
93
|
+
* no locking due to auto-increment
|
94
|
+
* with time-based UUIDs you can store a timestamp within your UUID
|
95
|
+
* you can create natural keys
|
96
|
+
|
97
|
+
## Future work
|
98
|
+
* more transparent support for natural and composite keys
|
99
|
+
* support for MySQLs `INSERT ... ON DUPLICATE KEY UPDATE` syntax
|
100
|
+
* support a primary column name other than `id`
|
101
|
+
* tests
|
102
|
+
|
103
|
+
## Inspiration
|
104
|
+
James Golick's `friendly` is a great gem for NoSQL on MySQL. It's
|
105
|
+
a great gateway drug to systems like Cassandra for teams that are
|
106
|
+
already familiar with the ins-and-outs of MySQL.
|
107
|
+
|
108
|
+
## References
|
109
|
+
* [1] http://bret.appspot.com/entry/how-friendfeed-uses-mysql
|
110
|
+
* [2] http://kekoav.com/blog/36-computers/58-uuids-as-primary-keys-in-mysql.html
|
111
|
+
* [3] https://gist.github.com/937739
|
112
|
+
* [4] http://www.codinghorror.com/blog/2007/03/primary-keys-ids-versus-guids.html
|
113
|
+
* [5] http://krow.livejournal.com/497839.html
|
114
|
+
* [6] https://github.com/jamesgolick/friendly
|
115
|
+
|
116
|
+
## Dependencies
|
117
|
+
Rails ~> 3.1.0 - It uses the custom column serialization Aaron
|
118
|
+
Patterson introduced in Rails 3.1.
|
119
|
+
|
120
|
+
I'm using JRuby 1.9 (1.6.3) but this should work under MRI (YMMV).
|
121
|
+
|
122
|
+
## Author
|
123
|
+
Nate Murray <nate@xcombinator.com>
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/activeuuid.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "activeuuid/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "activeuuid"
|
7
|
+
s.version = Activeuuid::VERSION
|
8
|
+
s.authors = ["Nate Murray"]
|
9
|
+
s.email = ["nate@natemurray.com"]
|
10
|
+
s.homepage = "http://www.xcombinator.com"
|
11
|
+
s.summary = %q{Add binary UUIDs to ActiveRecord in MySQL}
|
12
|
+
s.description = %q{Add's binary (not string) UUIDs to ActiveRecord in MySQL}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
# s.add_development_dependency "rspec"
|
20
|
+
s.add_runtime_dependency "uuidtools"
|
21
|
+
end
|
data/lib/activeuuid.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'activeuuid'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
module ActiveUUID
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
railtie_name :activeuuid
|
7
|
+
initializer "activeuuid.configure_rails_initialization" do
|
8
|
+
|
9
|
+
module ActiveRecord::ConnectionAdapters
|
10
|
+
class TableDefinition
|
11
|
+
def uuid (*args)
|
12
|
+
options = args.extract_options!
|
13
|
+
column_names = args
|
14
|
+
column_names.each { |name| column(name, 'binary(16)', options) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module UUIDTools
|
2
|
+
class UUID
|
3
|
+
# monkey-patch Friendly::UUID to serialize UUIDs to MySQL
|
4
|
+
def quoted_id
|
5
|
+
s = raw.unpack("H*")[0]
|
6
|
+
"x'#{s}'"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ActiveUUID
|
12
|
+
class UUIDSerializer
|
13
|
+
def load(binary)
|
14
|
+
case binary
|
15
|
+
when UUIDTools::UUID then binary
|
16
|
+
when nil then nil
|
17
|
+
else UUIDTools::UUID.parse_raw(binary)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
def dump(uuid)
|
21
|
+
uuid ? uuid.raw : nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module UUID
|
26
|
+
extend ActiveSupport::Concern
|
27
|
+
|
28
|
+
included do
|
29
|
+
before_create :generate_uuid_if_needed
|
30
|
+
|
31
|
+
set_primary_key "id"
|
32
|
+
serialize :id, ActiveUUID::UUIDSerializer.new
|
33
|
+
|
34
|
+
|
35
|
+
def generate_uuid_if_needed
|
36
|
+
generate_uuid unless self.id
|
37
|
+
end
|
38
|
+
|
39
|
+
def generate_uuid
|
40
|
+
self.id = UUIDTools::UUID.timestamp_create
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module ClassMethods
|
45
|
+
#def uuids(*attributes)
|
46
|
+
# attributes.each do |attribute|
|
47
|
+
# class_eval <<-eos
|
48
|
+
# # def #{@association_name}
|
49
|
+
# # @_#{@association_name} ||= self.class.associations[:#{@association_name}].new_proxy(self)
|
50
|
+
# # end
|
51
|
+
# eos
|
52
|
+
# end
|
53
|
+
#end
|
54
|
+
end
|
55
|
+
|
56
|
+
module InstanceMethods
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activeuuid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Nate Murray
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-08-29 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: uuidtools
|
16
|
+
requirement: &2156096440 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2156096440
|
25
|
+
description: Add's binary (not string) UUIDs to ActiveRecord in MySQL
|
26
|
+
email:
|
27
|
+
- nate@natemurray.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- Gemfile
|
34
|
+
- README.mkd
|
35
|
+
- Rakefile
|
36
|
+
- activeuuid.gemspec
|
37
|
+
- lib/activeuuid.rb
|
38
|
+
- lib/activeuuid/railtie.rb
|
39
|
+
- lib/activeuuid/uuid.rb
|
40
|
+
- lib/activeuuid/version.rb
|
41
|
+
homepage: http://www.xcombinator.com
|
42
|
+
licenses: []
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.8.6
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: Add binary UUIDs to ActiveRecord in MySQL
|
65
|
+
test_files: []
|