proper_active_record_uniqueness_validations 0.1.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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/README.rdoc +51 -0
  4. data/lib/proper_uniqueness_validation.rb +37 -0
  5. data/proper_active_record_uniqueness_validations.gemspec +21 -0
  6. data/test/Gemfile +28 -0
  7. data/test/Gemfile.lock +102 -0
  8. data/test/README.rdoc +261 -0
  9. data/test/Rakefile +7 -0
  10. data/test/app/models/.gitkeep +0 -0
  11. data/test/app/models/thing.rb +8 -0
  12. data/test/app/models/widget.rb +6 -0
  13. data/test/config.ru +4 -0
  14. data/test/config/application.rb +68 -0
  15. data/test/config/boot.rb +6 -0
  16. data/test/config/database.yml +55 -0
  17. data/test/config/environment.rb +5 -0
  18. data/test/config/environments/development.rb +32 -0
  19. data/test/config/environments/production.rb +54 -0
  20. data/test/config/environments/test.rb +37 -0
  21. data/test/config/initializers/backtrace_silencers.rb +7 -0
  22. data/test/config/initializers/inflections.rb +15 -0
  23. data/test/config/initializers/mime_types.rb +5 -0
  24. data/test/config/initializers/secret_token.rb +7 -0
  25. data/test/config/initializers/session_store.rb +8 -0
  26. data/test/config/initializers/wrap_parameters.rb +14 -0
  27. data/test/config/locales/en.yml +5 -0
  28. data/test/config/routes.rb +61 -0
  29. data/test/db/migrate/20130608153311_create_things.rb +16 -0
  30. data/test/db/migrate/20130608160945_create_widgets.rb +11 -0
  31. data/test/db/schema.rb +37 -0
  32. data/test/db/seeds.rb +7 -0
  33. data/test/log/.gitkeep +0 -0
  34. data/test/script/rails +6 -0
  35. data/test/spec/models/thing_spec.rb +250 -0
  36. data/test/spec/models/widget_spec.rb +20 -0
  37. data/test/spec/spec_helper.rb +46 -0
  38. metadata +110 -0
