active_record_upsert 0.9.4 → 0.11.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
  SHA256:
3
- metadata.gz: de123fd4fdfdb6a7cbebd1aa2d7232ecfdebf014723e4b64c7e0ad3a8c8f61b4
4
- data.tar.gz: 27d972189bb6e75c48eb43cc7cb93362991ad237ffe84a25349fe148b648187b
3
+ metadata.gz: 459493128507f8a6ffd12fca94b8270cc93259601b4136c4686ae11f2acd7433
4
+ data.tar.gz: b3c8961297902a34a92d1f947980d4e1829d8e563401f54a7094d110df77fc66
5
5
  SHA512:
6
- metadata.gz: c33829cb05d8c261c850e3dcde28842cd50824369231c58b936c30bf6ddae64fb76c68ec594e4e62f4578eb4940d51d23033056a31bfeea2da4e4ad821e14070
7
- data.tar.gz: f8943ad447f87aa33b8ae4facdfda3ae7368c8d3e9b3c7bfc1985ddfd97dd2654670620ed803ed52f38034c6c4bc7a49fdc0d8135be83f16504b2efcdb59ed50
6
+ metadata.gz: 9007d2d2c1889c6b2742783319b7d0c878cf47d01bee568126984390b8ba73e52384aa84ebff72f1cd0f895a42833a53bf55cfe6339b5a07de0353fe307b1f5c
7
+ data.tar.gz: 8e643c603a776f476a4a0407a5bfa301b0ef8afa726000a32b79fa95a6b036ab04f8360183cf7d33b694912e2577745efbe17418dc572a9a5b6554a757cad864
data/Gemfile.base CHANGED
@@ -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'
@@ -1,5 +1,5 @@
1
1
  group :development, :test do
2
- gem 'rails', '>= 5.0', '< 5.1'
2
+ gem 'rails', '~> 6.0'
3
3
  end
4
4
 
5
5
  eval_gemfile "#{__dir__}/Gemfile.base"
@@ -1,5 +1,5 @@
1
1
  group :development, :test do
2
- gem 'rails', '>= 5.1', '< 5.2'
2
+ gem 'rails', '~> 6.1'
3
3
  end
4
4
 
5
5
  eval_gemfile "#{__dir__}/Gemfile.base"
