is_unique 0.0.1
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/Rakefile +15 -0
- data/VERSION +1 -0
- data/generators/is_unique/is_unique_generator.rb +16 -0
- data/generators/is_unique/templates/is_unique_migration.rb +10 -0
- data/init.rb +1 -0
- data/is_unique.gemspec +51 -0
- data/lib/is_unique.rb +61 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/db.rb +31 -0
- data/spec/unique_model_spec.rb +57 -0
- metadata +86 -0
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gemspec|
|
4
|
+
gemspec.name = "is_unique"
|
5
|
+
gemspec.summary = "ActiveRecord extension to prevent duplicate records"
|
6
|
+
gemspec.description = "Makes ActiveRecord return existing records instead of creating duplicates"
|
7
|
+
gemspec.email = "eugene.bolshakov@gmail.com"
|
8
|
+
gemspec.homepage = "http://github.com/loco2/is_unique"
|
9
|
+
gemspec.authors = ["Eugene Bolshakov"]
|
10
|
+
gemspec.add_dependency 'activerecord', '2.3.5'
|
11
|
+
end
|
12
|
+
Jeweler::GemcutterTasks.new
|
13
|
+
rescue LoadError
|
14
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
15
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class IsUniqueGenerator < Rails::Generator::NamedBase
|
2
|
+
def manifest
|
3
|
+
record do |m|
|
4
|
+
m.migration_template 'is_unique_migration.rb', 'db/migrate',
|
5
|
+
:assigns => {:unique_hash_column => actions.first || 'unique_hash' }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def banner
|
10
|
+
"Usage: #{$0} #{spec.name} ModelName [custom_unique_has_column_name] [options]"
|
11
|
+
end
|
12
|
+
|
13
|
+
def file_name
|
14
|
+
"add_unique_hash_column_to_#{@plural_name}"
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class <%= file_name.camelcase %> < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
add_column :<%= table_name %>, :<%= unique_hash_column %>, :integer
|
4
|
+
add_index :<%= table_name %>, :<%= unique_hash_column %>
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.down
|
8
|
+
remove_column :<%= table_name %>, :<%= unique_hash_column %>
|
9
|
+
end
|
10
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), 'lib', 'is_unique')
|
data/is_unique.gemspec
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{is_unique}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Eugene Bolshakov"]
|
12
|
+
s.date = %q{2010-03-22}
|
13
|
+
s.description = %q{Makes ActiveRecord return existing records instead of creating duplicates}
|
14
|
+
s.email = %q{eugene.bolshakov@gmail.com}
|
15
|
+
s.files = [
|
16
|
+
"Rakefile",
|
17
|
+
"VERSION",
|
18
|
+
"generators/is_unique/is_unique_generator.rb",
|
19
|
+
"generators/is_unique/templates/is_unique_migration.rb",
|
20
|
+
"init.rb",
|
21
|
+
"is_unique.gemspec",
|
22
|
+
"lib/is_unique.rb",
|
23
|
+
"spec/spec_helper.rb",
|
24
|
+
"spec/support/db.rb",
|
25
|
+
"spec/unique_model_spec.rb"
|
26
|
+
]
|
27
|
+
s.homepage = %q{http://github.com/loco2/is_unique}
|
28
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
s.rubygems_version = %q{1.3.6}
|
31
|
+
s.summary = %q{ActiveRecord extension to prevent duplicate records}
|
32
|
+
s.test_files = [
|
33
|
+
"spec/unique_model_spec.rb",
|
34
|
+
"spec/support/db.rb",
|
35
|
+
"spec/spec_helper.rb"
|
36
|
+
]
|
37
|
+
|
38
|
+
if s.respond_to? :specification_version then
|
39
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
40
|
+
s.specification_version = 3
|
41
|
+
|
42
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
43
|
+
s.add_runtime_dependency(%q<activerecord>, ["= 2.3.5"])
|
44
|
+
else
|
45
|
+
s.add_dependency(%q<activerecord>, ["= 2.3.5"])
|
46
|
+
end
|
47
|
+
else
|
48
|
+
s.add_dependency(%q<activerecord>, ["= 2.3.5"])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
data/lib/is_unique.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module IsUnique
|
2
|
+
def self.included(base)
|
3
|
+
base.extend ClassMethods
|
4
|
+
end
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def is_unique(options = {})
|
8
|
+
|
9
|
+
write_inheritable_attribute :is_unique_hash_column, options[:hash_column] || 'unique_hash'
|
10
|
+
class_inheritable_reader :is_unique_hash_column
|
11
|
+
|
12
|
+
write_inheritable_attribute :is_unique_ignore, [
|
13
|
+
primary_key,
|
14
|
+
is_unique_hash_column.to_s,
|
15
|
+
'created_at',
|
16
|
+
'updated_at'
|
17
|
+
].concat(Array(options[:ignore]).map(&:to_s))
|
18
|
+
|
19
|
+
self.class_eval do
|
20
|
+
include InstanceMethods
|
21
|
+
|
22
|
+
before_save :calculate_unique_hash
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module InstanceMethods
|
28
|
+
private
|
29
|
+
|
30
|
+
def is_unique_hash
|
31
|
+
send(self.class.is_unique_hash_column)
|
32
|
+
end
|
33
|
+
|
34
|
+
def is_unique_hash=(value)
|
35
|
+
send("#{self.class.is_unique_hash_column}=", value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def create
|
39
|
+
if existing = self.class.find(:first, :conditions => {self.class.is_unique_hash_column => is_unique_hash})
|
40
|
+
self.id = existing.id
|
41
|
+
@new_record = false
|
42
|
+
reload
|
43
|
+
id
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def calculate_unique_hash
|
50
|
+
self.is_unique_hash = unique_attributes.hash
|
51
|
+
end
|
52
|
+
|
53
|
+
def unique_attributes
|
54
|
+
attributes.except(*self.class.read_inheritable_attribute(:is_unique_ignore))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
ActiveRecord::Base.class_eval do
|
60
|
+
include IsUnique
|
61
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/support/db.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
2
|
+
|
3
|
+
def suppress_stdout
|
4
|
+
tmp = $stdout
|
5
|
+
$stdout = StringIO.new
|
6
|
+
yield
|
7
|
+
$stdout = tmp
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup_db
|
11
|
+
ActiveRecord::Schema.define(:version => 1) do
|
12
|
+
create_table :locations do |t|
|
13
|
+
t.string :name
|
14
|
+
t.float :lat, :lng
|
15
|
+
t.string :alias
|
16
|
+
t.integer :unique_hash
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown_db
|
23
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
24
|
+
ActiveRecord::Base.connection.drop_table(table)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Spec::Runner.configure do |config|
|
29
|
+
config.before(:all) { suppress_stdout { setup_db } }
|
30
|
+
config.after(:all) { suppress_stdout { teardown_db } }
|
31
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Location < ActiveRecord::Base
|
4
|
+
# String name
|
5
|
+
# Float lat
|
6
|
+
# Float lng
|
7
|
+
# String alias
|
8
|
+
# Integer unique_hash
|
9
|
+
|
10
|
+
is_unique :ignore => :alias
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "A unique model" do
|
14
|
+
before(:each) do
|
15
|
+
@it = Location.create(
|
16
|
+
:name => 'London',
|
17
|
+
:lat => '51.5084152563931',
|
18
|
+
:lng => '-0.125532746315002',
|
19
|
+
:alias => 'Londinium'
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should not create a new record with the same attributes" do
|
24
|
+
new_record = @it.clone
|
25
|
+
lambda { new_record.save! }.
|
26
|
+
should_not change(Location, :count)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not create a new record with a different ignored attribute" do
|
30
|
+
new_record = @it.clone
|
31
|
+
new_record.alias = 'Greater London'
|
32
|
+
lambda { new_record.save! }.
|
33
|
+
should_not change(Location, :count)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return the existing record on creation" do
|
37
|
+
new_record = @it.clone
|
38
|
+
new_record.save!
|
39
|
+
new_record.should == @it
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should allow to create a record with different attributes" do
|
43
|
+
new_record = @it.clone
|
44
|
+
new_record.name = 'Greater London'
|
45
|
+
lambda { new_record.save! }.
|
46
|
+
should change(Location, :count).by(1)
|
47
|
+
new_record.should_not == @it
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should allow to create a record with original attributes when an existing record is updated" do
|
51
|
+
new_record = @it.clone
|
52
|
+
@it.update_attribute(:name, 'Greater London')
|
53
|
+
lambda { new_record.save! }.
|
54
|
+
should change(Location, :count).by(1)
|
55
|
+
new_record.should_not == @it
|
56
|
+
end
|
57
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: is_unique
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Eugene Bolshakov
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-03-22 00:00:00 +03:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: activerecord
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 2
|
29
|
+
- 3
|
30
|
+
- 5
|
31
|
+
version: 2.3.5
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
description: Makes ActiveRecord return existing records instead of creating duplicates
|
35
|
+
email: eugene.bolshakov@gmail.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files: []
|
41
|
+
|
42
|
+
files:
|
43
|
+
- Rakefile
|
44
|
+
- VERSION
|
45
|
+
- generators/is_unique/is_unique_generator.rb
|
46
|
+
- generators/is_unique/templates/is_unique_migration.rb
|
47
|
+
- init.rb
|
48
|
+
- is_unique.gemspec
|
49
|
+
- lib/is_unique.rb
|
50
|
+
- spec/spec_helper.rb
|
51
|
+
- spec/support/db.rb
|
52
|
+
- spec/unique_model_spec.rb
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://github.com/loco2/is_unique
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options:
|
59
|
+
- --charset=UTF-8
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
version: "0"
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.3.6
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: ActiveRecord extension to prevent duplicate records
|
83
|
+
test_files:
|
84
|
+
- spec/unique_model_spec.rb
|
85
|
+
- spec/support/db.rb
|
86
|
+
- spec/spec_helper.rb
|