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 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 "locations" WHERE ("locations"."unique_hash" = 189338025) LIMIT 1
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" = 189338025) LIMIT 1
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" = 189338025) LIMIT 1
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.4
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 %>, :integer
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) }
@@ -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.4"
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-03-26}
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 = [
@@ -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 = unique_attributes.sort_by{ |pair| pair.hash }.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 unique_attributes
56
- attributes.except(*self.class.read_inheritable_attribute(:is_unique_ignore))
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
@@ -14,7 +14,7 @@ def setup_db
14
14
  t.string :name
15
15
  t.float :lat, :lng
16
16
  t.string :alias
17
- t.integer :unique_hash
17
+ t.string :unique_hash, :length => 20
18
18
  t.timestamps
19
19
  end
20
20
  end
@@ -16,8 +16,8 @@ class PopulatedPlace < Location
16
16
  end
17
17
 
18
18
  describe "A unique model" do
19
- before(:each) do
20
- @it = Location.create(
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 = @it.clone
30
- lambda { new_record.save! }.
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 = @it.clone
38
+ new_record = subject.clone
36
39
  new_record.alias = 'Greater London'
37
- lambda { new_record.save! }.
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 = @it.clone
44
+ new_record = subject.clone
43
45
  new_record.save!
44
- new_record.should == @it
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 = @it.clone
50
+ new_record = subject.clone
49
51
  new_record.name = 'Greater London'
50
- lambda { new_record.save! }.
51
- should change(Location, :count).by(1)
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 = @it.clone
57
- @it.update_attribute(:name, 'Greater London')
58
- lambda { new_record.save! }.
59
- should change(Location, :count).by(1)
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
- before(:each) do
65
- @it = PopulatedPlace.create(
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 = @it.clone
74
+ new_record = subject.clone
75
75
  new_record.name = 'Greater London'
76
76
  new_record.alias = 'London, UK'
77
- lambda { new_record.save! }.
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
- @it.should_receive(:calculate_unique_hash).once
83
- @it.save
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
- - 4
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-03-26 00:00:00 +03:00
17
+ date: 2010-05-18 00:00:00 +04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency