rescue_unique_constraint 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f0e3cb076ff7875dceb8444be36ef82080bcd05c
4
- data.tar.gz: 353306cab3df3aa689c7c74f4208c30d28c05c91
3
+ metadata.gz: 27efaf5971c6a6112e6bd8a425dce94b75c460be
4
+ data.tar.gz: 9ad629f199f45389fcc44e86ba4dd7a563d5393a
5
5
  SHA512:
6
- metadata.gz: 230a6f59b281782ace42043b97ccee346739b4db72bc42091a24017636491af825bbc1f2cc9aac05a2928193b69ae8dbe674c25598b9a84037ede76e4f1b206d
7
- data.tar.gz: e6326eb248afb2684f3a58f9246c56152a58478a497fda051fc491a948afde9fa4b46d38ae149610cd733636b3e0de39c7e59b26403bc5e5f7a6f5ae23c245d0
6
+ metadata.gz: c3889a2162aa6549ede31230f0d2d6fbda77a81ed59b59fcd1b220c0ecf17b682bdb850ac1a521fde94e17bc8b5e57f4fe0f16b437766983abe0b6497d52959e
7
+ data.tar.gz: 4bacd362cc9b17643ca4d181753b4cb666759b69e54dc9b82a6bb2110e10efa5bf7a593065071509e8902c2cedd43013f8be49cd9182e500dbcbd88fdc58b1b2
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  *.gem
2
2
  *.rbc
3
+ .tags
3
4
  .bundle
4
5
  .config
5
6
  .yardoc
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.1
data/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # RescueUniqueConstraint
2
2
 
3
- Rails doesn't do a great job of rescuing ActiveRecord::RecordNotUnique
4
- violations resulting from a duplicate entry on a unique constraint.
3
+ ActiveRecord doesn't do a great job of rescuing ActiveRecord::RecordNotUnique
4
+ violations resulting from a duplicate entry on a database level unique constraint.
5
5
 
6
6
  This gem automatically rescues the error and instead adds a validation error
7
7
  on the field in question, making it behave as if you had a normal uniqueness
8
8
  validation.
9
9
 
10
- Note that if you have only a unique constraint and no uniquness validation, it
10
+ Note that if you have only a unique constraint in the database and no uniqueness validation in ActiveRecord, it
11
11
  is possible for your object to validate but then fail to save.
12
12
 
13
13
  See Usage for more info.
@@ -55,6 +55,7 @@ enter your database.
55
55
  After:
56
56
 
57
57
  class Thing < ActiveRecord::Base
58
+ include RescueUniqueConstraint
58
59
  rescue_unique_constraint index: "my_unique_index", field: "somefield"
59
60
  end
60
61
 
