active_record_upsert 0.9.5 → 0.10.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 70b72c5ef238f1ffe3f7cfd5e2cc53f3b4c9e31f
4
- data.tar.gz: '09bfa1ad9bf6e4ea635371382d1b8e02f5fe8fb0'
2
+ SHA256:
3
+ metadata.gz: d08fb14b2c2b66a287518710472aba53e095a4cc54c581a5c1dcf5ecd86088f6
4
+ data.tar.gz: 02b6d45767b9ecfeb8bcd1b27dcef517470d7f7747f522d3c14db445725ab3fc
5
5
  SHA512:
6
- metadata.gz: 2e75ee846435aeb19b077eb34d844345d86d04374b9bdfb09d5311b3fea00c029dc8acd590b61541186d38efa907166df01faaeffcf9bae022e6116ef3baa405
7
- data.tar.gz: 2b2b56642038a8c350bfd6d7cc8a6d5bd1349e353b69b206509ffe68b1e716b95e23e2c71a4c5f2ccdda13b541f1152d69167da1bd6ba07f9fd054daee936430
6
+ metadata.gz: fefab82228fbfc68fbdd96912617ec7bdea1547be32b02800d46d2507bc0ba62c78001c21bf1cccb8b689ef1d5426432b8d49d40379dac8a044a326bcc158c9e
7
+ data.tar.gz: 249d793b70c0855b907a1036ca887a7c0c4b1ab68ee28a276d22b14bbdabd1ee6cf3973b11f6f818d1507fb0df7bf27475aca0c4d1e2e1f0e613be53ab85ecf0
@@ -5,7 +5,7 @@ gemspec
5
5
  group :development, :test do
6
6
  gem 'bundler', '>= 1.13'
7
7
  gem 'database_cleaner', '~> 1.6'
8
- gem 'pg', '~> 0.18'
8
+ gem 'pg', '~> 1.1'
9
9
  gem 'pry', '> 0'
10
10
  gem 'rake', '>= 10.0'
11
11
  gem 'rspec', '>= 3.0', '< 4'
@@ -0,0 +1,5 @@
1
+ group :development, :test do
2
+ gem 'rails', '>= 6.1.0-rc1', '< 6.2'
3
+ end
4
+
5
+ eval_gemfile "#{__dir__}/Gemfile.base"
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # ActiveRecordUpsert
6
6
 
