rails_has_uuid 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +16 -0
- data/Appraisals +11 -0
- data/Gemfile +4 -0
- data/Guardfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +234 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/gemfiles/rails_3_2.gemfile +7 -0
- data/gemfiles/rails_4_0.gemfile +7 -0
- data/gemfiles/rails_4_1.gemfile +7 -0
- data/has_uuid.gemspec +32 -0
- data/lib/has_uuid.rb +67 -0
- data/lib/has_uuid/active_record/associations/builder/collection_association.rb +73 -0
- data/lib/has_uuid/active_record/associations/builder/singular_association.rb +51 -0
- data/lib/has_uuid/active_record/associations/collection_association.rb +28 -0
- data/lib/has_uuid/active_record/associations/singular_association.rb +24 -0
- data/lib/has_uuid/active_record/belongs_to_association.rb +27 -0
- data/lib/has_uuid/active_record/connection_adapters/migration.rb +15 -0
- data/lib/has_uuid/active_record/finder_methods.rb +54 -0
- data/lib/has_uuid/active_record/reflection.rb +42 -0
- data/lib/has_uuid/railtie.rb +22 -0
- data/lib/has_uuid/version.rb +3 -0
- metadata +198 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fe1a840f333bb81266b31143361bb208d9cf8de5
|
4
|
+
data.tar.gz: e5a4d58e367ed67670e6c09e5f3d7c73d6a06e9b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 601952672befc862b8aa84bde7a29cae13d2452b5b0d023eb71183148d1839c5c91184d0811990cd3fc48e7de7baa632ace502e87df034a953a50e664c21d7f6
|
7
|
+
data.tar.gz: e766bf870602fe7f22472117f36b84fd4f1c58dc33446d1161830e40420b2aceaadf7fc017a280b1bed6d12197485763a47665fc6323adb8e774969cb017bd26
|
data/.document
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.3.0
|
4
|
+
- 2.2.0
|
5
|
+
- 2.1.0
|
6
|
+
- 2.0.0
|
7
|
+
- 1.9.3
|
8
|
+
|
9
|
+
before_install: gem install bundler -v 1.11.2
|
10
|
+
|
11
|
+
gemfile:
|
12
|
+
- gemfiles/rails_3_2.gemfile
|
13
|
+
- gemfiles/rails_4_0.gemfile
|
14
|
+
- gemfiles/rails_4_1.gemfile
|
15
|
+
|
16
|
+
script: "bundle exec rake spec"
|
data/Appraisals
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Myles Eftos
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
# has_uuid [![Build Status](https://travis-ci.org/madpilot/has_uuid.svg?branch=master)](https://travis-ci.org/madpilot/has_uuid)
|
2
|
+
|
3
|
+
A gem to help you retrofit UUIDs to your existing Rails application.
|
4
|
+
|
5
|
+
## The scenario
|
6
|
+
|
7
|
+
You have an existing Rails site that uses auto-incrementing ids, that you want to add, say, offline syncronization. The problem with auto-incrementing ids is that you will hit clashes
|
8
|
+
if you create entries offline.
|
9
|
+
|
10
|
+
One solution is to use UUIDs, which has a very, very low probability of clashing. Now the problem is how do you add that to Rails? One way is to replace all the auto-incrementing ids
|
11
|
+
with uuids, using something like activeuuid. This can be problematic if you have a running site, as converting all the ids and relationships would be a pain.
|
12
|
+
|
13
|
+
Enter: has_uuid
|
14
|
+
|
15
|
+
To use has_uuid you mirror all of the primary key id, and foreign key ids with another uuid column, and it makes sure you can search the whole object graph using uuids! If that didn't make sense check this out.
|
16
|
+
|
17
|
+
## Example
|
18
|
+
|
19
|
+
###Migration
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class SetupDatabase < ActiveRecord::Migration
|
23
|
+
def up
|
24
|
+
create_table :record_labels do |t|
|
25
|
+
t.string :name
|
26
|
+
t.uuid :uuid
|
27
|
+
end
|
28
|
+
|
29
|
+
create_table :albums do |t|
|
30
|
+
t.uuid :uuid
|
31
|
+
t.string :name
|
32
|
+
t.integer :record_label_id
|
33
|
+
t.uuid :record_label_uuid
|
34
|
+
t.integer :artist_id
|
35
|
+
t.uuid :artist_uuid
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
has_uuid adds a uuid type to mirations. On SQLite and MySQL it's a binary(16), on PostgreSQL it uses their native uuid type. Notice how we have both a
|
42
|
+
record_label_id and record_label_uuid column...
|
43
|
+
|
44
|
+
###Model
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class RecordLabel < ActiveRecord::Base
|
48
|
+
has_uuid
|
49
|
+
has_many :albums
|
50
|
+
end
|
51
|
+
|
52
|
+
class Album < ActiveRecord::Base
|
53
|
+
has_uuid
|
54
|
+
belongs_to :record_label
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
By calling the has_uuid class method, your model is primed.
|
59
|
+
|
60
|
+
### Finders
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
|
64
|
+
# id: 1, the autogenerated uuid is: cf1ba930-6946-4bd5-9265-d9043e5dbb93
|
65
|
+
record_label = RecordLabel.create!(:name => 'Misfit Records')
|
66
|
+
# id: 2, the autogenerated uuid is: a957f2d6-371e-4275-9aec-b54a380688e0
|
67
|
+
|
68
|
+
RecordLabel.find(1, 2)
|
69
|
+
RecordLabel.find('cf1ba930-6946-4bd5-9265-d9043e5dbb93', a957f2d6-371e-4275-9aec-b54a380688e0)
|
70
|
+
RecordLabel.find(UUIDTools::UUID.parse('cf1ba930-6946-4bd5-9265-d9043e5dbb93'), UUIDTools::UUID.parse('a957f2d6-371e-4275-9aec-b54a380688e0'))
|
71
|
+
```
|
72
|
+
|
73
|
+
...will return an array of objects that match those ids
|
74
|
+
|
75
|
+
### Relationships
|
76
|
+
```ruby
|
77
|
+
record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
|
78
|
+
artist_1 = Artist.create!(:name => 'NOFX')
|
79
|
+
|
80
|
+
artist.record_label = record_label
|
81
|
+
artist.record_label_uuid
|
82
|
+
```
|
83
|
+
|
84
|
+
Will return the uuid of the associated record label.
|
85
|
+
|
86
|
+
The reverse is also true
|
87
|
+
```ruby
|
88
|
+
record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
|
89
|
+
artist_1 = Artist.create!(:name => 'NOFX')
|
90
|
+
|
91
|
+
artist.record_label_uuid = record_label.uuid
|
92
|
+
artist.record_label
|
93
|
+
```
|
94
|
+
|
95
|
+
Will return the record label object
|
96
|
+
|
97
|
+
Finally, it'll find the uuid when you associate via id
|
98
|
+
i
|
99
|
+
```ruby
|
100
|
+
record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
|
101
|
+
artist_1 = Artist.create!(:name => 'NOFX')
|
102
|
+
|
103
|
+
artist.record_label_id = record_label.id
|
104
|
+
artist.record_label_uuid
|
105
|
+
```
|
106
|
+
|
107
|
+
Will be the uuid of the record label
|
108
|
+
|
109
|
+
Generally, a UUID will be a UUIDTools::UUID, but you can set uuids via a string, so these are equivalent:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
uuid = UUIDTools::UUID.random_create
|
113
|
+
=> #<UUID:0x3fd4186240e0 UUID:7c9748da-f9fe-467e-bdb3-34ce2dc67605>
|
114
|
+
|
115
|
+
record_label = RecordLabel.create!(:name => 'Fat Wreck Chords', :uuid => uuid)
|
116
|
+
artist_1 = Artist.create!(:name => 'NOFX')
|
117
|
+
|
118
|
+
artist.record_label_uuid = uuid.to
|
119
|
+
# is the same as
|
120
|
+
artist.record_label_uuid = '7c9748da-f9fe-467e-bdb3-34ce2dc67605'
|
121
|
+
```
|
122
|
+
|
123
|
+
### Collections
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
|
127
|
+
artist_1 = Artist.create!(:name => 'NOFX')
|
128
|
+
artist_2 = Artist.create!(:name => 'Strung Out')
|
129
|
+
artist_3 = Artist.create!(:name => 'Screeching Weasel')
|
130
|
+
|
131
|
+
record_label.artists = [ artist_1, artist_2, artist_3 ]
|
132
|
+
record_label.save!
|
133
|
+
|
134
|
+
record_label.artists_uuids
|
135
|
+
```
|
136
|
+
|
137
|
+
Returns an array of UUIDTools::UUID objects that correspond to artist_1, artist_2, artist_3
|
138
|
+
|
139
|
+
it also works the other way:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
|
143
|
+
artist_1 = Artist.create!(:name => 'NOFX')
|
144
|
+
artist_2 = Artist.create!(:name => 'Strung Out')
|
145
|
+
artist_3 = Artist.create!(:name => 'Screeching Weasel')
|
146
|
+
|
147
|
+
record_label.artist_uuids = [ artist_1.uuid, artist_2.uuid, artist_3.uuid ]
|
148
|
+
record_label.save!
|
149
|
+
|
150
|
+
record_label.artists
|
151
|
+
```
|
152
|
+
|
153
|
+
Returns artist_1, artist_2, artist_3
|
154
|
+
|
155
|
+
Finally, if you set a relationship id, it will automatically fetch the uuid for you
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
|
159
|
+
artist_1 = Artist.create!(:name => 'NOFX')
|
160
|
+
artist_2 = Artist.create!(:name => 'Strung Out')
|
161
|
+
artist_3 = Artist.create!(:name => 'Screeching Weasel')
|
162
|
+
|
163
|
+
record_label.artist_ids = [ artist_1.uuid, artist_2.uuid, artist_3.uuid ]
|
164
|
+
record_label.save!
|
165
|
+
|
166
|
+
record_label.artists_uuids
|
167
|
+
```
|
168
|
+
|
169
|
+
Will also return an array of UUIDTools::UUID objects that correspond to artist_1, artist_2, artist_3
|
170
|
+
|
171
|
+
All of these will return the same record
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
|
175
|
+
# id: 1, the autogenerated uuid is: cf1ba930-6946-4bd5-9265-d9043e5dbb93
|
176
|
+
RecordLabel.find(1)
|
177
|
+
RecordLabel.find('cf1ba930-6946-4bd5-9265-d9043e5dbb93')
|
178
|
+
RecordLabel.find(UUIDTools::UUID.parse('cf1ba930-6946-4bd5-9265-d9043e5dbb93'))
|
179
|
+
```
|
180
|
+
|
181
|
+
## What doesn't work
|
182
|
+
|
183
|
+
Unfortunately, because of the way ARel works, you can only search via a UUIDTools::UUID
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
uuid = UUIDTools::UUID.random_create
|
187
|
+
=> #<UUID:0x3fd4186323c0 UUID:7cd2feb5-6929-4288-9ea4-c4e68927f289>
|
188
|
+
|
189
|
+
Artist.where('uuid = ?', uuid) # This works
|
190
|
+
Artist.where('uuid = ?', '7cd2feb5-6929-4288-9ea4-c4e68927f289') # This won't (except on PostgreSQL)
|
191
|
+
|
192
|
+
As a result, this also won't work
|
193
|
+
Artist.find_by_uuid('7cd2feb5-6929-4288-9ea4-c4e68927f289')
|
194
|
+
```
|
195
|
+
|
196
|
+
## TODO
|
197
|
+
|
198
|
+
* Some more testing - I'm sure it will fail if you have a relationship between a has_uuidmodel and a regular one
|
199
|
+
* Release as a gem - it's not tested well enough yet.
|
200
|
+
* Probably other stuff I haven't thought of yet
|
201
|
+
|
202
|
+
## Contributing to has_uuid
|
203
|
+
|
204
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
205
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
206
|
+
* Fork the project.
|
207
|
+
* Start a feature/bugfix branch.
|
208
|
+
* Commit and push until you are happy with your contribution.
|
209
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
210
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
211
|
+
|
212
|
+
## Setting up for development environment
|
213
|
+
|
214
|
+
has_uuid uses the appraisal gem for testing against multiple versions of Rails.
|
215
|
+
|
216
|
+
To get started run:
|
217
|
+
|
218
|
+
```appraisal install```
|
219
|
+
|
220
|
+
Then to run tests against rails 3.2:
|
221
|
+
|
222
|
+
```appraisal rails-3-2 rspec```
|
223
|
+
|
224
|
+
Against rails 4.0
|
225
|
+
|
226
|
+
```appraisal rails-4-0 rspec```
|
227
|
+
|
228
|
+
Against rails 4.1
|
229
|
+
|
230
|
+
```appraisal rails-4-1 rspec```
|
231
|
+
|
232
|
+
## Copyright
|
233
|
+
|
234
|
+
Copyright (c) 2012 [MadPilot Productions](http://www.madpilot.com.au/). See LICENSE.txt for further details.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "has_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
|
data/bin/setup
ADDED
data/has_uuid.gemspec
ADDED
@@ -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 'has_uuid/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rails_has_uuid"
|
8
|
+
spec.version = HasUuid::VERSION
|
9
|
+
spec.authors = ["Myles Eftos"]
|
10
|
+
spec.email = ["myles@madpilot.com.au"]
|
11
|
+
|
12
|
+
spec.summary ="A gem to help you retrofit UUIDs to your existing Rails application. "
|
13
|
+
spec.description ="A gem to help you retrofit UUIDs to your existing Rails application. "
|
14
|
+
spec.homepage = "http://github.com/madpilot/has_uuid"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "uuidtools"
|
23
|
+
spec.add_dependency "activeuuid"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
28
|
+
spec.add_development_dependency "appraisal", "~> 2.1.0"
|
29
|
+
spec.add_development_dependency "sqlite3"
|
30
|
+
spec.add_development_dependency "database_cleaner"
|
31
|
+
spec.add_development_dependency "mocha"
|
32
|
+
end
|
data/lib/has_uuid.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require "has_uuid/version"
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'uuidtools'
|
5
|
+
require 'activeuuid'
|
6
|
+
|
7
|
+
module HasUuid
|
8
|
+
def self.included(base)
|
9
|
+
base.send :extend, ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def has_uuid(options = {})
|
14
|
+
self.class_eval do
|
15
|
+
cattr_accessor :has_uuid_options
|
16
|
+
self.has_uuid_options = options
|
17
|
+
options[:primary_uuid] ||= :uuid
|
18
|
+
|
19
|
+
if options[:primary_uuid] != :uuid
|
20
|
+
class_eval do
|
21
|
+
include ActiveUUID::UUID
|
22
|
+
|
23
|
+
def uuid
|
24
|
+
self.send self.class.primary_uuid
|
25
|
+
end
|
26
|
+
|
27
|
+
def uuid=(uuid)
|
28
|
+
self.send "#{self.class.primary_uuid}=".to_sym, uuid
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class_eval do
|
34
|
+
before_create :generate_uuids_if_needed
|
35
|
+
|
36
|
+
def generate_uuids_if_needed
|
37
|
+
unless self.uuid
|
38
|
+
begin
|
39
|
+
self.uuid = UUIDTools::UUID.random_create
|
40
|
+
end while !HasUuid.check_uuid(self)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def primary_uuid
|
48
|
+
self.has_uuid_options[:primary_uuid]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.check_uuid(klass)
|
53
|
+
return false if klass.uuid && klass.id && klass.class.where("#{klass.class.primary_uuid} = ?", klass.uuid).where('id <> ?', klass.id).count > 0
|
54
|
+
return false if klass.uuid && klass.new_record? && klass.class.where("#{klass.class.primary_uuid} = ?", klass.uuid).count > 0
|
55
|
+
return true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
require File.join(File.dirname(__FILE__), 'has_uuid', 'active_record', 'connection_adapters', 'migration.rb')
|
60
|
+
require File.join(File.dirname(__FILE__), 'has_uuid', 'active_record', 'finder_methods.rb')
|
61
|
+
require File.join(File.dirname(__FILE__), 'has_uuid', 'active_record', 'reflection.rb')
|
62
|
+
require File.join(File.dirname(__FILE__), 'has_uuid', 'active_record', 'associations', 'singular_association.rb')
|
63
|
+
require File.join(File.dirname(__FILE__), 'has_uuid', 'active_record', 'associations', 'collection_association.rb')
|
64
|
+
require File.join(File.dirname(__FILE__), 'has_uuid', 'active_record', 'associations', 'builder', 'collection_association.rb')
|
65
|
+
require File.join(File.dirname(__FILE__), 'has_uuid', 'active_record', 'associations', 'builder', 'singular_association.rb')
|
66
|
+
require File.join(File.dirname(__FILE__), 'has_uuid', 'active_record', 'belongs_to_association.rb')
|
67
|
+
require File.join(File.dirname(__FILE__), 'has_uuid', 'railtie.rb')
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module HasUuid
|
2
|
+
module ActiveRecord
|
3
|
+
module Associations
|
4
|
+
module Builder
|
5
|
+
module CollectionAssociation
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
def self.included(base)
|
8
|
+
if ::ActiveRecord::VERSION::STRING >= "4.1"
|
9
|
+
base.extend RedefinedReader
|
10
|
+
base.extend RedefinedWriter
|
11
|
+
|
12
|
+
base.class_eval do
|
13
|
+
class << self
|
14
|
+
alias_method_chain :define_readers, :uuid_args
|
15
|
+
alias_method_chain :define_writers, :uuid_args
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
else
|
20
|
+
base.class_eval do
|
21
|
+
include RedefinedReader
|
22
|
+
include RedefinedWriter
|
23
|
+
|
24
|
+
alias_method_chain :define_readers, :uuid_no_args
|
25
|
+
alias_method_chain :define_writers, :uuid_no_args
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module RedefinedReader
|
31
|
+
def define_readers_with_uuid(mixin, name)
|
32
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
33
|
+
def #{name.to_s.singularize}_uuids
|
34
|
+
association(:#{name}).uuids_reader
|
35
|
+
end
|
36
|
+
CODE
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_readers_with_uuid_args(mixin, name)
|
40
|
+
define_readers_without_uuid_args(mixin, name)
|
41
|
+
define_readers_with_uuid(mixin, name)
|
42
|
+
end
|
43
|
+
|
44
|
+
def define_readers_with_uuid_no_args
|
45
|
+
define_readers_without_uuid_no_args
|
46
|
+
define_readers_with_uuid(mixin, name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module RedefinedWriter
|
51
|
+
def define_writers_with_uuid(mixin, name)
|
52
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
53
|
+
def #{name.to_s.singularize}_uuids=(uuids)
|
54
|
+
association(:#{name}).uuids_writer(uuids)
|
55
|
+
end
|
56
|
+
CODE
|
57
|
+
end
|
58
|
+
|
59
|
+
def define_writers_with_uuid_args(mixin, name)
|
60
|
+
define_writers_without_uuid_args(mixin, name)
|
61
|
+
define_writers_with_uuid(mixin, name)
|
62
|
+
end
|
63
|
+
|
64
|
+
def define_writers_with_uuid_no_args
|
65
|
+
define_writers_without_uuid_no_args
|
66
|
+
define_writers_with_uuid(mixin, name)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module HasUuid
|
2
|
+
module ActiveRecord
|
3
|
+
module Associations
|
4
|
+
module Builder
|
5
|
+
module SingularAssociation
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
if ::ActiveRecord::VERSION::STRING >= "4.1"
|
10
|
+
base.extend RedefinedWriter
|
11
|
+
base.class_eval do
|
12
|
+
class << self
|
13
|
+
alias_method_chain :define_writers, :uuid_args
|
14
|
+
end
|
15
|
+
end
|
16
|
+
else
|
17
|
+
base.class_eval do
|
18
|
+
include RedefinedWriter
|
19
|
+
alias_method_chain :define_writers, :uuid_no_args
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module RedefinedWriter
|
25
|
+
def define_writers_with_uuid(mixin, name)
|
26
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
27
|
+
def #{name.to_s.singularize}_uuid=(uuids)
|
28
|
+
association(:#{name}).uuid_writer(uuids)
|
29
|
+
end
|
30
|
+
|
31
|
+
def #{name.to_s.singularize}_uuid
|
32
|
+
association(:#{name}).uuid_reader
|
33
|
+
end
|
34
|
+
CODE
|
35
|
+
end
|
36
|
+
|
37
|
+
def define_writers_with_uuid_args(mixin, name)
|
38
|
+
define_writers_without_uuid_args(mixin, name)
|
39
|
+
define_writers_with_uuid(mixin, name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def define_writers_with_uuid_no_args
|
43
|
+
define_writers_without_uuid_no_args
|
44
|
+
define_writers_with_uuid(mixin, name)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module HasUuid
|
2
|
+
module ActiveRecord
|
3
|
+
module Associations
|
4
|
+
module CollectionAssociation
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
included do
|
7
|
+
def uuids_reader
|
8
|
+
if loaded? || options[:finder_sql]
|
9
|
+
load_target.map do |record|
|
10
|
+
record.send(reflection.association_primary_uuid)
|
11
|
+
end
|
12
|
+
else
|
13
|
+
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_uuid}"
|
14
|
+
association_scope.pluck(column)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def uuids_writer(uuids)
|
19
|
+
uuid_column = reflection.primary_uuid_column
|
20
|
+
uuids = Array(uuids).reject { |uuid| uuid.blank? }
|
21
|
+
uuids.map! { |u| uuid_column.type_cast(u) }
|
22
|
+
replace(klass.find(uuids).index_by { |r| r.uuid }.values_at(*uuids))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module HasUuid
|
2
|
+
module ActiveRecord
|
3
|
+
module Associations
|
4
|
+
module SingularAssociation
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
included do
|
7
|
+
def uuid_writer(uuid)
|
8
|
+
replace(klass.find(uuid)) unless uuid.nil? || uuid.to_s.empty?
|
9
|
+
replace(nil) if uuid.nil? || uuid.to_s.empty?
|
10
|
+
end
|
11
|
+
|
12
|
+
def uuid_reader(force_reload = false)
|
13
|
+
id = self.owner.send(reflection.foreign_key)
|
14
|
+
if id
|
15
|
+
klass.find(id).uuid
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module HasUuid
|
2
|
+
module ActiveRecord
|
3
|
+
module BelongsToAssociation
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
def replace_keys_with_uuid(record)
|
8
|
+
if record.class.respond_to?(:has_uuid?) && record.class.has_uuid?
|
9
|
+
if foreign_uuid_present?
|
10
|
+
if record
|
11
|
+
owner[reflection.foreign_uuid] = record[reflection.association_uuid_key(record.class)]
|
12
|
+
else
|
13
|
+
owner[reflections.foreign_uuid] = nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
replace_keys_without_uuid(record)
|
18
|
+
end
|
19
|
+
alias_method_chain :replace_keys, :uuid
|
20
|
+
end
|
21
|
+
|
22
|
+
def foreign_uuid_present?
|
23
|
+
owner.class.columns.map(&:name).include?(reflection.foreign_uuid.to_s)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module HasUuid
|
2
|
+
module ActiveRecord
|
3
|
+
module ConnectionAdapters
|
4
|
+
module Migration
|
5
|
+
def uuid(*column_names)
|
6
|
+
options = column_names.extract_options!
|
7
|
+
column_names.each do |name|
|
8
|
+
type = @base.adapter_name.downcase == 'postgresql' ? 'uuid' : 'binary(16)'
|
9
|
+
column(name, "#{type}", options)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module HasUuid
|
2
|
+
VALID_FORMAT = /^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{2})([0-9a-f]{2})-([0-9a-f]{12})$/
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module FinderMethods
|
6
|
+
def find_one(id)
|
7
|
+
if id.is_a?(::UUIDTools::UUID) || id.to_s =~ VALID_FORMAT
|
8
|
+
id = ::UUIDTools::UUID.parse(id) if id.is_a?(String)
|
9
|
+
return self.find_by_uuid!(id)
|
10
|
+
end
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_some(ids)
|
15
|
+
if ids.length > 0 && ids.inject(true) { |r, id| r && (id.is_a?(::UUIDTools::UUID) || id.to_s =~ VALID_FORMAT) }
|
16
|
+
uuids = ids.map do |id|
|
17
|
+
id = ::UUIDTools::UUID.parse(id) if id.is_a?(String)
|
18
|
+
id
|
19
|
+
end
|
20
|
+
|
21
|
+
arr = where(table[:uuid].in(uuids))
|
22
|
+
if ::ActiveRecord::VERSION::MAJOR >= 4
|
23
|
+
result = arr.load
|
24
|
+
else
|
25
|
+
result = arr.all
|
26
|
+
end
|
27
|
+
|
28
|
+
expected_size =
|
29
|
+
if @limit_value && ids.size > @limit_value
|
30
|
+
@limit_value
|
31
|
+
else
|
32
|
+
ids.size
|
33
|
+
end
|
34
|
+
|
35
|
+
if @offset_value && (ids.size - @offset_value < expected_size)
|
36
|
+
expected_size = ids.size - @offset_value
|
37
|
+
end
|
38
|
+
|
39
|
+
if result.size == expected_size
|
40
|
+
result
|
41
|
+
else
|
42
|
+
conditions = arel.where_sql
|
43
|
+
conditions = " [#{conditions}]" if conditions
|
44
|
+
error = "Couldn't find all #{@klass.name.pluralize} with UUIDs "
|
45
|
+
error << "(#{ids.join(", ")})#{conditions} (found #{result.size} results, but was looking for #{expected_size})"
|
46
|
+
raise ::ActiveRecord::RecordNotFound, error
|
47
|
+
end
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module HasUuid
|
2
|
+
module ActiveRecord
|
3
|
+
module Reflection
|
4
|
+
module AssociationReflection
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
def primary_uuid_column
|
9
|
+
@primary_uuid_column ||= klass.columns.find { |c| c.name.to_sym == klass.primary_uuid }
|
10
|
+
end
|
11
|
+
|
12
|
+
def association_primary_uuid(klass = nil)
|
13
|
+
options[:primary_uuid] || primary_uuid(klass || self.klass)
|
14
|
+
end
|
15
|
+
|
16
|
+
def active_record_primary_uuid
|
17
|
+
@active_record_primary_uuid ||= options[:primary_uuid] || primary_uuid(active_record)
|
18
|
+
end
|
19
|
+
|
20
|
+
def foreign_uuid
|
21
|
+
@foreign_uuid ||= options[:foreign_uuid] || derive_foreign_uuid
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def primary_uuid(klass)
|
26
|
+
klass.primary_uuid || raise(UnknownPrimaryKey.new(klass))
|
27
|
+
end
|
28
|
+
|
29
|
+
def derive_foreign_uuid
|
30
|
+
if belongs_to?
|
31
|
+
"#{name}_uuid"
|
32
|
+
elsif options[:as]
|
33
|
+
"#{options[:as]}_uuid"
|
34
|
+
else
|
35
|
+
active_record.name.foreign_key
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module HasUuid
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
initializer 'has_uuid' do |app|
|
4
|
+
ActiveSupport.on_load :active_record do
|
5
|
+
::ActiveRecord::Relation.send :include, HasUuid::ActiveRecord::FinderMethods
|
6
|
+
::ActiveRecord::Reflection::AssociationReflection.send :include, HasUuid::ActiveRecord::Reflection::AssociationReflection
|
7
|
+
::ActiveRecord::Associations::BelongsToAssociation.send :include, HasUuid::ActiveRecord::BelongsToAssociation
|
8
|
+
::ActiveRecord::Associations::SingularAssociation.send :include, HasUuid::ActiveRecord::Associations::SingularAssociation
|
9
|
+
::ActiveRecord::Associations::CollectionAssociation.send :include, HasUuid::ActiveRecord::Associations::CollectionAssociation
|
10
|
+
::ActiveRecord::Associations::Builder::SingularAssociation.send :include, HasUuid::ActiveRecord::Associations::Builder::SingularAssociation
|
11
|
+
::ActiveRecord::Associations::Builder::CollectionAssociation.send :include, HasUuid::ActiveRecord::Associations::Builder::CollectionAssociation
|
12
|
+
::ActiveRecord::Base.send :include, HasUuid
|
13
|
+
end
|
14
|
+
|
15
|
+
ActiveSupport.on_load :activeuuid do
|
16
|
+
# We don't want our ids to be UUIDs so override activeuuid's implementation
|
17
|
+
::ActiveRecord::ConnectionAdapters::Table.send :include, HasUuid::ActiveRecord::ConnectionAdapters::Migrations if defined? ActiveRecord::ConnectionAdapters::Table
|
18
|
+
::ActiveRecord::ConnectionAdapters::TableDefinition.send :include, HasUuid::ActiveRecord::ConnectionAdapters::Migrations if defined? ActiveRecord::ConnectionAdapters::TableDefinition
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_has_uuid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Myles Eftos
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: uuidtools
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activeuuid
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.11'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.11'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: appraisal
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.1.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.1.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sqlite3
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: database_cleaner
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: mocha
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: 'A gem to help you retrofit UUIDs to your existing Rails application. '
|
140
|
+
email:
|
141
|
+
- myles@madpilot.com.au
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- ".document"
|
147
|
+
- ".gitignore"
|
148
|
+
- ".rspec"
|
149
|
+
- ".travis.yml"
|
150
|
+
- Appraisals
|
151
|
+
- Gemfile
|
152
|
+
- Gemfile.lock
|
153
|
+
- Guardfile
|
154
|
+
- LICENSE.txt
|
155
|
+
- README.md
|
156
|
+
- Rakefile
|
157
|
+
- bin/console
|
158
|
+
- bin/setup
|
159
|
+
- gemfiles/rails_3_2.gemfile
|
160
|
+
- gemfiles/rails_4_0.gemfile
|
161
|
+
- gemfiles/rails_4_1.gemfile
|
162
|
+
- has_uuid.gemspec
|
163
|
+
- lib/has_uuid.rb
|
164
|
+
- lib/has_uuid/active_record/associations/builder/collection_association.rb
|
165
|
+
- lib/has_uuid/active_record/associations/builder/singular_association.rb
|
166
|
+
- lib/has_uuid/active_record/associations/collection_association.rb
|
167
|
+
- lib/has_uuid/active_record/associations/singular_association.rb
|
168
|
+
- lib/has_uuid/active_record/belongs_to_association.rb
|
169
|
+
- lib/has_uuid/active_record/connection_adapters/migration.rb
|
170
|
+
- lib/has_uuid/active_record/finder_methods.rb
|
171
|
+
- lib/has_uuid/active_record/reflection.rb
|
172
|
+
- lib/has_uuid/railtie.rb
|
173
|
+
- lib/has_uuid/version.rb
|
174
|
+
homepage: http://github.com/madpilot/has_uuid
|
175
|
+
licenses:
|
176
|
+
- MIT
|
177
|
+
metadata: {}
|
178
|
+
post_install_message:
|
179
|
+
rdoc_options: []
|
180
|
+
require_paths:
|
181
|
+
- lib
|
182
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
188
|
+
requirements:
|
189
|
+
- - ">="
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
192
|
+
requirements: []
|
193
|
+
rubyforge_project:
|
194
|
+
rubygems_version: 2.4.6
|
195
|
+
signing_key:
|
196
|
+
specification_version: 4
|
197
|
+
summary: A gem to help you retrofit UUIDs to your existing Rails application.
|
198
|
+
test_files: []
|