@@ -0,0 +1,9 @@
1
+ module RescueUniqueConstraint
2
+ module Adapter
3
+ class PostgresqlAdapter
4
+ def index_error?(index, error_message)
5
+ error_message[/#{index.name}/]
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module RescueUniqueConstraint
2
+ module Adapter
3
+ class SqliteAdapter
4
+ def index_error?(index, error_message)
5
+ error_message[/UNIQUE.*#{index.field}/]
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module RescueUniqueConstraint
2
+ class Index
3
+ attr_reader :name, :field
4
+ def initialize(name, field)
5
+ @name = name
6
+ @field = field
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,42 @@
1
+ module RescueUniqueConstraint
2
+ # Handles storing and matching [index, field] pairs to exceptions
3
+ class RescueHandler
4
+ def initialize(model)
5
+ @model = model
6
+ @indexes_to_rescue_on = []
7
+ end
8
+
9
+ def add_index(index, field)
10
+ indexes_to_rescue_on << Index.new(index, field)
11
+ end
12
+
13
+ def matching_indexes(e)
14
+ indexes = indexes_to_rescue_on.select do |index|
15
+ database_adapter.index_error?(index, e.message)
16
+ end
17
+ raise e unless indexes.any?
18
+ indexes
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :indexes_to_rescue_on, :model
24
+
25
+ def database_adapter
26
+ @_database_adapter ||= (
27
+ case database_name
28
+ when :postgresql
29
+ Adapter::PostgresqlAdapter.new
30
+ when :sqlite
31
+ Adapter::SqliteAdapter.new
32
+ else
33
+ raise "Database (#{database_name}) not supported"
34
+ end
35
+ )
36
+ end
37
+
38
+ def database_name
39
+ model.connection.adapter_name.downcase.to_sym
40
+ end
41
+ end
42
+ end
@@ -1,3 +1,3 @@
1
1
  module RescueUniqueConstraint
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -1,34 +1,42 @@
1
- require "rescue_unique_constraint/version"
1
+ require 'rescue_unique_constraint/version'
2
+ require 'rescue_unique_constraint/index'
3
+ require 'rescue_unique_constraint/rescue_handler'
4
+ require 'rescue_unique_constraint/adapter/postgresql_adapter'
5
+ require 'rescue_unique_constraint/adapter/sqlite_adapter'
2
6
  require 'active_record'
3
7
 
8
+ # Module which will rescue ActiveRecord::RecordNotUnique exceptions
9
+ # and add errors for indexes that are registered with
10
+ # rescue_unique_constraint(index:, field:)
4
11
  module RescueUniqueConstraint
5
12
  def self.included(base)
6
13
  base.extend(ClassMethods)
7
14
  end
8
15
 
16
+ # methods mixed into ActiveRecord class
9
17
  module ClassMethods
18
+ def index_rescue_handler
19
+ @_index_rescue_handler ||= RescueUniqueConstraint::RescueHandler.new(self)
20
+ end
21
+
10
22
  def rescue_unique_constraint(index:, field:)
11
- define_method(:create_or_update_with_rescue) do
12
- begin
13
- create_or_update_without_rescue
14
- rescue ActiveRecord::RecordNotUnique => e
15
- case e.message
16
- when /#{index}/ # Postgres
17
- errors.add(field, :taken)
18
- when /UNIQUE.*#{field}/ # SQLite
19
- errors.add(field, :taken)
20
- else
21
- # This should not happen; we want to know if we forgot to handle some unique constraint
22
- raise e
23
+ unless method_defined?(:create_or_update_with_rescue)
24
+ define_method(:create_or_update_with_rescue) do
25
+ begin
26
+ create_or_update_without_rescue
27
+ rescue ActiveRecord::RecordNotUnique => e
28
+ self.class.index_rescue_handler.matching_indexes(e).each do |matching_index|
29
+ errors.add(matching_index.field, :taken)
30
+ end
31
+ return false
23
32
  end
24
- false
33
+ true
25
34
  end
26
- end
27
35
 
28
- alias_method :create_or_update_without_rescue, :create_or_update
29
- alias_method :create_or_update, :create_or_update_with_rescue
36
+ alias_method :create_or_update_without_rescue, :create_or_update
37
+ alias_method :create_or_update, :create_or_update_with_rescue
38
+ end
39
+ index_rescue_handler.add_index(index, field)
30
40
  end
31
41
  end
32
42
  end
33
-
34
- ActiveRecord::Base.include(RescueUniqueConstraint)
@@ -24,4 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "rake", "~> 10.5"
25
25
  spec.add_development_dependency "rspec", "~> 3.0"
26
26
  spec.add_development_dependency "sqlite3", "~> 1.3"
27
+ spec.add_development_dependency 'pry'
28
+ spec.add_development_dependency 'pry-byebug'
27
29
  end
@@ -8,20 +8,25 @@ describe RescueUniqueConstraint do
8
8
  ActiveRecord::Schema.define(:version => 1) do
9
9
  create_table :things do |t|
10
10
  t.string :name
11
+ t.string :test
11
12
  end
12
13
 
13
14
  add_index :things, :name, unique: true, name: "idx_things_on_name_unique"
15
+ add_index :things, :test, unique: true, name: "idx_things_on_test_unique"
14
16
  end
15
17
  end
16
18
 
17
19
  class Thing < ActiveRecord::Base
20
+ include RescueUniqueConstraint
18
21
  rescue_unique_constraint index: "idx_things_on_name_unique", field: "name"
22
+ rescue_unique_constraint index: "idx_things_on_test_unique", field: "test"
19
23
  end
20
24
 
21
25
  it "rescues unique constraint violations as activerecord errors" do
22
- thing = Thing.create(name: "foo")
23
- dupe = Thing.new(name: "foo")
26
+ thing = Thing.create(name: "foo", test: 'bar')
27
+ dupe = Thing.new(name: "foo", test: 'bar')
24
28
  expect(dupe.save).to eql false
25
29
  expect(dupe.errors[:name].first).to match /taken/
30
+ expect(dupe.errors[:test].first).to match /taken/
26
31
  end
27
32
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rescue_unique_constraint
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tam Dang
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-01-14 00:00:00.000000000 Z
12
+ date: 2017-01-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -81,6 +81,34 @@ dependencies:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
83
  version: '1.3'
84
+ - !ruby/object:Gem::Dependency
85
+ name: pry
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: pry-byebug
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
84
112
  description: Rescues unique constraint violations and turns them into ActiveRecord
85
113
  errors
86
114
  email:
@@ -91,11 +119,16 @@ extensions: []
91
119
  extra_rdoc_files: []
92
120
  files:
93
121
  - ".gitignore"
122
+ - ".rubocop.yml"
94
123
  - Gemfile
95
124
  - LICENSE.txt
96
125
  - README.md
97
126
  - Rakefile
98
127
  - lib/rescue_unique_constraint.rb
128
+ - lib/rescue_unique_constraint/adapter/postgresql_adapter.rb
129
+ - lib/rescue_unique_constraint/adapter/sqlite_adapter.rb
130
+ - lib/rescue_unique_constraint/index.rb
131
+ - lib/rescue_unique_constraint/rescue_handler.rb
99
132
  - lib/rescue_unique_constraint/version.rb
100
133
  - rescue_unique_constraint.gemspec
101
134
  - spec/rescue_unique_constraint_spec.rb
@@ -119,9 +152,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
152
  version: '0'
120
153
  requirements: []
121
154
  rubyforge_project:
122
- rubygems_version: 2.4.8
155
+ rubygems_version: 2.2.2
123
156
  signing_key:
124
157
  specification_version: 4
125
158
  summary: Turns ActiveRecord::RecordNotUnique errors into ActiveRecord errors
126
159
  test_files:
127
160
  - spec/rescue_unique_constraint_spec.rb
161
+ has_rdoc: