activerecord-rescue_from_duplicate 0.2.6 → 0.2.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e6132feb5c01f32d458d37eeccdc5551263280c72040d5718e10d53795d9c98
4
- data.tar.gz: 5f9e6a0f3a05e9ced301db7f25f51176292af0fa33f0c9fe3dc3b91f7bceaef3
3
+ metadata.gz: 00a9576198c85da3503fc44c7420dd287082a3a8219fe716cd88340ab8da432d
4
+ data.tar.gz: 196bb9b82c0d333e4fdbaf254992f9e2cf997d62d171420bff1859bdf4d1aca8
5
5
  SHA512:
6
- metadata.gz: '09bdf44118974354a00a74635d8b3b277f85bb9adf8d1fc48a186699962de82e834fef26ac5dc599a7b039c085eeb30815f900649e7ab0c5a33c4510fa3e5ede'
7
- data.tar.gz: 03bc4fe8d528ab693e0619114af9662fecc5dcf914ce41ca5b1c8c98613f9c893afea72e5f6b5de303c7ef50324c9c83da107f807a1d9b54f69a92cfbd96705b
6
+ metadata.gz: df70343a3219b0c4c2ce0497329d3f59ac3c1e60b6888fabb8b1b5a0562981d1a1b0a27ad1eb0f52e7b190dbfb7584cd6dbbcf72df9f4672b8d4de34f04807cd
7
+ data.tar.gz: fb404a3201ab1bc5739a4ac71686d7bb5002ce849e439c87063aa9f530f9390165e9044a549bcc733fcc15d7d004c4dd5b1386b1b8a203be4762c5f94bb03c9d
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: "/"
5
+ schedule:
6
+ interval: weekly
@@ -24,15 +24,16 @@ jobs:
24
24
  MYSQL_DATABASE: "rescue_from_duplicate"
25
25
  ports:
26
26
  - "3306:3306"
27
+ options: --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 10
27
28
  strategy:
28
29
  fail-fast: false
29
30
  matrix:
30
- ruby: ["3.2"]
31
+ ruby: ["3.0", "3.1", "3.2", "3.3"]
31
32
  steps:
32
33
  - name: Check out code
33
- uses: actions/checkout@v4
34
+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
34
35
  - name: Set up Ruby ${{ matrix.ruby }}
35
- uses: ruby/setup-ruby@v1
36
+ uses: ruby/setup-ruby@e34163cd15f4bb403dcd72d98e295997e6a55798 # v1.238.0
36
37
  with:
37
38
  ruby-version: ${{ matrix.ruby }}
38
39
  bundler-cache: true
@@ -0,0 +1,26 @@
1
+ name: Release
2
+
3
+ on: workflow_dispatch
4
+
5
+ jobs:
6
+ release:
7
+ permissions:
8
+ contents: write
9
+ id-token: write
10
+
11
+ environment: release
12
+
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
17
+ - name: Install required packages
18
+ run: |
19
+ sudo apt-get update
20
+ sudo apt-get -y install libmemcached-dev libmysqlclient-dev libpq-dev libsasl2-dev
21
+ - name: Set up Ruby
22
+ uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
23
+ with:
24
+ bundler-cache: true
25
+ ruby-version: 3.3
26
+ - uses: rubygems/release-gem@052cc82692552de3ef2b81fd670e41d13cba8092 # v1.4.0
data/.gitignore CHANGED
@@ -3,7 +3,6 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
- Gemfile.lock
7
6
  InstalledFiles
8
7
  _yardoc
