active_record_upsert 0.6.3 → 0.7.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
  SHA1:
3
- metadata.gz: 385757b53b221b5a937972a8fae3fddc3b8d7961
4
- data.tar.gz: 85c767dfb3b0256dc87e0957067d7324fca7239e
3
+ metadata.gz: 861dbd4abac80eae38c3644d5a982f3b4c7d95a8
4
+ data.tar.gz: fe700280c47b2cff78e3f8eeed2a14d966a10b72
5
5
  SHA512:
6
- metadata.gz: d37843428c375432fe428096d17d9e9aa5f3c7cb5a866eb0b28a3956d5bcb79db515b6941212eaae67b9b8c2df1f4667e5c7e307f375d8d233754bfaaa3c39df
7
- data.tar.gz: 6a47e3bfb52cd8fa1ea29650c044fa8d747e1c9ec695e54c6ed4146bafb08572fc377393d57a333fe248dd433eb621a2a875ef6618f012fd1aae49797a7625b0
6
+ metadata.gz: 3254084380ac0b9a14189ed071f53ec45e74d2701962b892720839d93b2855156e684f26e025742d19e8575031b3062e662c034bc19705caacabfafddde1a6ad
7
+ data.tar.gz: 859f1a55e2f6f2b64f192f18f305ae634fc50b4d24475a01bd5d0049cf4ad6d70cb931bd7215d4cfae492941046c04b4f15ca7a907fc2bbe844635bec5a0e340
data/README.md CHANGED
@@ -40,21 +40,40 @@ Or install it yourself as:
40
40
  ## Usage
41
41
  Just use `ActiveRecord.upsert` or `ActiveRecord#upsert`.
42
42
  *ActiveRecordUpsert* respects timestamps.
43
-
44
- ```
43
+
44
+ ```ruby
45
45
  class MyRecord < ActiveRecord::Base
46
46
  end
47
47
 
48
48
  MyRecord.create(name: 'foo', wisdom: 1)
49
- => #<MyRecord id: 1, name: "foo", created_at: "2016-02-20 14:15:55", updated_at: "2016-02-20 14:15:55", wisdom: 1>
49
+ # => #<MyRecord id: 1, name: "foo", created_at: "2016-02-20 14:15:55", updated_at: "2016-02-20 14:15:55", wisdom: 1>
50
50
 
51
51
  MyRecord.upsert(id: 1, wisdom: 3)
52
- => #<MyRecord id: 2, name: "foo", created_at: "2016-02-20 14:15:55", updated_at: "2016-02-20 14:18:15", wisdom: 3>
52
+ # => #<MyRecord id: 1, name: "foo", created_at: "2016-02-20 14:15:55", updated_at: "2016-02-20 14:18:15", wisdom: 3>
53
53
 
54
54
  r = MyRecord.new(id: 1)
55
55
  r.name = 'bar'
56
56
  r.upsert