7
- Real upsert for PostgreSQL 9.5+ and Rails 5 / ActiveRecord 5. Uses [ON CONFLICT DO UPDATE](http://www.postgresql.org/docs/9.5/static/sql-insert.html).
7
+ Real upsert for PostgreSQL 9.5+ and Rails 5+ / ActiveRecord 5+. Uses [ON CONFLICT DO UPDATE](http://www.postgresql.org/docs/9.5/static/sql-insert.html).
8
8
 
9
9
  ## Main points
10
10
 
@@ -15,7 +15,7 @@ Real upsert for PostgreSQL 9.5+ and Rails 5 / ActiveRecord 5. Uses [ON CONFLICT
15
15
  ## Prerequisites
16
16
 
17
17
  - PostgreSQL 9.5+ (that's when UPSERT support was added; see Wikipedia's [PostgreSQL Release History](https://en.wikipedia.org/wiki/PostgreSQL#Release_history))
18
- - ActiveRecord ~> 5
18
+ - ActiveRecord >= 5
19
19
  - For MRI: pg
20
20
 
21
21
  - For JRuby: No support
@@ -123,6 +123,37 @@ r = MyRecord.new(id: 1, name: 'bar')
123
123
  r.upsert!
124
124
  ```
125
125
 
126
+ ### Gotcha with database defaults
127
+
128
+ When a table is defined with a database default for a field, this gotcha can occur when trying to explicitly upsert a record _to_ the default value (from a non-default value).
129
+
130
+ **Example**: a table called `hardwares` has a `prio` column with a default value.
131
+
132
+ ┌─────────┬─────────┬───────┬
133
+ │ Column │ Type │Default│
134
+ ├─────────┼─────────┼───────┼
135
+ │ id │ integer │ ...
136
+ │ prio │ integer │ 999
137
+
138
+ And `hardwares` has a record with a non-default value for `prio`. Say, the record with `id` 1 has a `prio` of `998`.
139
+
140
+ In this situation, upserting like:
141
+
142
+ ```ruby
143
+ hw = { id: 1, prio: 999 }
144
+ Hardware.new(prio: hw[:prio]).upsert
145
+ ```
146
+
147
+ will not mention the `prio` column in the `ON CONFLICT` clause, resulting in no update.
148
+
149
+ However, upserting like so:
150
+
151
+ ```ruby
152
+ Hardware.upsert(prio: hw[:prio]).id
153
+ ```
154
+
155
+ will indeed update the record in the database back to its default value, `999`.
156
+
126
157
  ### Conflict Clauses
127
158
 
128
159
  It's possible to specify which columns should be used for the conflict clause. **These must comprise a unique index in Postgres.**
@@ -160,6 +191,24 @@ class Account < ApplicationRecord
160
191
  end
161
192
  ```
162
193
 
194
+ Overriding the models' `upsert_keys` when calling `#upsert` or `.upsert`:
195
+
196
+ ```ruby
197
+ Account.upsert(attrs, opts: { upsert_keys: [:foo, :bar] })
198
+ # Or, on an instance:
199
+ account = Account.new(attrs)
200
+ account.upsert(opts: { upsert_keys: [:foo, :bar] })
201
+ ```
202
+
203
+ Overriding the models' `upsert_options` (partial index) when calling `#upsert` or `.upsert`:
204
+
205
+ ```ruby
206
+ Account.upsert(attrs, opts: { upsert_options: { where: 'foo IS NOT NULL' } })
207
+ # Or, on an instance:
208
+ account = Account.new(attrs)
209
+ account.upsert(opts: { upsert_options: { where: 'foo IS NOT NULLL } })
210
+ ```
211
+
163
212
  ## Tests
164
213
 
165
214
  Make sure to have an upsert_test database:
@@ -190,3 +239,5 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/jesjos
190
239
  - Daniel Cooper ([@danielcooper](https://github.com/danielcooper))
191
240
  - Laurent Vallar ([@val](https://github.com/val))
192
241
  - Emmanuel Quentin ([@manuquentin](https://github.com/manuquentin))
242
+ - Jeff Wallace ([@tjwallace](https://github.com/tjwallace))
243
+ - Kirill Zaitsev ([@Bugagazavr](https://github.com/Bugagazavr))
@@ -21,6 +21,6 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.platform = Gem::Platform::RUBY
23
23
 
24
- spec.add_runtime_dependency 'activerecord', '>= 5.0', '< 6.1'
24
+ spec.add_runtime_dependency 'activerecord', '>= 5.0', '< 6.2'
25
25
  spec.add_runtime_dependency 'pg', '>= 0.18', '< 2.0'
26
26
  end
@@ -13,7 +13,7 @@ require 'active_record_upsert/active_record'
13
13
 
14
14
  version = defined?(Rails) ? Rails.version : ActiveRecord.version.to_s
15
15
 
16
- require 'active_record_upsert/compatibility/rails60.rb' if version >= '6.0.0' && version < '6.1.0'
16
+ require 'active_record_upsert/compatibility/rails60.rb' if version >= '6.0.0' && version < '6.2.0'
17
17
  require 'active_record_upsert/compatibility/rails51.rb' if version >= '5.1.0' && version < '5.2.0'
18
18
  require 'active_record_upsert/compatibility/rails50.rb' if version >= '5.0.0' && version < '5.1.0'
19
19
 
@@ -1,7 +1,7 @@
1
1
  module ActiveRecordUpsert
2
2
  module ActiveRecord
3
3
  module PersistenceExtensions
4
- def upsert!(attributes: nil, arel_condition: nil, validate: true)
4
+ def upsert!(attributes: nil, arel_condition: nil, validate: true, opts: {})
5
5
  raise ::ActiveRecord::ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
6
6
  raise ::ActiveRecord::RecordSavedError, "Can't upsert a record that has already been saved" if persisted?
7
7
  validate == false || perform_validations || raise_validation_error
@@ -11,7 +11,7 @@ module ActiveRecordUpsert
11
11
  attributes = attributes +
12
12
  timestamp_attributes_for_create_in_model +
13
13
  timestamp_attributes_for_update_in_model
14
- _upsert_record(attributes.map(&:to_s).uniq, arel_condition)
14
+ _upsert_record(attributes.map(&:to_s).uniq, arel_condition, opts)
15
15
  }
16
16
  }
17
17
 
@@ -24,10 +24,10 @@ module ActiveRecordUpsert
24
24
  false
25
25
  end
26
26
 
27
- def _upsert_record(upsert_attribute_names = changed, arel_condition = nil)
27
+ def _upsert_record(upsert_attribute_names = changed, arel_condition = nil, opts = {})
28
28
  existing_attribute_names = attributes_for_create(attributes.keys)
29
29
  existing_attributes = attributes_with_values(existing_attribute_names)
30
- values = self.class._upsert_record(existing_attributes, upsert_attribute_names, [arel_condition].compact)
30
+ values = self.class._upsert_record(existing_attributes, upsert_attribute_names, [arel_condition].compact, opts)
31
31
  @attributes = self.class.attributes_builder.build_from_database(values.first.to_h)
32
32
  @new_record = false
33
33
  values
@@ -40,12 +40,12 @@ module ActiveRecordUpsert
40
40
  end
41
41
 
42
42
  module ClassMethods
43
- def upsert!(attributes, arel_condition: nil, validate: true, &block)
43
+ def upsert!(attributes, arel_condition: nil, validate: true, opts: {}, &block)
44
44
  if attributes.is_a?(Array)
45
45
  attributes.collect { |hash| upsert(hash, &block) }
46
46
  else
47
47
  new(attributes, &block).upsert!(
48
- attributes: attributes.keys, arel_condition: arel_condition, validate: validate
48
+ attributes: attributes.keys, arel_condition: arel_condition, validate: validate, opts: opts
49
49
  )
50
50
  end
51
51
  end
@@ -56,8 +56,9 @@ module ActiveRecordUpsert
56
56
  false
57
57
  end
58
58
 
59
- def _upsert_record(existing_attributes, upsert_attributes_names, wheres) # :nodoc:
60
- upsert_keys = self.upsert_keys || [primary_key]
59
+ def _upsert_record(existing_attributes, upsert_attributes_names, wheres, opts) # :nodoc:
60
+ upsert_keys = opts[:upsert_keys] || self.upsert_keys || [primary_key]
61
+ upsert_options = opts[:upsert_options] || self.upsert_options
61
62
  upsert_attributes_names = upsert_attributes_names - [*upsert_keys, 'created_at']
62
63
 
63
64
  existing_attributes = existing_attributes
@@ -1,10 +1,10 @@
1
1
  module ActiveRecordUpsert
2
2
  module ActiveRecord
3
3
  module PersistenceExtensions
4
- def _upsert_record(upsert_attribute_names = changed, arel_condition = nil)
4
+ def _upsert_record(upsert_attribute_names = changed, arel_condition = nil, opts = {})
5
5
  upsert_attribute_names = upsert_attribute_names.map { |name| _prepare_column(name) } & self.class.column_names
6
6
  existing_attributes = arel_attributes_with_values_for_create(self.class.column_names)
7
- values = self.class.unscoped.upsert(existing_attributes, upsert_attribute_names, [arel_condition].compact)
7
+ values = self.class.unscoped.upsert(existing_attributes, upsert_attribute_names, [arel_condition].compact, opts)
8
8
  @new_record = false
9
9
  @attributes = self.class.attributes_builder.build_from_database(values.first.to_h)
10
10
  values
@@ -20,9 +20,10 @@ module ActiveRecordUpsert
20
20
  end
21
21
 
22
22
  module RelationExtensions
23
- def upsert(existing_attributes, upsert_attributes, wheres) # :nodoc:
23
+ def upsert(existing_attributes, upsert_attributes, wheres, opts) # :nodoc:
24
24
  substitutes, binds = substitute_values(existing_attributes)
25
- upsert_keys = self.klass.upsert_keys || [primary_key]
25
+ upsert_keys = opts[:upsert_keys] || self.klass.upsert_keys || [primary_key]
26
+ upsert_options = opts[:upsert_options] || self.klass.upsert_options
26
27
 
27
28
  upsert_attributes = upsert_attributes - [*upsert_keys, 'created_at']
28
29
  upsert_keys_filter = ->(o) { upsert_attributes.include?(o.name) }
@@ -34,7 +35,7 @@ module ActiveRecordUpsert
34
35
 
35
36
  on_conflict_do_update = ::Arel::OnConflictDoUpdateManager.new
36
37
  on_conflict_do_update.target = target
37
- on_conflict_do_update.target_condition = self.klass.upsert_options[:where]
38
+ on_conflict_do_update.target_condition = upsert_options[:where]
38
39
  on_conflict_do_update.wheres = wheres
39
40
  on_conflict_do_update.set(vals_for_upsert)
40
41
 
@@ -43,7 +44,7 @@ module ActiveRecordUpsert
43
44
  insert_manager.on_conflict = on_conflict_do_update.to_node
44
45
  insert_manager.insert substitutes
45
46
 
46
- @klass.connection.upsert(insert_manager, "#{self} Upsert", binds + on_conflict_binds)
47
+ @klass.connection.upsert(insert_manager, "#{@klass.name} Upsert", binds + on_conflict_binds)
47
48
  end
48
49
 
49
50
  ::ActiveRecord::Relation.include(self)
@@ -5,5 +5,14 @@ module ActiveRecordUpsert
5
5
  with_transaction_returning_status { super }
6
6
  end
7
7
  end
8
+
9
+ module ConnectAdapterExtension
10
+ def upsert(*args)
11
+ ::ActiveRecord::Base.clear_query_caches_for_current_thread
12
+ super
13
+ end
14
+
15
+ ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(self)
16
+ end
8
17
  end
9
18
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordUpsert
2
- VERSION = "0.9.5"
2
+ VERSION = "0.10.0"
3
3
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_upsert
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jesper Josefsson
8
8
  - Olle Jonsson
9
- autorequire:
9
+ autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2019-04-26 00:00:00.000000000 Z
12
+ date: 2020-12-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -20,7 +20,7 @@ dependencies:
20
20
  version: '5.0'
21
21
  - - "<"
22
22
  - !ruby/object:Gem::Version
23
- version: '6.1'
23
+ version: '6.2'
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
@@ -30,7 +30,7 @@ dependencies:
30
30
  version: '5.0'
31
31
  - - "<"
32
32
  - !ruby/object:Gem::Version
33
- version: '6.1'
33
+ version: '6.2'
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: pg
36
36
  requirement: !ruby/object:Gem::Requirement
@@ -51,7 +51,7 @@ dependencies:
51
51
  - - "<"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '2.0'
54
- description:
54
+ description:
55
55
  email:
56
56
  - jesper.josefsson@gmail.com
57
57
  - olle.jonsson@gmail.com
@@ -64,6 +64,7 @@ files:
64
64
  - Gemfile.rails-5-1
65
65
  - Gemfile.rails-5-2
66
66
  - Gemfile.rails-6-0
67
+ - Gemfile.rails-6-1
67
68
  - Gemfile.rails-master
68
69
  - LICENSE
69
70
  - README.md
@@ -96,7 +97,7 @@ homepage: https://github.com/jesjos/active_record_upsert/
96
97
  licenses:
97
98
  - MIT
98
99
  metadata: {}
99
- post_install_message:
100
+ post_install_message:
100
101
  rdoc_options: []
101
102
  require_paths:
102
103
  - lib
@@ -111,9 +112,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
112
  - !ruby/object:Gem::Version
112
113
  version: '0'
113
114
  requirements: []
114
- rubyforge_project:
115
- rubygems_version: 2.6.14.4
116
- signing_key:
115
+ rubygems_version: 3.2.1
116
+ signing_key:
117
117
  specification_version: 4
118
118
  summary: Real PostgreSQL 9.5+ upserts using ON CONFLICT for ActiveRecord
119
119
  test_files: []