9
8
  coverage
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.2.7] - 2026-08-26
11
+
12
+ ### Fixed
13
+
14
+ - It was possible that the gem wouldn't rescue a unique constraint violation error in
15
+ the even that a non unique index name would match a subset of the unique index
16
+ to rescue.
data/Gemfile.lock ADDED
@@ -0,0 +1,100 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ activerecord-rescue_from_duplicate (0.2.7)
5
+ activerecord (>= 7)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (7.1.5.2)
11
+ activesupport (= 7.1.5.2)
12
+ activerecord (7.1.5.2)
13
+ activemodel (= 7.1.5.2)
14
+ activesupport (= 7.1.5.2)
15
+ timeout (>= 0.4.0)
16
+ activesupport (7.1.5.2)
17
+ base64
18
+ benchmark (>= 0.3)
19
+ bigdecimal
20
+ concurrent-ruby (~> 1.0, >= 1.0.2)
21
+ connection_pool (>= 2.2.5)
22
+ drb
23
+ i18n (>= 1.6, < 2)
24
+ logger (>= 1.4.2)
25
+ minitest (>= 5.1)
26
+ mutex_m
27
+ securerandom (>= 0.3)
28
+ tzinfo (~> 2.0)
29
+ base64 (0.3.0)
30
+ benchmark (0.4.1)
31
+ bigdecimal (3.2.2)
32
+ coderay (1.1.3)
33
+ concurrent-ruby (1.3.5)
34
+ connection_pool (2.5.3)
35
+ diff-lcs (1.5.1)
36
+ docile (1.4.0)
37
+ drb (2.2.3)
38
+ i18n (1.14.7)
39
+ concurrent-ruby (~> 1.0)
40
+ logger (1.7.0)
41
+ method_source (1.0.0)
42
+ minitest (5.25.5)
43
+ mutex_m (0.3.0)
44
+ mysql2 (0.5.6)
45
+ pg (1.5.6)
46
+ pry (0.14.2)
47
+ coderay (~> 1.1)
48
+ method_source (~> 1.0)
49
+ rake (13.2.0)
50
+ rspec (3.13.0)
51
+ rspec-core (~> 3.13.0)
52
+ rspec-expectations (~> 3.13.0)
53
+ rspec-mocks (~> 3.13.0)
54
+ rspec-core (3.13.0)
55
+ rspec-support (~> 3.13.0)
56
+ rspec-expectations (3.13.0)
57
+ diff-lcs (>= 1.2.0, < 2.0)
58
+ rspec-support (~> 3.13.0)
59
+ rspec-mocks (3.13.0)
60
+ diff-lcs (>= 1.2.0, < 2.0)
61
+ rspec-support (~> 3.13.0)
62
+ rspec-support (3.13.1)
63
+ securerandom (0.3.2)
64
+ simplecov (0.22.0)
65
+ docile (~> 1.1)
66
+ simplecov-html (~> 0.11)
67
+ simplecov_json_formatter (~> 0.1)
68
+ simplecov-html (0.12.3)
69
+ simplecov_json_formatter (0.1.4)
70
+ sqlite3 (1.7.3-aarch64-linux)
71
+ sqlite3 (1.7.3-arm-linux)
72
+ sqlite3 (1.7.3-arm64-darwin)
73
+ sqlite3 (1.7.3-x86-linux)
74
+ sqlite3 (1.7.3-x86_64-darwin)
75
+ sqlite3 (1.7.3-x86_64-linux)
76
+ timeout (0.4.3)
77
+ tzinfo (2.0.6)
78
+ concurrent-ruby (~> 1.0)
79
+
80
+ PLATFORMS
81
+ aarch64-linux
82
+ arm-linux
83
+ arm64-darwin
84
+ x86-linux
85
+ x86_64-darwin
86
+ x86_64-linux
87
+
88
+ DEPENDENCIES
89
+ activerecord-rescue_from_duplicate!
90
+ bundler
91
+ mysql2
92
+ pg
93
+ pry
94
+ rake
95
+ rspec
96
+ simplecov
97
+ sqlite3
98
+
99
+ BUNDLED WITH
100
+ 2.5.3
@@ -13,6 +13,8 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = "https://github.com/Shopify/activerecord-rescue_from_duplicate"
14
14
  spec.license = "MIT"
15
15
 
16
+ spec.required_ruby_version = ">= 3.0"
17
+
16
18
  spec.files = `git ls-files`.split($/)
17
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
@@ -2,6 +2,8 @@ require 'active_support/core_ext/class'
2
2
 
3
3
  module RescueFromDuplicate::ActiveRecord
4
4
  module Extension
5
+ DUPLICATE_KEY_REGEX = /for key ['"](?<key>[^'"]+)['"]/
6
+
5
7
  extend ActiveSupport::Concern
6
8
 
7
9
  module ClassMethods
@@ -80,8 +82,15 @@ module RescueFromDuplicate::ActiveRecord
80
82
  end
81
83
 
82
84
  def other_exception_columns(exception)
85
+ key = exception.message[DUPLICATE_KEY_REGEX, :key]
86
+ return [] unless key
87
+
88
+ # Mysql > 8.0 prefixes indexes with the table name.
89
+ index_name = key.delete_prefix("#{self.class.table_name}.")
90
+
83
91
  indexes = self.class.connection.schema_cache.indexes(self.class.table_name)
84
- indexes.detect{ |i| exception.message.include?(i.name) }.try(:columns) || []
92
+ index = indexes.find { |i| i.name == index_name }
93
+ index ? index.columns : []
85
94
  end
86
95
  end
87
96
  end
@@ -1,5 +1,5 @@
1
1
  module Activerecord
2
2
  module RescueFromDuplicate
3
- VERSION = '0.2.6'
3
+ VERSION = '0.2.7'
4
4
  end
5
5
  end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ module RescueFromDuplicate
