active_record_upsert 0.9.5 → 0.10.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
- 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: []