@@ -0,0 +1,11 @@
1
+ class CreateWidgets < ActiveRecord::Migration
2
+ def change
3
+ create_table :widgets do |t|
4
+ t.integer :some_id
5
+ t.string :name
6
+ t.string :description
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
data/test/db/schema.rb ADDED
@@ -0,0 +1,37 @@
1
+ # encoding: UTF-8
2
+ # This file is auto-generated from the current state of the database. Instead
3
+ # of editing this file, please use the migrations feature of Active Record to
4
+ # incrementally modify your database, and then regenerate this schema definition.
5
+ #
6
+ # Note that this schema.rb definition is the authoritative source for your
7
+ # database schema. If you need to create the application database on another
8
+ # system, you should be using db:schema:load, not running all the migrations
9
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
11
+ #
12
+ # It's strongly recommended to check this file into your version control system.
13
+
14
+ ActiveRecord::Schema.define(:version => 20130608160945) do
15
+
16
+ create_table "things", :force => true do |t|
17
+ t.integer "some_id"
18
+ t.text "name"
19
+ t.text "description"
20
+ t.text "unregistered"
21
+ t.datetime "created_at", :null => false
22
+ t.datetime "updated_at", :null => false
23
+ end
24
+
25
+ add_index "things", ["name", "description"], :name => "index_things_on_name_and_description", :unique => true
26
+ add_index "things", ["some_id"], :name => "index_things_on_some_id", :unique => true
27
+ add_index "things", ["unregistered"], :name => "index_things_on_unregistered", :unique => true
28
+
29
+ create_table "widgets", :force => true do |t|
30
+ t.integer "some_id"
31
+ t.string "name"
32
+ t.string "description"
33
+ t.datetime "created_at", :null => false
34
+ t.datetime "updated_at", :null => false
35
+ end
36
+
37
+ end
data/test/db/seeds.rb ADDED
@@ -0,0 +1,7 @@
1
+ # This file should contain all the record creation needed to seed the database with its default values.
2
+ # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3
+ #
4
+ # Examples:
5
+ #
6
+ # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
7
+ # Mayor.create(:name => 'Emanuel', :city => cities.first)
data/test/log/.gitkeep ADDED
File without changes
data/test/script/rails ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,250 @@
1
+ require 'spec_helper'
2
+
3
+ describe Thing do
4
+ let( :a_valid_thing ) do
5
+ Thing.new( :some_id => 1,
6
+ :name => "foo",
7
+ :description => "bar",
8
+ :unregistered => "fubar" )
9
+ end
10
+
11
+ let( :another_thing ) do
12
+ Thing.new( :some_id => 10,
13
+ :name => "foo_2000",
14
+ :description => "binford_9000",
15
+ :unregistered => "snafu" )
16
+ end
17
+
18
+ let( :a_widget_with_a_uniqueness_violation ) do
19
+ Widget.create( :some_id => 1, :name => "foo", :description => "bar" )
20
+ w = Widget.new( :some_id => 1, :name => "baz", :description => "quux" )
21
+ w.save
22
+ w
23
+ end
24
+
25
+ let( :a_widget_with_a_multi_column_uniqueness_violation ) do
26
+ Widget.create( :some_id => 1, :name => "foo", :description => "bar" )
27
+ w = Widget.new( :some_id => 2, :name => "foo", :description => "bar" )
28
+ w.save
29
+ w
30
+ end
31
+
32
+ describe "creating with #save" do
33
+ context "without violating a uniqueness constraint" do
34
+ subject { a_valid_thing }
35
+
36
+ it "should not raise an exception" do
37
+ expect { subject.save }.to_not raise_error
38
+ end
39
+
40
+ it "should return true" do
41
+ subject.save.should be_true
42
+ end
43
+
44
+ it "should have stored the value in the database" do
45
+ expect { subject.save }.to change { Thing.count }.by(1)
46
+ end
47
+
48
+ it "should not have inserted anything to the errors" do
49
+ subject.save
50
+ subject.errors.should be_empty
51
+ end
52
+ end
53
+
54
+ context "when violating an unregistered index" do
55
+ subject do
56
+ another_thing.unregistered = a_valid_thing.unregistered
57
+ another_thing
58
+ end
59
+
60
+ before( :each ) do
61
+ a_valid_thing.save
62
+ end
63
+
64
+ it "should raise an exception" do
65
+ expect { subject.save }.to raise_error( ActiveRecord::RecordNotUnique )
66
+ end
67
+
68
+ it "should not have stored the value in the database" do
69
+ expect do
70
+ subject.save rescue nil
71
+ end.to_not change { Thing.count }
72
+ end
73
+ end
74
+
75
+ context "when violating a single column uniqueness constraint" do
76
+ subject do
77
+ another_thing.some_id = a_valid_thing.some_id
78
+ another_thing
79
+ end
80
+
81
+ before( :each ) do
82
+ a_valid_thing.save
83
+ end
84
+
85
+ it "should not raise an exception" do
86
+ expect { subject.save }.to_not raise_error
87
+ end
88
+
89
+ it "should return false" do
90
+ subject.save.should be_false
91
+ end
92
+
93
+ it "should not have stored the value in the database" do
94
+ expect { subject.save }.to_not change { Thing.count }
95
+ end
96
+
97
+ it "should have added an errors entry for the attribute" do
98
+ subject.save
99
+ subject.errors.messages.should eq( a_widget_with_a_uniqueness_violation.errors.messages )
100
+ end
101
+ end
102
+
103
+ context "when violating a multi-column uniqueness constraint" do
104
+ subject do
105
+ another_thing.name = a_valid_thing.name
106
+ another_thing.description = a_valid_thing.description
107
+ another_thing
108
+ end
109
+
110
+ before( :each ) do
111
+ a_valid_thing.save
112
+ end
113
+
114
+ it "should not raise an exception" do
115
+ expect { subject.save }.to_not raise_error
116
+ end
117
+
118
+ it "should return false" do
119
+ subject.save.should be_false
120
+ end
121
+
122
+ it "should not have stored the value in the database" do
123
+ expect { subject.save }.to_not change { Thing.count }
124
+ end
125
+
126
+ it "should have added an errors entry for the scoped attributes" do
127
+ subject.save
128
+ subject.errors.messages.should eq( a_widget_with_a_multi_column_uniqueness_violation.errors.messages )
129
+ end
130
+ end
131
+ end
132
+
133
+ describe "updating with #save" do
134
+ context "without violating a uniqueness constraint" do
135
+ subject { a_valid_thing }
136
+
137
+ before( :each ) do
138
+ subject.save
139
+ subject.some_id = 3
140
+ end
141
+
142
+ it "should not raise an exception" do
143
+ expect { subject.save }.to_not raise_error
144
+ end
145
+
146
+ it "should return true" do
147
+ subject.save.should be_true
148
+ end
149
+
150
+ it "should have stored the value in the database" do
151
+ expect do
152
+ subject.save
153
+ subject.reload
154
+ end.to change { subject.updated_at }
155
+ end
156
+
157
+ it "should not have inserted anything to the errors" do
158
+ subject.save
159
+ subject.errors.should be_empty
160
+ end
161
+ end
162
+
163
+ context "when violating an unregistered index" do
164
+ subject { another_thing }
165
+
166
+ before( :each ) do
167
+ a_valid_thing.save
168
+ subject.save
169
+ subject.reload # grab new updated_at
170
+ subject.unregistered = a_valid_thing.unregistered
171
+ end
172
+
173
+ it "should raise an exception" do
174
+ expect { subject.save }.to raise_error( ActiveRecord::RecordNotUnique )
175
+ end
176
+
177
+ it "should not have stored the value in the database" do
178
+ expect do
179
+ subject.save rescue nil
180
+ subject.reload
181
+ end.to_not change { subject.updated_at }
182
+ end
183
+ end
184
+
185
+ context "when violating a single column uniqueness constraint" do
186
+ subject { another_thing }
187
+
188
+ before( :each ) do
189
+ a_valid_thing.save
190
+ subject.save
191
+ subject.reload # grab new updated_at
192
+ subject.some_id = a_valid_thing.some_id
193
+ end
194
+
195
+ it "should not raise an exception" do
196
+ expect { subject.save }.to_not raise_error
197
+ end
198
+
199
+ it "should return false" do
200
+ subject.save.should be_false
201
+ end
202
+
203
+ it "should not have stored the value in the database" do
204
+ expect do
205
+ subject.save
206
+ subject.reload
207
+ end.to_not change { subject.updated_at }
208
+ end
209
+
210
+ it "should have added an errors entry for the attribute" do
211
+ subject.save
212
+ subject.errors.messages.should eq( a_widget_with_a_uniqueness_violation.errors.messages )
213
+ end
214
+ end
215
+
216
+ context "when violating a multi-column uniqueness constraint" do
217
+ subject { another_thing }
218
+
219
+ before( :each ) do
220
+ a_valid_thing.save
221
+ subject.save
222
+ subject.reload # grab new updated_at
223
+ subject.name = a_valid_thing.name
224
+ subject.description = a_valid_thing.description
225
+ end
226
+
227
+ it "should not raise an exception" do
228
+ expect { subject.save }.to_not raise_error
229
+ end
230
+
231
+ it "should return false" do
232
+ subject.save.should be_false
233
+ end
234
+
235
+ it "should not have stored the value in the database" do
236
+ expect do
237
+ subject.save
238
+ subject.reload
239
+ end.to_not change { subject.updated_at }
240
+ end
241
+
242
+ it "should have added an errors entry for the scoped attributes" do
243
+ subject.save
244
+ subject.errors.messages.should eq( a_widget_with_a_multi_column_uniqueness_violation.errors.messages )
245
+ end
246
+ end
247
+
248
+ end
249
+
250
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Widget do
4
+ describe "the validations" do
5
+ it "should validate the uniqueness of some_id" do
6
+ Widget.create( :some_id => 1, :name => "foo", :description => "bar" )
7
+
8
+ another_widget = Widget.new( :some_id => 1, :name => "baz", :description => "quux" )
9
+ another_widget.should_not be_valid
10
+ another_widget.should have(1).error_on( :some_id )
11
+ end
12
+
13
+ it "should validate the uniqueness of the (name, description) tuple" do
14
+ Widget.create( :some_id => 1, :name => "foo", :description => "bar" )
15
+ another_widget = Widget.new( :some_id => 2, :name => "foo", :description => "bar" )
16
+ another_widget.should_not be_valid
17
+ another_widget.should have(1).error_on( :name )
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,46 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+ require File.expand_path("../../config/environment", __FILE__)
4
+ require 'rspec/rails'
5
+ require 'database_cleaner'
6
+
7
+ DatabaseCleaner.strategy = :deletion
8
+
9
+ require 'rspec/autorun'
10
+
11
+ # Requires supporting ruby files with custom matchers and macros, etc,
12
+ # in spec/support/ and its subdirectories.
13
+ Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
14
+
15
+ RSpec.configure do |config|
16
+ # ## Mock Framework
17
+ #
18
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
19
+ #
20
+ # config.mock_with :mocha
21
+ # config.mock_with :flexmock
22
+ # config.mock_with :rr
23
+
24
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
25
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
26
+
27
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
28
+ # examples within a transaction, remove the following line or assign false
29
+ # instead of true.
30
+ config.use_transactional_fixtures = false
31
+
32
+ # If true, the base class of anonymous controllers will be inferred
33
+ # automatically. This will be the default behavior in future versions of
34
+ # rspec-rails.
35
+ config.infer_base_class_for_anonymous_controllers = false
36
+
37
+ # Run specs in random order to surface order dependencies. If you find an
38
+ # order dependency and want to debug it, you can fix the order by providing
39
+ # the seed, which is printed after each run.
40
+ # --seed 1234
41
+ config.order = "random"
42
+
43
+ config.after( :each ) do
44
+ DatabaseCleaner.clean
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: proper_active_record_uniqueness_validations
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Sven Riedel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.13
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.13
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.14.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 0.14.1
41
+ description: ActiveRecord uniqueness validations without raceconditions
42
+ email: sr@gimp.org
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files:
46
+ - README.rdoc
47
+ files:
48
+ - .gitignore
49
+ - README.rdoc
50
+ - lib/proper_uniqueness_validation.rb
51
+ - proper_active_record_uniqueness_validations.gemspec
52
+ - test/Gemfile
53
+ - test/Gemfile.lock
54
+ - test/README.rdoc
55
+ - test/Rakefile
56
+ - test/app/models/.gitkeep
57
+ - test/app/models/thing.rb
58
+ - test/app/models/widget.rb
59
+ - test/config.ru
60
+ - test/config/application.rb
61
+ - test/config/boot.rb
62
+ - test/config/database.yml
63
+ - test/config/environment.rb
64
+ - test/config/environments/development.rb
65
+ - test/config/environments/production.rb
66
+ - test/config/environments/test.rb
67
+ - test/config/initializers/backtrace_silencers.rb
68
+ - test/config/initializers/inflections.rb
69
+ - test/config/initializers/mime_types.rb
70
+ - test/config/initializers/secret_token.rb
71
+ - test/config/initializers/session_store.rb
72
+ - test/config/initializers/wrap_parameters.rb
73
+ - test/config/locales/en.yml
74
+ - test/config/routes.rb
75
+ - test/db/migrate/20130608153311_create_things.rb
76
+ - test/db/migrate/20130608160945_create_widgets.rb
77
+ - test/db/schema.rb
78
+ - test/db/seeds.rb
79
+ - test/log/.gitkeep
80
+ - test/script/rails
81
+ - test/spec/models/thing_spec.rb
82
+ - test/spec/models/widget_spec.rb
83
+ - test/spec/spec_helper.rb
84
+ homepage: http://github.com/sriedel/proper_active_record_uniqueness_validations
85
+ licenses: []
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options:
89
+ - --charset=UTF-8
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: 1.9.3
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.0.0
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Uses unique indexes on the database level to see and deal with uniqueness
108
+ constraint violations. No raceconditions and less database calls required to save
109
+ or update a record.
110
+ test_files: []