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 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
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /coverage
11
+ /spec/debug.log
12
+ /gemfiles/*.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
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
@@ -0,0 +1,11 @@
1
+ appraise "rails-3-2" do
2
+ gem "rails", "3.2.22.2"
3
+ end
4
+
5
+ appraise "rails-4-0" do
6
+ gem "rails", "4.0.13"
7
+ end
8
+
9
+ appraise "rails-4-1" do
10
+ gem "rails", "4.1.15"
11
+ end
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in has_uuid.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,4 @@
1
+ guard :test do
2
+ watch(%r{^lib/has_uuid/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
3
+ watch(%r{^test/(.+)_test\.rb$}) { |m| "lib/has_uuid/#{m[1]}.rb" }
4
+ end
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
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "3.2.22.2"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "4.0.13"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "4.1.15"
6
+
7
+ gemspec :path => "../"
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
@@ -0,0 +1,3 @@
1
+ module HasUuid
2
+ VERSION = "0.2.0"
3
+ 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: []