0.0
No commit activity in last 3 years
No release in over 3 years
A gem to help you retrofit UUIDs to your existing Rails application.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 2.1.0
~> 1.11
>= 0
~> 10.0
~> 3.0

Runtime

 Project Readme

has_uuid Build Status No Maintenance Intended

A gem to help you retrofit UUIDs to your existing Rails application.

The scenario

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 if you create entries offline.

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 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.

Enter: has_uuid

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.

Installation

NOTE The name of the gem is rails_has_uuid because has_uuid was already taken.

Via Gemfile:

gem 'rails_has_uuid', require: 'has_uuid'

On the commandline

get install rails_has_uuid

Example

###Migration

class SetupDatabase < ActiveRecord::Migration
  def up
    create_table :record_labels do |t|
      t.string :name
      t.uuid :uuid
    end

    create_table :albums do |t|
      t.uuid :uuid
      t.string :name
      t.integer :record_label_id
      t.uuid :record_label_uuid
      t.integer :artist_id
      t.uuid :artist_uuid
    end
  end
end

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 record_label_id and record_label_uuid column...

###Model

class RecordLabel < ActiveRecord::Base
  has_uuid
  has_many :albums
end

class Album < ActiveRecord::Base
  has_uuid
  belongs_to :record_label
end

By calling the has_uuid class method, your model is primed.

Finders

record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
# id: 1, the autogenerated uuid is: cf1ba930-6946-4bd5-9265-d9043e5dbb93
record_label = RecordLabel.create!(:name => 'Misfit Records')
# id: 2, the autogenerated uuid is: a957f2d6-371e-4275-9aec-b54a380688e0

RecordLabel.find(1, 2)
RecordLabel.find('cf1ba930-6946-4bd5-9265-d9043e5dbb93', a957f2d6-371e-4275-9aec-b54a380688e0)
RecordLabel.find(UUIDTools::UUID.parse('cf1ba930-6946-4bd5-9265-d9043e5dbb93'), UUIDTools::UUID.parse('a957f2d6-371e-4275-9aec-b54a380688e0'))

...will return an array of objects that match those ids

Relationships

record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
artist_1 = Artist.create!(:name => 'NOFX')

artist.record_label = record_label
artist.record_label_uuid

Will return the uuid of the associated record label.

The reverse is also true

record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
artist_1 = Artist.create!(:name => 'NOFX')

artist.record_label_uuid = record_label.uuid
artist.record_label

Will return the record label object

Finally, it'll find the uuid when you associate via id i

record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
artist_1 = Artist.create!(:name => 'NOFX')

artist.record_label_id = record_label.id
artist.record_label_uuid

Will be the uuid of the record label

Generally, a UUID will be a UUIDTools::UUID, but you can set uuids via a string, so these are equivalent:

uuid = UUIDTools::UUID.random_create
 => #<UUID:0x3fd4186240e0 UUID:7c9748da-f9fe-467e-bdb3-34ce2dc67605>

record_label = RecordLabel.create!(:name => 'Fat Wreck Chords', :uuid => uuid)
artist_1 = Artist.create!(:name => 'NOFX')

artist.record_label_uuid = uuid.to
# is the same as
artist.record_label_uuid = '7c9748da-f9fe-467e-bdb3-34ce2dc67605'

Collections

record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
artist_1 = Artist.create!(:name => 'NOFX')
artist_2 = Artist.create!(:name => 'Strung Out')
artist_3 = Artist.create!(:name => 'Screeching Weasel')

record_label.artists = [ artist_1, artist_2, artist_3 ]
record_label.save!

record_label.artists_uuids

Returns an array of UUIDTools::UUID objects that correspond to artist_1, artist_2, artist_3

it also works the other way:

record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
artist_1 = Artist.create!(:name => 'NOFX')
artist_2 = Artist.create!(:name => 'Strung Out')
artist_3 = Artist.create!(:name => 'Screeching Weasel')

record_label.artist_uuids = [ artist_1.uuid, artist_2.uuid, artist_3.uuid ]
record_label.save!

record_label.artists

Returns artist_1, artist_2, artist_3

Finally, if you set a relationship id, it will automatically fetch the uuid for you

record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
artist_1 = Artist.create!(:name => 'NOFX')
artist_2 = Artist.create!(:name => 'Strung Out')
artist_3 = Artist.create!(:name => 'Screeching Weasel')

record_label.artist_ids = [ artist_1.uuid, artist_2.uuid, artist_3.uuid ]
record_label.save!

record_label.artists_uuids

Will also return an array of UUIDTools::UUID objects that correspond to artist_1, artist_2, artist_3

All of these will return the same record

record_label = RecordLabel.create!(:name => 'Fat Wreck Chords')
# id: 1, the autogenerated uuid is: cf1ba930-6946-4bd5-9265-d9043e5dbb93
RecordLabel.find(1)
RecordLabel.find('cf1ba930-6946-4bd5-9265-d9043e5dbb93')
RecordLabel.find(UUIDTools::UUID.parse('cf1ba930-6946-4bd5-9265-d9043e5dbb93'))

What doesn't work

Unfortunately, because of the way ARel works, you can only search via a UUIDTools::UUID

uuid = UUIDTools::UUID.random_create
 => #<UUID:0x3fd4186323c0 UUID:7cd2feb5-6929-4288-9ea4-c4e68927f289>

Artist.where('uuid = ?', uuid) # This works
Artist.where('uuid = ?', '7cd2feb5-6929-4288-9ea4-c4e68927f289') # This won't (except on PostgreSQL)

As a result, this also won't work
Artist.find_by_uuid('7cd2feb5-6929-4288-9ea4-c4e68927f289')

TODO

  • Some more testing - I'm sure it will fail if you have a relationship between a has_uuidmodel and a regular one
  • Release as a gem - it's not tested well enough yet.
  • Probably other stuff I haven't thought of yet

Contributing to has_uuid

  • Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
  • Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
  • Fork the project.
  • Start a feature/bugfix branch.
  • Commit and push until you are happy with your contribution.
  • Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
  • 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.

Setting up for development environment

has_uuid uses the appraisal gem for testing against multiple versions of Rails.

To get started run:

appraisal install

Then to run tests against rails 3.2:

appraisal rails-3-2 rspec

Against rails 4.0

appraisal rails-4-0 rspec

Against rails 4.1

appraisal rails-4-1 rspec

Copyright

Copyright (c) 2012 MadPilot Productions. See LICENSE.txt for further details.