active_record_upsert 0.9.4 → 0.11.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
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