57
- => #<MyRecord id: 2, name: "bar", created_at: "2016-02-20 14:17:50", updated_at: "2016-02-20 14:18:49", wisdom: 3>
57
+ # => #<MyRecord id: 1, name: "bar", created_at: "2016-02-20 14:15:55", updated_at: "2016-02-20 14:18:49", wisdom: 3>
58
+ ```
59
+
60
+ Also, it's possible to specify which columns should be used for the conflict clause. **These must comprise a unique index in Postgres.**
61
+
62
+ ```ruby
63
+ class Vehicle < ActiveRecord::Base
64
+ upsert_keys [:make, :name]
65
+ end
66
+
67
+ Vehicle.upsert(make: 'Ford', name: 'F-150', doors: 4)
68
+ # => #<Vehicle id: 1, make: 'Ford', name: 'Focus', doors: 2>
69
+
70
+ Vehicle.create(make: 'Ford', name: 'Focus', doors: 4)
71
+ # => #<Vehicle id: 2, make: 'Ford', name: 'Focus', doors: 4>
72
+
73
+ r = Vehicle.new(make: 'Ford', name: 'F-150')
74
+ r.doors = 2
75
+ r.upsert
76
+ # => #<Vehicle id: 1, make: 'Ford', name: 'Focus', doors: 2>
58
77
  ```
59
78
 
60
79
  ## Tests
@@ -77,3 +96,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/jesjos
77
96
  - Jens Nockert
78
97
  - Olle Jonsson
79
98
  - Simon Dahlbacka
99
+ - Paul Hoffer
@@ -12,21 +12,15 @@ Gem::Specification.new do |spec|
12
12
 
13
13
  spec.summary = %q{Real PostgreSQL 9.5+ upserts using ON CONFLICT for ActiveRecord}
14
14
 
15
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(bin|test|spec|features)/}) } -
16
+ %w[.gitignore .rspec .travis.yml Dockerfile Gemfile Gemfile.docker docker-compose.yml]
16
17
  spec.bindir = "exe"
17
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
19
  spec.require_paths = ["lib"]
19
20
 
20
- spec.add_runtime_dependency 'activerecord', '>= 5.0', '< 5.1'
21
- spec.add_runtime_dependency "arel", "~>7.0"
22
-
23
21
  spec.platform = Gem::Platform::RUBY
24
- spec.add_runtime_dependency 'pg', '~> 0.18'
25
22
 
26
- spec.add_development_dependency "bundler", ">= 1.13"
27
- spec.add_development_dependency "rake", ">= 10.0"
28
- spec.add_development_dependency "rspec", ">= 3.0", "< 4"
29
- spec.add_development_dependency "pry", "> 0"
30
- spec.add_development_dependency "database_cleaner", "~> 1.5.3"
31
- spec.add_development_dependency 'rails', '>= 5.0', '< 5.1'
23
+ spec.add_runtime_dependency 'activerecord', '>= 5.0', '< 5.2'
24
+ spec.add_runtime_dependency 'arel', '> 7.0', '< 9.0'
25
+ spec.add_runtime_dependency 'pg', '~> 0.18'
32
26
  end
@@ -2,12 +2,16 @@ module ActiveRecordUpsert
2
2
  module ActiveRecord
3
3
  module PersistenceExtensions
4
4
 
5
- def upsert
5
+ def upsert(attribute_names=nil)
6
6
  raise ::ActiveRecord::ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
7
7
  raise ::ActiveRecord::RecordSavedError, "Can't upsert a record that has already been saved" if persisted?
8
8
  values = run_callbacks(:save) {
9
9
  run_callbacks(:create) {
10
- _upsert_record
10
+ attribute_names ||= changed
11
+ attribute_names = attribute_names.map(&:to_s) +
12
+ timestamp_attributes_for_create_in_model +
13
+ timestamp_attributes_for_update_in_model
14
+ _upsert_record(attribute_names.uniq)
11
15
  }
12
16
  }
13
17
  assign_attributes(values.first.to_h)
@@ -28,9 +32,14 @@ module ActiveRecordUpsert
28
32
  if attributes.is_a?(Array)
29
33
  attributes.collect { |hash| upsert(hash, &block) }
30
34
  else
31
- new(attributes, &block).upsert
35
+ new(attributes, &block).upsert(attributes.keys)
32
36
  end
33
37
  end
38
+ def upsert_keys(*keys)
39
+ return @_upsert_keys if keys.empty?
40
+ keys = keys.first if keys.size == 1 # support single string/symbol, multiple string/symbols, and array
41
+ @_upsert_keys = Array(keys).map(&:to_s)
42
+ end
34
43
  end
35
44
  end
36
45
  end
@@ -14,11 +14,13 @@ module ActiveRecordUpsert
14
14
  im.into arel_table
15
15
 
16
16
  substitutes, binds = substitute_values values
17
+ column_arr = self.klass.upsert_keys || [primary_key]
18
+ column_name = column_arr.join(',')
17
19
 
18
20
  cm = arel_table.create_on_conflict_do_update
19
- cm.target = arel_table[primary_key]
21
+ cm.target = arel_table[column_name]
22
+ filter = ->(o) { [*column_arr, 'created_at'].include?(o.name) }
20
23
 
21
- filter = ->(o) { [primary_key, 'created_at'].include?(o.name) }
22
24
  cm.set(substitutes.reject { |s| filter.call(s.first) })
23
25
  on_conflict_binds = binds.reject(&filter)
24
26
 
@@ -29,8 +31,8 @@ module ActiveRecordUpsert
29
31
  @klass.connection.upsert(
30
32
  im,
31
33
  'SQL',
32
- primary_key,
33
- primary_key_value,
34
+ primary_key, # not used
35
+ primary_key_value, # not used
34
36
  nil,
35
37
  binds + on_conflict_binds)
36
38
  end
@@ -5,7 +5,7 @@ module ActiveRecordUpsert
5
5
  if self.record_timestamps
6
6
  current_time = current_time_from_proper_timezone
7
7
 
8
- all_timestamp_attributes.each do |column|
8
+ all_timestamp_attributes_in_model.each do |column|
9
9
  column = column.to_s
10
10
  if has_attribute?(column) && !attribute_present?(column)
11
11
  write_attribute(column, current_time)
@@ -13,7 +13,7 @@ module ActiveRecordUpsert
13
13
 
14
14
  def visit_Arel_Nodes_OnConflict o, collector
15
15
  collector << "ON CONFLICT "
16
- collector << " (#{quote_column_name o.target.name}) "
16
+ collector << " (#{quote_column_name o.target.name}) ".gsub(',', '","')
17
17
  maybe_visit o.action, collector
18
18
  end
19
19
 
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordUpsert
2
- VERSION = "0.6.3"
2
+ VERSION = "0.7.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_upsert
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jesper Josefsson
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2016-10-17 00:00:00.000000000 Z
12
+ date: 2017-08-22 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: '5.1'
23
+ version: '5.2'
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
@@ -30,21 +30,27 @@ dependencies:
30
30
  version: '5.0'
31
31
  - - "<"
32
32
  - !ruby/object:Gem::Version
33
- version: '5.1'
33
+ version: '5.2'
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: arel
36
36
  requirement: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '7.0'
41
+ - - "<"
42
+ - !ruby/object:Gem::Version
43
+ version: '9.0'
41
44
  type: :runtime
42
45
  prerelease: false
43
46
  version_requirements: !ruby/object:Gem::Requirement
44
47
  requirements:
45
- - - "~>"
48
+ - - ">"
46
49
  - !ruby/object:Gem::Version
47
50
  version: '7.0'
51
+ - - "<"
52
+ - !ruby/object:Gem::Version
53
+ version: '9.0'
48
54
  - !ruby/object:Gem::Dependency
49
55
  name: pg
50
56
  requirement: !ruby/object:Gem::Requirement
@@ -59,102 +65,6 @@ dependencies:
59
65
  - - "~>"
60
66
  - !ruby/object:Gem::Version
61
67
  version: '0.18'
62
- - !ruby/object:Gem::Dependency
63
- name: bundler
64
- requirement: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '1.13'
69
- type: :development
70
- prerelease: false
71
- version_requirements: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '1.13'
76
- - !ruby/object:Gem::Dependency
77
- name: rake
78
- requirement: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '10.0'
83
- type: :development
84
- prerelease: false
85
- version_requirements: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '10.0'
90
- - !ruby/object:Gem::Dependency
91
- name: rspec
92
- requirement: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '3.0'
97
- - - "<"
98
- - !ruby/object:Gem::Version
99
- version: '4'
100
- type: :development
101
- prerelease: false
102
- version_requirements: !ruby/object:Gem::Requirement
103
- requirements:
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- version: '3.0'
107
- - - "<"
108
- - !ruby/object:Gem::Version
109
- version: '4'
110
- - !ruby/object:Gem::Dependency
111
- name: pry
112
- requirement: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - ">"
115
- - !ruby/object:Gem::Version
116
- version: '0'
117
- type: :development
118
- prerelease: false
119
- version_requirements: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - ">"
122
- - !ruby/object:Gem::Version
123
- version: '0'
124
- - !ruby/object:Gem::Dependency
125
- name: database_cleaner
126
- requirement: !ruby/object:Gem::Requirement
127
- requirements:
128
- - - "~>"
129
- - !ruby/object:Gem::Version
130
- version: 1.5.3
131
- type: :development
132
- prerelease: false
133
- version_requirements: !ruby/object:Gem::Requirement
134
- requirements:
135
- - - "~>"
136
- - !ruby/object:Gem::Version
137
- version: 1.5.3
138
- - !ruby/object:Gem::Dependency
139
- name: rails
140
- requirement: !ruby/object:Gem::Requirement
141
- requirements:
142
- - - ">="
143
- - !ruby/object:Gem::Version
144
- version: '5.0'
145
- - - "<"
146
- - !ruby/object:Gem::Version
147
- version: '5.1'
148
- type: :development
149
- prerelease: false
150
- version_requirements: !ruby/object:Gem::Requirement
151
- requirements:
152
- - - ">="
153
- - !ruby/object:Gem::Version
154
- version: '5.0'
155
- - - "<"
156
- - !ruby/object:Gem::Version
157
- version: '5.1'
158
68
  description:
159
69
  email:
160
70
  - jesper.josefsson@gmail.com
@@ -163,21 +73,9 @@ executables: []
163
73
  extensions: []
164
74
  extra_rdoc_files: []
165
75
  files:
166
- - ".gitignore"
167
- - ".rspec"
168
- - ".travis.yml"
169
- - Dockerfile
170
- - Gemfile
171
- - Gemfile.docker
172
76
  - README.md
173
77
  - Rakefile
174
78
  - active_record_upsert.gemspec
175
- - bin/console
176
- - bin/release
177
- - bin/run_docker_test.sh
178
- - bin/run_rails.sh
179
- - bin/setup
180
- - docker-compose.yml
181
79
  - lib/active_record_upsert.rb
182
80
  - lib/active_record_upsert/active_record.rb
183
81
  - lib/active_record_upsert/active_record/connection_adapters/abstract/database_statements.rb
@@ -218,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
218
116
  version: '0'
219
117
  requirements: []
220
118
  rubyforge_project:
221
- rubygems_version: 2.6.7
119
+ rubygems_version: 2.6.12
222
120
  signing_key:
223
121
  specification_version: 4
224
122
  summary: Real PostgreSQL 9.5+ upserts using ON CONFLICT for ActiveRecord
data/.gitignore DELETED
@@ -1,10 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- /spec/dummy/log/*.log
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --require spec_helper
2
- --format documentation
3
- --color
data/.travis.yml DELETED
@@ -1,16 +0,0 @@
1
- sudo: required
2
- dist: trusty
3
- language: ruby
4
- env:
5
- - DATABASE_URL="postgresql://postgres@localhost/upsert_test"
6
- rvm:
7
- - 2.3.1
8
- before_script:
9
- - createdb --echo -U postgres upsert_test
10
- - psql -U postgres upsert_test < spec/dummy/db/structure.sql
11
-
12
- before_install: gem install bundler -v 1.13.3
13
-
14
- addons:
15
- postgresql: "9.5"
16
-
data/Dockerfile DELETED
@@ -1,18 +0,0 @@
1
- FROM quay.io/travisci/travis-ruby
2
- RUN apt-add-repository ppa:brightbox/ruby-ng
3
- RUN apt-get update
4
- RUN apt-get install ruby2.3 ruby2.3-dev
5
- RUN ruby --version
6
-
7
- ENV BUNDLE_GEMFILE=/app/Gemfile.docker
8
- RUN gem install nokogiri
9
- RUN gem install bundler
10
- COPY Gemfile* *.gemspec /app/
11
- RUN mkdir -p /app/lib/active_record_upsert
12
- COPY lib/active_record_upsert/version.rb /app/lib/active_record_upsert/
13
- WORKDIR /app
14
- RUN bundle install
15
- COPY . /app
16
- CMD bin/run_docker_test.sh
17
-
18
-
data/Gemfile DELETED
@@ -1,5 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in active_record_upsert.gemspec
4
- gemspec
5
-
data/Gemfile.docker DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in active_record_upsert.gemspec
4
- gemspec
data/bin/console DELETED
@@ -1,18 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require 'active_record'
5
- require 'active_record/connection_adapters/postgresql_adapter'
6
- require "active_record_upsert"
7
- require File.join(__dir__, '../spec/setup')
8
- ActiveRecord::Base.logger = Logger.new(STDOUT)
9
-
10
- # You can add fixtures and/or initialization code here to make experimenting
11
- # with your gem easier. You can also use a different console, if you like.
12
-
13
- # (If you use this, don't forget to add pry to your Gemfile!)
14
- # require "pry"
15
- # Pry.start
16
-
17
- require "irb"
18
- IRB.start
data/bin/release DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -e
3
- OUR_RUBY=2.3.1
4
-
5
- rvm $OUR_RUBY do bundle check
6
- rvm $OUR_RUBY do bundle exec rake release
7
-
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env bash
2
- pushd spec/dummy
3
- RAILS_ENV=test rails db:migrate
4
- popd
5
- RAILS_ENV=test bundle exec rspec
data/bin/run_rails.sh DELETED
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env bash
2
- pushd spec/dummy
3
- RAILS_ENV=test bundle exec rails $@
4
- popd
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/docker-compose.yml DELETED
@@ -1,14 +0,0 @@
1
- version: '2'
2
- services:
3
- db:
4
- image: postgres:9.5
5
- ports:
6
- - "5432:5432"
7
- app:
8
- environment:
9
- - DATABASE_URL=postgresql://postgres@db/active_record_upsert_test
10
- build: .
11
- depends_on:
12
- - db
13
- links:
14
- - db