4
+ describe "#other_exception_columns (MySQL/MariaDB duplicate-key parsing)" do
5
+ let(:prefix_index) do
6
+ ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(
7
+ "order_transactions", "index_order_transactions_on_shop_id", false, ["shop_id"]
8
+ )
9
+ end
10
+ let(:unique_index) do
11
+ ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(
12
+ "order_transactions", "index_order_transactions_on_shop_id_and_unique_token", true, ["shop_id", "unique_token"]
13
+ )
14
+ end
15
+
16
+ let(:model) do
17
+ indexes = [prefix_index, unique_index]
18
+ connection = double(schema_cache: double(indexes: indexes))
19
+ Class.new do
20
+ include RescueFromDuplicate::ActiveRecord::Extension
21
+ define_singleton_method(:table_name) { "order_transactions" }
22
+ define_singleton_method(:connection) { connection }
23
+ end
24
+ end
25
+
26
+ subject(:columns) { model.allocate.send(:other_exception_columns, exception) }
27
+
28
+ context "with a MySQL 8.0+ table-qualified key name" do
29
+ let(:exception) do
30
+ ::ActiveRecord::RecordNotUnique.new(
31
+ "Duplicate entry '1-abc' for key 'order_transactions.index_order_transactions_on_shop_id_and_unique_token'"
32
+ )
33
+ end
34
+
35
+ it "selects the violated index regardless of ordering" do
36
+ expect(columns).to eq ["shop_id", "unique_token"]
37
+ end
38
+ end
39
+
40
+ context "with a MySQL 5.7 / MariaDB bare key name" do
41
+ let(:exception) do
42
+ ::ActiveRecord::RecordNotUnique.new(
43
+ "Duplicate entry '1-abc' for key 'index_order_transactions_on_shop_id_and_unique_token'"
44
+ )
45
+ end
46
+
47
+ it "selects the violated index by exact name" do
48
+ expect(columns).to eq ["shop_id", "unique_token"]
49
+ end
50
+ end
51
+
52
+ context "when the message carries no key name" do
53
+ let(:exception) { ::ActiveRecord::RecordNotUnique.new("some unrelated error") }
54
+
55
+ it "returns no columns" do
56
+ expect(columns).to eq []
57
+ end
58
+ end
59
+ end
60
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-rescue_from_duplicate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guillaume Malette
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-14 00:00:00.000000000 Z
11
+ date: 2026-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -143,11 +143,16 @@ executables: []
143
143
  extensions: []
144
144
  extra_rdoc_files: []
145
145
  files:
146
+ - ".github/dependabot.yml"
146
147
  - ".github/workflows/ci.yml"
147
148
  - ".github/workflows/cla.yml"
149
+ - ".github/workflows/release.yml"
148
150
  - ".gitignore"
149
151
  - ".rspec"
152
+ - ".ruby-version"
153
+ - CHANGELOG.md
150
154
  - Gemfile
155
+ - Gemfile.lock
151
156
  - LICENSE.txt
152
157
  - README.md
153
158
  - Rakefile
@@ -161,6 +166,7 @@ files:
161
166
  - lib/rescue_from_duplicate/rescuer.rb
162
167
  - lib/rescue_from_duplicate/uniqueness_rescuer.rb
163
168
  - shipit.rubygems.yml
169
+ - spec/other_exception_columns_spec.rb
164
170
  - spec/rescue_from_duplicate_spec.rb
165
171
  - spec/rescuer_spec.rb
166
172
  - spec/spec_helper.rb
@@ -172,7 +178,7 @@ licenses:
172
178
  - MIT
173
179
  metadata:
174
180
  allowed_push_host: https://rubygems.org
175
- post_install_message:
181
+ post_install_message:
176
182
  rdoc_options: []
177
183
  require_paths:
178
184
  - lib
@@ -180,19 +186,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
180
186
  requirements:
181
187
  - - ">="
182
188
  - !ruby/object:Gem::Version
183
- version: '0'
189
+ version: '3.0'
184
190
  required_rubygems_version: !ruby/object:Gem::Requirement
185
191
  requirements:
186
192
  - - ">="
187
193
  - !ruby/object:Gem::Version
188
194
  version: '0'
189
195
  requirements: []
190
- rubygems_version: 3.4.22
191
- signing_key:
196
+ rubygems_version: 3.5.22
197
+ signing_key:
192
198
  specification_version: 4
193
199
  summary: Rescue from MySQL and Sqlite duplicate errors when trying to insert records
194
200
  that fail uniqueness validation
195
201
  test_files:
202
+ - spec/other_exception_columns_spec.rb
196
203
  - spec/rescue_from_duplicate_spec.rb
197
204
  - spec/rescuer_spec.rb
198
205
  - spec/spec_helper.rb