data/Gemfile.rails-7-0 ADDED
@@ -0,0 +1,5 @@
1
+ group :development, :test do
2
+ gem 'rails', '~> 7.0'
3
+ end
4
+
5
+ eval_gemfile "#{__dir__}/Gemfile.base"
data/README.md CHANGED
@@ -2,12 +2,9 @@
2
2
  [![Build Status](https://travis-ci.org/jesjos/active_record_upsert.svg?branch=master)](https://travis-ci.org/jesjos/active_record_upsert)
3
3
  [![Code Climate](https://codeclimate.com/github/jesjos/active_record_upsert/badges/gpa.svg)](https://codeclimate.com/github/jesjos/active_record_upsert)
4
4
 
5
- # NB: INCOMPATIBILITY
6
- B/c of a broken build matrix, versions 0.9.2 - 0.9.3 are incompatible with rails < 5.2.1!
7
-
8
5
  # ActiveRecordUpsert
9
6
 
10
- 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.2+ / ActiveRecord 5.2+. Uses [ON CONFLICT DO UPDATE](http://www.postgresql.org/docs/9.5/static/sql-insert.html).
11
8
 
12
9
  ## Main points
13
10
 
@@ -17,11 +14,29 @@ Real upsert for PostgreSQL 9.5+ and Rails 5 / ActiveRecord 5. Uses [ON CONFLICT
17
14
 
18
15
  ## Prerequisites
19
16
 
20
- - PostgreSQL 9.5+
21
- - ActiveRecord ~> 5
22
- - For MRI: pg
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.2
19
+ - Ruby MRI, with the `pg` gem
20
+ - _JRuby is currently not supported_
21
+
22
+ ### NB: Releases to avoid
23
+
24
+ Due to a broken build matrix, v0.9.2 and v0.9.3 are incompatible with Rails
25
+ < 5.2.1. [v0.9.4](https://github.com/jesjos/active_record_upsert/releases/tag/v0.9.4) fixed this issue.
26
+
27
+ ### Supported Rails versions
23
28
 
24
- - For JRuby: No support
29
+ This library is compatible with all major Rails versions covered by the Rails
30
+ ["Severe Security Issues" maintenance policy](https://guides.rubyonrails.org/maintenance_policy.html).
31
+
32
+ ### Supported Ruby versions
33
+
34
+ This library may be compatible with older versions of Ruby, however we only run automated
35
+ tests using the
36
+ [officially supported Ruby versions](https://www.ruby-lang.org/en/downloads/branches/).
37
+
38
+ Please note that Ruby 3.1 is not currently tested because it is incompatible with Rails
39
+ `7.0.0`, `6.1.4.4`, `6.0.4.4` and `5.2.6` ([issue](https://github.com/rails/rails/issues/43998)).
25
40
 
26
41
  ## Installation
27
42
 
@@ -121,6 +136,37 @@ r = MyRecord.new(id: 1, name: 'bar')
121
136
  r.upsert!
122
137
  ```
123
138
 
139
+ ### Gotcha with database defaults
140
+
141
+ 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).
142
+
143
+ **Example**: a table called `hardwares` has a `prio` column with a default value.
144
+
145
+ ┌─────────┬─────────┬───────┬
146
+ │ Column │ Type │Default│
147
+ ├─────────┼─────────┼───────┼
148
+ │ id │ integer │ ...
149
+ │ prio │ integer │ 999
150
+
151
+ And `hardwares` has a record with a non-default value for `prio`. Say, the record with `id` 1 has a `prio` of `998`.
152
+
153
+ In this situation, upserting like:
154
+
155
+ ```ruby
156
+ hw = { id: 1, prio: 999 }
157
+ Hardware.new(prio: hw[:prio]).upsert
158
+ ```
159
+
160
+ will not mention the `prio` column in the `ON CONFLICT` clause, resulting in no update.
161
+
162
+ However, upserting like so:
163
+
164
+ ```ruby
165
+ Hardware.upsert(prio: hw[:prio]).id
166
+ ```
167
+
168
+ will indeed update the record in the database back to its default value, `999`.
169
+
124
170
  ### Conflict Clauses
125
171
 
126
172
  It's possible to specify which columns should be used for the conflict clause. **These must comprise a unique index in Postgres.**
@@ -158,6 +204,24 @@ class Account < ApplicationRecord
158
204
  end
159
205
  ```
160
206
 
207
+ Overriding the models' `upsert_keys` when calling `#upsert` or `.upsert`:
208
+
209
+ ```ruby
210
+ Account.upsert(attrs, opts: { upsert_keys: [:foo, :bar] })
211
+ # Or, on an instance:
212
+ account = Account.new(attrs)
213
+ account.upsert(opts: { upsert_keys: [:foo, :bar] })
214
+ ```
215
+
216
+ Overriding the models' `upsert_options` (partial index) when calling `#upsert` or `.upsert`:
217
+
218
+ ```ruby
219
+ Account.upsert(attrs, opts: { upsert_options: { where: 'foo IS NOT NULL' } })
220
+ # Or, on an instance:
221
+ account = Account.new(attrs)
222
+ account.upsert(opts: { upsert_options: { where: 'foo IS NOT NULLL } })
223
+ ```
224
+
161
225
  ## Tests
162
226
 
163
227
  Make sure to have an upsert_test database:
@@ -188,3 +252,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/jesjos
188
252
  - Daniel Cooper ([@danielcooper](https://github.com/danielcooper))
189
253
  - Laurent Vallar ([@val](https://github.com/val))
190
254
  - Emmanuel Quentin ([@manuquentin](https://github.com/manuquentin))
255
+ - Jeff Wallace ([@tjwallace](https://github.com/tjwallace))
256
+ - Kirill Zaitsev ([@Bugagazavr](https://github.com/Bugagazavr))
257
+ - Nick Campbell ([@nickcampbell18](https://github.com/nickcampbell18))
258
+ - Mikhail Doronin ([@misdoro](https://github.com/misdoro))
@@ -13,15 +13,14 @@ Gem::Specification.new do |spec|
13
13
 
14
14
  spec.summary = %q{Real PostgreSQL 9.5+ upserts using ON CONFLICT for ActiveRecord}
15
15
 
16
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(bin|test|spec|features)/}) } -
17
- %w[.gitignore .rspec .travis.yml Dockerfile Gemfile Gemfile.docker docker-compose.yml]
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(.github|bin|test|spec|features)/}) } -
17
+ %w[.gitignore .rspec Dockerfile Gemfile Gemfile.docker docker-compose.yml]
18
18
  spec.bindir = "exe"
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
22
  spec.platform = Gem::Platform::RUBY
23
23
 
24
- spec.add_runtime_dependency 'activerecord', '>= 5.0', '< 6.0'
25
- spec.add_runtime_dependency 'arel', '> 7.0', '< 10.0'
24
+ spec.add_runtime_dependency 'activerecord', '>= 5.2', '< 7.1'
26
25
  spec.add_runtime_dependency 'pg', '>= 0.18', '< 2.0'
27
26
  end
@@ -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,24 +11,26 @@ 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
 
18
18
  self
19
19
  end
20
20
 
21
- def upsert(*args)
22
- upsert!(*args)
21
+ def upsert(**kwargs)
22
+ upsert!(**kwargs)
23
23
  rescue ::ActiveRecord::RecordInvalid
24
24
  false
25
25
  end
26
26
 
27
- def _upsert_record(upsert_attribute_names = changed, arel_condition = nil)
28
- existing_attributes = attributes_with_values_for_create(self.attributes.keys)
29
- values = self.class._upsert_record(existing_attributes, upsert_attribute_names, [arel_condition].compact)
27
+ def _upsert_record(upsert_attribute_names = changed, arel_condition = nil, opts = {})
28
+ existing_attribute_names = attributes_for_create(attributes.keys)
29
+ existing_attributes = attributes_with_values(existing_attribute_names)
30
+ values = self.class._upsert_record(existing_attributes, upsert_attribute_names, [arel_condition].compact, opts)
30
31
  @attributes = self.class.attributes_builder.build_from_database(values.first.to_h)
31
32
  @new_record = false
33
+ changes_applied
32
34
  values
33
35
  end
34
36
 
@@ -39,24 +41,25 @@ module ActiveRecordUpsert
39
41
  end
40
42
 
41
43
  module ClassMethods
42
- def upsert!(attributes, arel_condition: nil, validate: true, &block)
44
+ def upsert!(attributes, arel_condition: nil, validate: true, opts: {}, &block)
43
45
  if attributes.is_a?(Array)
44
46
  attributes.collect { |hash| upsert(hash, &block) }
45
47
  else
46
48
  new(attributes, &block).upsert!(
47
- attributes: attributes.keys, arel_condition: arel_condition, validate: validate
49
+ attributes: attributes.keys, arel_condition: arel_condition, validate: validate, opts: opts
48
50
  )
49
51
  end
50
52
  end
51
53
 
52
- def upsert(*args)
53
- upsert!(*args)
54
+ def upsert(attributes, **kwargs, &block)
55
+ upsert!(attributes, **kwargs, &block)
54
56
  rescue ::ActiveRecord::RecordInvalid
55
57
  false
56
58
  end
57
59
 
58
- def _upsert_record(existing_attributes, upsert_attributes_names, wheres) # :nodoc:
59
- upsert_keys = self.upsert_keys || [primary_key]
60
+ def _upsert_record(existing_attributes, upsert_attributes_names, wheres, opts) # :nodoc:
61
+ upsert_keys = opts[:upsert_keys] || self.upsert_keys || [primary_key]
62
+ upsert_options = opts[:upsert_options] || self.upsert_options
60
63
  upsert_attributes_names = upsert_attributes_names - [*upsert_keys, 'created_at']
61
64
 
62
65
  existing_attributes = existing_attributes
@@ -1,47 +1,8 @@
1
- # module ActiveRecordUpsert
2
- # module Arel
3
- # module Nodes
4
- # module InsertStatementExtensions
5
- # attr_accessor :on_conflict
6
- #
7
- # def initialize
8
- # @on_conflict = nil
9
- # super()
10
- # end
11
- #
12
- # def hash
13
- # [@relation, @columns, @values, @select, @on_conflict].hash
14
- # end
15
- #
16
- # def eql? other
17
- # self.class == other.class &&
18
- # self.relation == other.relation &&
19
- # self.columns == other.columns &&
20
- # self.select == other.select &&
21
- # self.values == other.values &&
22
- # self.on_conflict == other.on_conflict
23
- # end
24
- # end
25
- #
26
- # ::Arel::Nodes::InsertStatement.prepend(InsertStatementExtensions)
27
- # end
28
- # end
29
- # end
30
-
31
1
  module Arel
32
2
  module Nodes
33
3
  class InsertStatement
34
4
  attr_accessor :on_conflict
35
5
 
36
- def initialize
37
- super()
38
- @relation = nil
39
- @columns = []
40
- @values = nil
41
- @select = nil
42
- @on_conflict = nil
43
- end
44
-
45
6
  def hash
46
7
  [@relation, @columns, @values, @select, @on_conflict].hash
47
8
  end
@@ -0,0 +1,23 @@
1
+ module ActiveRecordUpsert
2
+ module Arel
3
+ module TableExtensions
4
+ def compile_upsert(upsert_keys, upsert_options, upsert_values, insert_values, wheres)
5
+ # Support non-attribute key (like `md5(my_attribute)``)
6
+ target = self[upsert_options.key?(:literal) ? ::Arel::Nodes::SqlLiteral.new(upsert_options[:literal]) : upsert_keys.join(',')]
7
+ on_conflict_do_update = ::Arel::OnConflictDoUpdateManager.new
8
+
9
+ on_conflict_do_update.target = target
10
+ on_conflict_do_update.target_condition = upsert_options[:where]
11
+ on_conflict_do_update.wheres = wheres
12
+ on_conflict_do_update.set(upsert_values)
13
+
14
+ insert_manager = ::Arel::InsertManager.new
15
+ insert_manager.on_conflict = on_conflict_do_update.to_node
16
+ insert_manager.into insert_values.first.first.relation
17
+ insert_manager.insert(insert_values)
18
+ insert_manager
19
+ end
20
+ end
21
+ ::Arel::Table.prepend(TableExtensions)
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ module ActiveRecordUpsert
2
+ module ActiveRecord
3
+ module TransactionsExtensions
4
+ def upsert(*args, **kwargs)
5
+ with_transaction_returning_status { super }
6
+ end
7
+ end
8
+
9
+ module ConnectAdapterExtension
10
+ def upsert(*args, **kwargs)
11
+ ::ActiveRecord::Base.clear_query_caches_for_current_thread
12
+ super
13
+ end
14
+
15
+ ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(self)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,59 @@
1
+ module ActiveRecordUpsert
2
+ module ActiveRecord
3
+ module PersistenceExtensions
4
+ module ClassMethods
5
+ def __substitute_values(values, table)
6
+ values.map do |name, value|
7
+ attr = table[name]
8
+ unless ::Arel.arel_node?(value) || value.is_a?(::ActiveModel::Attribute)
9
+ type = type_for_attribute(attr.name)
10
+ value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
11
+ end
12
+ [attr, value]
13
+ end
14
+ end
15
+
16
+ def _upsert_record(existing_attributes, upsert_attributes_names, wheres, opts) # :nodoc:
17
+ upsert_keys = opts[:upsert_keys] || self.upsert_keys || [primary_key]
18
+ upsert_options = opts[:upsert_options] || self.upsert_options
19
+ upsert_attributes_names = upsert_attributes_names - [*upsert_keys, 'created_at']
20
+
21
+ existing_attributes = existing_attributes
22
+ .transform_keys { |name| _prepare_column(name) }
23
+ .reject { |key, _| key.nil? }
24
+
25
+ upsert_attributes_names = upsert_attributes_names
26
+ .map { |name| _prepare_column(name) }
27
+ .compact
28
+
29
+ values_for_upsert = existing_attributes.select { |(name, _value)| upsert_attributes_names.include?(name) }
30
+
31
+ insert_manager = arel_table.compile_upsert(
32
+ upsert_keys,
33
+ upsert_options,
34
+ __substitute_values(values_for_upsert, arel_table),
35
+ __substitute_values(existing_attributes, arel_table),
36
+ wheres
37
+ )
38
+
39
+ connection.upsert(insert_manager, "#{self} Upsert")
40
+ end
41
+ end
42
+ end
43
+
44
+ module TransactionsExtensions
45
+ def upsert(*args, **kwargs)
46
+ with_transaction_returning_status { super }
47
+ end
48
+ end
49
+
50
+ module ConnectAdapterExtension
51
+ def upsert(*args, **kwargs)
52
+ ::ActiveRecord::Base.clear_query_caches_for_current_thread
53
+ super
54
+ end
55
+
56
+ ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(self)
57
+ end
58
+ end
59
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordUpsert
2
- VERSION = "0.9.4"
2
+ VERSION = "0.11.0"
3
3
  end
@@ -13,8 +13,8 @@ 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/rails51.rb' if version >= '5.1.0' && version < '5.2.0'
17
- require 'active_record_upsert/compatibility/rails50.rb' if version >= '5.0.0' && version < '5.1.0'
16
+ require 'active_record_upsert/compatibility/rails70.rb' if version >= '7.0.0' && version < '7.2.0'
17
+ require 'active_record_upsert/compatibility/rails60.rb' if version >= '6.0.0' && version < '6.2.0'
18
18
 
19
19
  module ActiveRecordUpsert
20
20
  # Your code goes here...
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.4
4
+ version: 0.11.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: 2018-09-19 00:00:00.000000000 Z
12
+ date: 2022-01-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -17,40 +17,20 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '5.0'
20
+ version: '5.2'
21
21
  - - "<"
22
22
  - !ruby/object:Gem::Version
23
- version: '6.0'
23
+ version: '7.1'
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
27
27
  requirements:
28
28
  - - ">="
29
29
  - !ruby/object:Gem::Version
30
- version: '5.0'
30
+ version: '5.2'
31
31
  - - "<"
32
32
  - !ruby/object:Gem::Version
33
- version: '6.0'
34
- - !ruby/object:Gem::Dependency
35
- name: arel
36
- requirement: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">"
39
- - !ruby/object:Gem::Version
40
- version: '7.0'
41
- - - "<"
42
- - !ruby/object:Gem::Version
43
- version: '10.0'
44
- type: :runtime
45
- prerelease: false
46
- version_requirements: !ruby/object:Gem::Requirement
47
- requirements:
48
- - - ">"
49
- - !ruby/object:Gem::Version
50
- version: '7.0'
51
- - - "<"
52
- - !ruby/object:Gem::Version
53
- version: '10.0'
33
+ version: '7.1'
54
34
  - !ruby/object:Gem::Dependency
55
35
  name: pg
56
36
  requirement: !ruby/object:Gem::Requirement
@@ -71,7 +51,7 @@ dependencies:
71
51
  - - "<"
72
52
  - !ruby/object:Gem::Version
73
53
  version: '2.0'
74
- description:
54
+ description:
75
55
  email:
76
56
  - jesper.josefsson@gmail.com
77
57
  - olle.jonsson@gmail.com
@@ -80,9 +60,10 @@ extensions: []
80
60
  extra_rdoc_files: []
81
61
  files:
82
62
  - Gemfile.base
83
- - Gemfile.rails-5-0
84
- - Gemfile.rails-5-1
85
63
  - Gemfile.rails-5-2
64
+ - Gemfile.rails-6-0
65
+ - Gemfile.rails-6-1
66
+ - Gemfile.rails-7-0
86
67
  - Gemfile.rails-master
87
68
  - LICENSE
88
69
  - README.md
@@ -96,7 +77,6 @@ files:
96
77
  - lib/active_record_upsert/active_record/timestamp.rb
97
78
  - lib/active_record_upsert/active_record/transactions.rb
98
79
  - lib/active_record_upsert/arel.rb
99
- - lib/active_record_upsert/arel/crud.rb
100
80
  - lib/active_record_upsert/arel/insert_manager.rb
101
81
  - lib/active_record_upsert/arel/nodes.rb
102
82
  - lib/active_record_upsert/arel/nodes/do_nothing.rb
@@ -106,15 +86,16 @@ files:
106
86
  - lib/active_record_upsert/arel/nodes/on_conflict.rb
107
87
  - lib/active_record_upsert/arel/nodes/on_conflict_action.rb
108
88
  - lib/active_record_upsert/arel/on_conflict_do_update_manager.rb
89
+ - lib/active_record_upsert/arel/table_extensions.rb
109
90
  - lib/active_record_upsert/arel/visitors/to_sql.rb
110
- - lib/active_record_upsert/compatibility/rails50.rb
111
- - lib/active_record_upsert/compatibility/rails51.rb
91
+ - lib/active_record_upsert/compatibility/rails60.rb
92
+ - lib/active_record_upsert/compatibility/rails70.rb
112
93
  - lib/active_record_upsert/version.rb
113
94
  homepage: https://github.com/jesjos/active_record_upsert/
114
95
  licenses:
115
96
  - MIT
116
97
  metadata: {}
117
- post_install_message:
98
+ post_install_message:
118
99
  rdoc_options: []
119
100
  require_paths:
120
101
  - lib
@@ -129,9 +110,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
110
  - !ruby/object:Gem::Version
130
111
  version: '0'
131
112
  requirements: []
132
- rubyforge_project:
133
- rubygems_version: 2.7.7
134
- signing_key:
113
+ rubygems_version: 3.2.7
114
+ signing_key:
135
115
  specification_version: 4
136
116
  summary: Real PostgreSQL 9.5+ upserts using ON CONFLICT for ActiveRecord
137
117
  test_files: []
@@ -1,31 +0,0 @@
1
- # module ActiveRecordUpsert
2
- # module Arel
3
- # module CrudExtensions
4
- # def create_on_conflict_do_update
5
- # OnConflictDoUpdateManager.new
6
- # end
7
- # end
8
- #
9
- # ::Arel::Crud.prepend(CrudExtensions)
10
- # end
11
- # end
12
- module Arel
13
- module Crud
14
- def compile_upsert(upsert_keys, upsert_options, upsert_values, insert_values, wheres)
15
- # Support non-attribute key (like `md5(my_attribute)``)
16
- target = self[upsert_options.key?(:literal) ? ::Arel::Nodes::SqlLiteral.new(upsert_options[:literal]) : upsert_keys.join(',')]
17
- on_conflict_do_update = OnConflictDoUpdateManager.new
18
-
19
- on_conflict_do_update.target = target
20
- on_conflict_do_update.target_condition = upsert_options[:where]
21
- on_conflict_do_update.wheres = wheres
22
- on_conflict_do_update.set(upsert_values)
23
-
24
- insert_manager = create_insert
25
- insert_manager.on_conflict = on_conflict_do_update.to_node
26
- insert_manager.into insert_values.first.first.relation
27
- insert_manager.insert(insert_values)
28
- insert_manager
29
- end
30
- end
31
- end
@@ -1 +0,0 @@
1
- require_relative 'rails51'
@@ -1,63 +0,0 @@
1
- module ActiveRecordUpsert
2
- module ActiveRecord
3
- module PersistenceExtensions
4
- def _upsert_record(upsert_attribute_names = changed, arel_condition = nil)
5
- upsert_attribute_names = upsert_attribute_names.map { |name| _prepare_column(name) } & self.class.column_names
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)
8
- @new_record = false
9
- @attributes = self.class.attributes_builder.build_from_database(values.first.to_h)
10
- values
11
- end
12
-
13
- def _prepare_column(name)
14
- if self.class.reflections.key?(name)
15
- self.class.reflections[name].foreign_key
16
- else
17
- name
18
- end
19
- end
20
- end
21
-
22
- module RelationExtensions
23
- def upsert(existing_attributes, upsert_attributes, wheres) # :nodoc:
24
- substitutes, binds = substitute_values(existing_attributes)
25
- upsert_keys = self.klass.upsert_keys || [primary_key]
26
-
27
- upsert_attributes = upsert_attributes - [*upsert_keys, 'created_at']
28
- upsert_keys_filter = ->(o) { upsert_attributes.include?(o.name) }
29
-
30
- on_conflict_binds = binds.select(&upsert_keys_filter)
31
- vals_for_upsert = substitutes.select { |s| upsert_keys_filter.call(s.first) }
32
-
33
- target = arel_table[upsert_options.key?(:literal) ? ::Arel::Nodes::SqlLiteral.new(upsert_options[:literal]) : upsert_keys.join(',')]
34
-
35
- on_conflict_do_update = ::Arel::OnConflictDoUpdateManager.new
36
- on_conflict_do_update.target = target
37
- on_conflict_do_update.target_condition = self.klass.upsert_options[:where]
38
- on_conflict_do_update.wheres = wheres
39
- on_conflict_do_update.set(vals_for_upsert)
40
-
41
- insert_manager = arel_table.create_insert
42
- insert_manager.into arel_table
43
- insert_manager.on_conflict = on_conflict_do_update.to_node
44
- insert_manager.insert substitutes
45
-
46
- @klass.connection.upsert(insert_manager, "#{self} Upsert", binds + on_conflict_binds)
47
- end
48
-
49
- ::ActiveRecord::Relation.include(self)
50
- end
51
-
52
- module ConnectionAdapters
53
- module Postgresql
54
- module DatabaseStatementsExtensions
55
- def upsert(arel, name = nil, binds = [])
56
- sql = to_sql(arel, binds)
57
- exec_upsert(sql, name, binds)
58
- end
59
- end
60
- end
61
- end
62
- end
63
- end