Is Unique

This is a simple ActiveRecord extension that makes sure that no duplicate records are added to the database. When you create a new model instance that already exists, it won't create a new record, but will return the existing one instead. The extension generates a hash off the model attributes and use it to check for existing records. Of course, it ignores primary key, created_at, updated_at and other columns that you ask it to.

Here's an example. Say you have a Location model and you want all your locations to be unique. When you add a new location you'd like to check whether it already exists and if it does, you don't want to create a new record.

$ ./script/generate model Location name:string lat:float lng:float alias:string

IsUnique will take care of that. You need to install the gem:

$ sudo gem install is_unique

Then you need to add a column to your table to store a unique hash that would be used to check for existing records. The gem includes a generator for that:

$ ./script/generate is_unique Location

In the model you need to specify that it's supposed to be unique:

class Location < ActiveRecord::Base is_unique end

Then fire up the console and try it:

l = Location.create(:name => 'London', :lat => '51.5', :lng => '-0.13', :alias => 'Londinium') Location Load (0.4ms) SELECT * FROM locations WHERE (locations.unique_hash = '...') LIMIT 1

Location Create (0.6ms) INSERT INTO "locations" ... => #<Location id: ...>

l.clone.save Location Load (0.7ms) SELECT * FROM "locations" WHERE ("locations"."unique_hash" = '...') LIMIT 1

Location Load (0.6ms) SELECT * FROM "locations" WHERE ("locations"."id" = 1) => true

Now there's a couple of customization options. First, you can ignore certain attributes when calculating the hash. Say there may be different aliases for a location, but you still want just one record in the database. The is_unique method accepts an ignore parameter for that purpose:

class Location < ActiveRecord::Base is_unique :ignore => :alias end

l = Location.last Location Load (0.7ms) SELECT * FROM "locations" ORDER BY locations.id DESC LIMIT 1

=> #<Location ...>

n = l.clone => #<Location ...> n.alias = 'Something different' => "Something different" n.save Location Load (0.7ms) SELECT * FROM "locations" WHERE ("locations"."unique_hash" = '...') LIMIT 1

Location Load (0.6ms) SELECT * FROM "locations" WHERE ("locations"."id" = 2) => true

n == l => true

If you'd like to name the unique hash column differently (it's "unique_hash" by default), you need to provide the name to the generator:

$ ./script/generate is_unique Location my_unique_hash_column_name

and specify it in the model:

class Location < ActiveRecord::Base is_unique :hash_column => :my_unique_has_column_name end

Copyright (c) 2010 Loco2, released under the MIT license