is_unique 0.0.4 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +5 -5
- data/VERSION +1 -1
- data/generators/is_unique/templates/is_unique_migration.rb +1 -1
- data/is_unique.gemspec +2 -2
- data/lib/is_unique.rb +9 -3
- data/spec/support/db.rb +1 -1
- data/spec/unique_model_spec.rb +25 -26
- metadata +3 -3
data/README
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Is Unique
|
2
2
|
=========
|
3
3
|
|
4
|
-
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 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.
|
4
|
+
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.
|
5
5
|
|
6
6
|
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.
|
7
7
|
|
@@ -21,14 +21,14 @@ class Location < ActiveRecord::Base
|
|
21
21
|
is_unique
|
22
22
|
end
|
23
23
|
|
24
|
-
Then fire up console and try it:
|
24
|
+
Then fire up the console and try it:
|
25
25
|
|
26
26
|
>> l = Location.create(:name => 'London', :lat => '51.5', :lng => '-0.13', :alias => 'Londinium')
|
27
|
-
Location Load (0.4ms) SELECT * FROM
|
27
|
+
Location Load (0.4ms) SELECT * FROM `locations` WHERE (`locations`.`unique_hash` = '...') LIMIT 1
|
28
28
|
Location Create (0.6ms) INSERT INTO "locations" ...
|
29
29
|
=> #<Location id: ...>
|
30
30
|
>> l.clone.save
|
31
|
-
Location Load (0.7ms) SELECT * FROM "locations" WHERE ("locations"."unique_hash" =
|
31
|
+
Location Load (0.7ms) SELECT * FROM "locations" WHERE ("locations"."unique_hash" = '...') LIMIT 1
|
32
32
|
Location Load (0.6ms) SELECT * FROM "locations" WHERE ("locations"."id" = 1)
|
33
33
|
=> true
|
34
34
|
|
@@ -46,7 +46,7 @@ end
|
|
46
46
|
>> n.alias = 'Something different'
|
47
47
|
=> "Something different"
|
48
48
|
>> n.save
|
49
|
-
Location Load (0.7ms) SELECT * FROM "locations" WHERE ("locations"."unique_hash" =
|
49
|
+
Location Load (0.7ms) SELECT * FROM "locations" WHERE ("locations"."unique_hash" = '...') LIMIT 1
|
50
50
|
Location Load (0.6ms) SELECT * FROM "locations" WHERE ("locations"."id" = 2)
|
51
51
|
=> true
|
52
52
|
>> n == l
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class <%= file_name.camelcase %> < ActiveRecord::Migration
|
2
2
|
def self.up
|
3
|
-
add_column :<%= table_name %>, :<%= unique_hash_column %>, :
|
3
|
+
add_column :<%= table_name %>, :<%= unique_hash_column %>, :string, :limit => 20
|
4
4
|
add_index :<%= table_name %>, :<%= unique_hash_column %>
|
5
5
|
# Generate hashes for existing records
|
6
6
|
<%= class_name %>.find_each { |r| r.save(false) }
|
data/is_unique.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{is_unique}
|
8
|
-
s.version = "0.0
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Eugene Bolshakov"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-05-18}
|
13
13
|
s.description = %q{Makes ActiveRecord return existing records instead of creating duplicates}
|
14
14
|
s.email = %q{eugene.bolshakov@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
data/lib/is_unique.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
1
3
|
module IsUnique
|
2
4
|
def self.included(base)
|
3
5
|
base.extend IsUnique::ClassMethods
|
@@ -49,11 +51,15 @@ module IsUnique
|
|
49
51
|
end
|
50
52
|
|
51
53
|
def calculate_unique_hash
|
52
|
-
self.is_unique_hash =
|
54
|
+
self.is_unique_hash = Digest::SHA1.digest(
|
55
|
+
unique_attribute_names.inject('') do |r, e|
|
56
|
+
r + "#{e}#{read_attribute_before_type_cast(e)}"
|
57
|
+
end
|
58
|
+
)
|
53
59
|
end
|
54
60
|
|
55
|
-
def
|
56
|
-
|
61
|
+
def unique_attribute_names
|
62
|
+
attribute_names - self.class.read_inheritable_attribute(:is_unique_ignore)
|
57
63
|
end
|
58
64
|
end
|
59
65
|
end
|
data/spec/support/db.rb
CHANGED
data/spec/unique_model_spec.rb
CHANGED
@@ -16,8 +16,8 @@ class PopulatedPlace < Location
|
|
16
16
|
end
|
17
17
|
|
18
18
|
describe "A unique model" do
|
19
|
-
|
20
|
-
|
19
|
+
subject do
|
20
|
+
Location.create(
|
21
21
|
:name => 'London',
|
22
22
|
:lat => '51.5084152563931',
|
23
23
|
:lng => '-0.125532746315002',
|
@@ -25,44 +25,44 @@ describe "A unique model" do
|
|
25
25
|
)
|
26
26
|
end
|
27
27
|
|
28
|
+
it 'should have a 20 characters long unique hash' do
|
29
|
+
subject.unique_hash.length.should == 20
|
30
|
+
end
|
31
|
+
|
28
32
|
it "should not create a new record with the same attributes" do
|
29
|
-
new_record =
|
30
|
-
|
31
|
-
should_not change(Location, :count)
|
33
|
+
new_record = subject.clone
|
34
|
+
expect { new_record.save! }.to_not change(Location, :count)
|
32
35
|
end
|
33
36
|
|
34
37
|
it "should not create a new record with a different ignored attribute" do
|
35
|
-
new_record =
|
38
|
+
new_record = subject.clone
|
36
39
|
new_record.alias = 'Greater London'
|
37
|
-
|
38
|
-
should_not change(Location, :count)
|
40
|
+
expect { new_record.save! }.to_not change(Location, :count)
|
39
41
|
end
|
40
42
|
|
41
43
|
it "should return the existing record on creation" do
|
42
|
-
new_record =
|
44
|
+
new_record = subject.clone
|
43
45
|
new_record.save!
|
44
|
-
new_record.should ==
|
46
|
+
new_record.should == subject
|
45
47
|
end
|
46
48
|
|
47
49
|
it "should allow to create a record with different attributes" do
|
48
|
-
new_record =
|
50
|
+
new_record = subject.clone
|
49
51
|
new_record.name = 'Greater London'
|
50
|
-
|
51
|
-
|
52
|
-
new_record.should_not == @it
|
52
|
+
expect { new_record.save! }.to change(Location, :count).by(1)
|
53
|
+
new_record.should_not == subject
|
53
54
|
end
|
54
55
|
|
55
56
|
it "should allow to create a record with original attributes when an existing record is updated" do
|
56
|
-
new_record =
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
new_record.should_not == @it
|
57
|
+
new_record = subject.clone
|
58
|
+
subject.update_attribute(:name, 'Greater London')
|
59
|
+
expect { new_record.save! }.to change(Location, :count).by(1)
|
60
|
+
new_record.should_not == subject
|
61
61
|
end
|
62
62
|
|
63
63
|
context "that is a subclass of another model" do
|
64
|
-
|
65
|
-
|
64
|
+
subject do
|
65
|
+
PopulatedPlace.create(
|
66
66
|
:name => 'London',
|
67
67
|
:lat => '51.5084152563931',
|
68
68
|
:lng => '-0.125532746315002',
|
@@ -71,16 +71,15 @@ describe "A unique model" do
|
|
71
71
|
end
|
72
72
|
|
73
73
|
it "should be able to override columns that should be ignored" do
|
74
|
-
new_record =
|
74
|
+
new_record = subject.clone
|
75
75
|
new_record.name = 'Greater London'
|
76
76
|
new_record.alias = 'London, UK'
|
77
|
-
|
78
|
-
should_not change(PopulatedPlace, :count)
|
77
|
+
expect { new_record.save! }.to_not change(PopulatedPlace, :count)
|
79
78
|
end
|
80
79
|
|
81
80
|
it "should run callbacks just once" do
|
82
|
-
|
83
|
-
|
81
|
+
subject.should_receive(:calculate_unique_hash).once
|
82
|
+
subject.save
|
84
83
|
end
|
85
84
|
end
|
86
85
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 0.0.4
|
9
|
+
version: 0.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Eugene Bolshakov
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-05-18 00:00:00 +04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|