activerecord-batch_touching 1.0.pre.beta → 1.0.pre.beta2
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 +4 -4
- data/lib/activerecord/batch_touching.rb +31 -27
- metadata +2 -16
- data/.gitignore +0 -4
- data/.rspec +0 -4
- data/Gemfile +0 -4
- data/activerecord-batch_touching.gemspec +0 -30
- data/spec/active_record/batch_touching_spec.rb +0 -369
- data/spec/rcov_exclude_list.rb +0 -3
- data/spec/spec_helper.rb +0 -22
- data/spec/support/models.rb +0 -9
- data/spec/support/schema.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd952639925581e1a9402a4e1542ca8be34e826ee23754742381daea8465f59d
|
4
|
+
data.tar.gz: 061f01b40c568b24b9c383558c63f87bf29e27ded41192ae30d5afc0106294af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25f107a0f99f1e951a5ec5c83115aab53c90676ffbc9652e170566fbd933fa61a18e1649a0a885a29e935fd08aa27dcc26450632c86c69230792cc48f5e6c799
|
7
|
+
data.tar.gz: a231c2bfc1aefd7d56b537c41d5a0f887660a4b4d842e1f066a6e65057402471fe8be1fc456eecb92c901f4e8b45bd13308a6c2a84c7c1f1b8343a7e14463108
|
@@ -2,6 +2,36 @@ require "activerecord/batch_touching/version"
|
|
2
2
|
require "activerecord/batch_touching/state"
|
3
3
|
|
4
4
|
module ActiveRecord
|
5
|
+
module BatchTouchingAbstractAdapter
|
6
|
+
# Batches up +touch+ calls for the duration of a transaction.
|
7
|
+
# +after_touch+ callbacks are also delayed until the transaction is committed.
|
8
|
+
#
|
9
|
+
# ==== Examples
|
10
|
+
#
|
11
|
+
# # Touches Person.first and Person.last in a single database round-trip.
|
12
|
+
# Person.transaction do
|
13
|
+
# Person.first.touch
|
14
|
+
# Person.last.touch
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# # Touches Person.first once, not twice, right before the transaction is committed.
|
18
|
+
# Person.transaction do
|
19
|
+
# Person.first.touch
|
20
|
+
# Person.first.touch
|
21
|
+
# end
|
22
|
+
def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
|
23
|
+
super(requires_new: requires_new, isolation: isolation, joinable: joinable) do
|
24
|
+
BatchTouching.start(requires_new: requires_new, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module ConnectionAdapters
|
30
|
+
class AbstractAdapter
|
31
|
+
prepend BatchTouchingAbstractAdapter
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
5
35
|
# = Active Record Batch Touching
|
6
36
|
module BatchTouching # :nodoc:
|
7
37
|
extend ActiveSupport::Concern
|
@@ -9,7 +39,7 @@ module ActiveRecord
|
|
9
39
|
# Override ActiveRecord::Base#touch_later. This will effectively disable the current built-in mechanism AR uses
|
10
40
|
# to delay touching in favor of our method of batch touching.
|
11
41
|
def touch_later(*names)
|
12
|
-
touch(*names)
|
42
|
+
BatchTouching.batch_touching? ? touch(*names) : super
|
13
43
|
end
|
14
44
|
|
15
45
|
# Override ActiveRecord::Base#touch. If currently batching touches, always return
|
@@ -24,32 +54,6 @@ module ActiveRecord
|
|
24
54
|
end
|
25
55
|
end
|
26
56
|
|
27
|
-
# These get added as class methods to ActiveRecord::Base.
|
28
|
-
module ClassMethods
|
29
|
-
# Batches up +touch+ calls for the duration of a transaction.
|
30
|
-
# +after_touch+ callbacks are also delayed until the transaction is committed.
|
31
|
-
#
|
32
|
-
# ==== Examples
|
33
|
-
#
|
34
|
-
# # Touches Person.first and Person.last in a single database round-trip.
|
35
|
-
# Person.transaction do
|
36
|
-
# Person.first.touch
|
37
|
-
# Person.last.touch
|
38
|
-
# end
|
39
|
-
#
|
40
|
-
# # Touches Person.first once, not twice, right before the transaction is committed.
|
41
|
-
# Person.transaction do
|
42
|
-
# Person.first.touch
|
43
|
-
# Person.first.touch
|
44
|
-
# end
|
45
|
-
#
|
46
|
-
def transaction(**options, &block)
|
47
|
-
super(**options) do
|
48
|
-
BatchTouching.start(**options, &block)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
57
|
class << self
|
54
58
|
def states
|
55
59
|
Thread.current[:batch_touching_states] ||= []
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-batch_touching
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.pre.
|
4
|
+
version: 1.0.pre.beta2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Morearty
|
@@ -132,19 +132,10 @@ executables: []
|
|
132
132
|
extensions: []
|
133
133
|
extra_rdoc_files: []
|
134
134
|
files:
|
135
|
-
- ".gitignore"
|
136
|
-
- ".rspec"
|
137
|
-
- Gemfile
|
138
135
|
- LICENSE
|
139
|
-
- activerecord-batch_touching.gemspec
|
140
136
|
- lib/activerecord/batch_touching.rb
|
141
137
|
- lib/activerecord/batch_touching/state.rb
|
142
138
|
- lib/activerecord/batch_touching/version.rb
|
143
|
-
- spec/active_record/batch_touching_spec.rb
|
144
|
-
- spec/rcov_exclude_list.rb
|
145
|
-
- spec/spec_helper.rb
|
146
|
-
- spec/support/models.rb
|
147
|
-
- spec/support/schema.rb
|
148
139
|
homepage: ''
|
149
140
|
licenses:
|
150
141
|
- MIT
|
@@ -168,9 +159,4 @@ rubygems_version: 3.3.7
|
|
168
159
|
signing_key:
|
169
160
|
specification_version: 4
|
170
161
|
summary: Batch up your ActiveRecord "touch" operations for better performance.
|
171
|
-
test_files:
|
172
|
-
- spec/active_record/batch_touching_spec.rb
|
173
|
-
- spec/rcov_exclude_list.rb
|
174
|
-
- spec/spec_helper.rb
|
175
|
-
- spec/support/models.rb
|
176
|
-
- spec/support/schema.rb
|
162
|
+
test_files: []
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/Gemfile
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'activerecord/batch_touching/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "activerecord-batch_touching"
|
8
|
-
spec.version = "1.0-beta" # Activerecord::BatchTouching::VERSION
|
9
|
-
spec.authors = ["Brian Morearty", "Phil Phillips"]
|
10
|
-
spec.email = ["phil@productplan.com"]
|
11
|
-
spec.summary = %q{Batch up your ActiveRecord "touch" operations for better performance.}
|
12
|
-
spec.description = %q{Batch up your ActiveRecord "touch" operations for better performance. All accumulated "touch" calls will be consolidated into as few database round trips as possible.}
|
13
|
-
spec.homepage = ""
|
14
|
-
spec.license = "MIT"
|
15
|
-
|
16
|
-
spec.files = `git ls-files -z`.split("\x0")
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = ["lib"]
|
20
|
-
|
21
|
-
spec.add_dependency "activerecord", ">= 6"
|
22
|
-
|
23
|
-
spec.add_development_dependency "bundler"
|
24
|
-
spec.add_development_dependency "rake"
|
25
|
-
spec.add_development_dependency "sqlite3"
|
26
|
-
spec.add_development_dependency "timecop"
|
27
|
-
spec.add_development_dependency "rspec-rails"
|
28
|
-
spec.add_development_dependency "simplecov"
|
29
|
-
spec.add_development_dependency "simplecov-rcov"
|
30
|
-
end
|
@@ -1,369 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Activerecord::BatchTouching do
|
4
|
-
let!(:owner) { Owner.create name: "Rosey" }
|
5
|
-
let!(:pet1) { Pet.create(name: "Bones", owner: owner) }
|
6
|
-
let!(:pet2) { Pet.create(name: "Ema", owner: owner) }
|
7
|
-
let!(:car) { Car.create(name: "Ferrari", lock_version: 1) }
|
8
|
-
|
9
|
-
it 'has a version number' do
|
10
|
-
expect(Activerecord::BatchTouching::VERSION).not_to be nil
|
11
|
-
end
|
12
|
-
|
13
|
-
it "touch returns true when not in a batch_touching block" do
|
14
|
-
expect(owner.touch).to equal(true)
|
15
|
-
end
|
16
|
-
|
17
|
-
it "touch returns true in a batch_touching block" do
|
18
|
-
ActiveRecord::Base.transaction do
|
19
|
-
expect(owner.touch).to equal(true)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
it "consolidates touches on a single record when inside a transaction" do
|
24
|
-
expect_updates [{ "owners" => { ids: owner } }] do
|
25
|
-
ActiveRecord::Base.transaction do
|
26
|
-
owner.touch
|
27
|
-
owner.touch
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
it "calls touch callbacks just once when there are multiple touches" do
|
33
|
-
expect(owner).to receive(:_run_touch_callbacks).once.and_call_original
|
34
|
-
ActiveRecord::Base.transaction do
|
35
|
-
owner.touch
|
36
|
-
owner.touch
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'sets updated_at on the in-memory instance when it eventually touches the record' do
|
41
|
-
original_time = new_time = nil
|
42
|
-
|
43
|
-
Timecop.freeze(2014, 7, 4, 12, 0, 0) do
|
44
|
-
original_time = Time.current
|
45
|
-
owner.touch
|
46
|
-
end
|
47
|
-
|
48
|
-
Timecop.freeze(2014, 7, 10, 12, 0, 0) do
|
49
|
-
new_time = Time.current
|
50
|
-
ActiveRecord::Base.transaction do
|
51
|
-
owner.touch
|
52
|
-
expect(owner.updated_at).to eq(original_time)
|
53
|
-
expect(owner.changed?).to be_falsey
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
expect(owner.updated_at).to eq(new_time)
|
58
|
-
expect(owner.changed?).to be_falsey
|
59
|
-
end
|
60
|
-
|
61
|
-
it "does not mark the instance as changed when touch is called" do
|
62
|
-
ActiveRecord::Base.transaction do
|
63
|
-
owner.touch
|
64
|
-
expect(owner).not_to be_changed
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
it "does not mark the instance as changed, even if its lock_version is incremented" do
|
69
|
-
ActiveRecord::Base.transaction do
|
70
|
-
car.touch
|
71
|
-
end
|
72
|
-
expect(car).not_to be_changed
|
73
|
-
end
|
74
|
-
|
75
|
-
it "consolidates touches for all instances in a single table" do
|
76
|
-
expect_updates [{ "pets" => { ids: [pet1, pet2] } }, "owners" => { ids: owner }] do
|
77
|
-
ActiveRecord::Base.transaction do
|
78
|
-
pet1.touch
|
79
|
-
pet2.touch
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
it "does nothing if no_touching is on" do
|
85
|
-
expect(owner).to receive(:_run_touch_callbacks).never
|
86
|
-
expect_updates [] do
|
87
|
-
ActiveRecord::Base.no_touching do
|
88
|
-
ActiveRecord::Base.transaction do
|
89
|
-
owner.touch
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
it "only applies touches for which no_touching is off" do
|
96
|
-
expect(owner).to receive(:_run_touch_callbacks).never
|
97
|
-
expect(pet1).to receive(:_run_touch_callbacks).once.and_call_original
|
98
|
-
expect_updates ["pets" => { ids: pet1 }] do
|
99
|
-
Owner.no_touching do
|
100
|
-
ActiveRecord::Base.transaction do
|
101
|
-
owner.touch
|
102
|
-
pet1.touch
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
it "does not apply nested touches if no_touching was turned on inside batch_touching" do
|
109
|
-
expect(owner).to receive(:_run_touch_callbacks).once.and_call_original
|
110
|
-
expect(pet1).to receive(:_run_touch_callbacks).never
|
111
|
-
expect_updates ["owners" => { ids: owner }] do
|
112
|
-
ActiveRecord::Base.transaction do
|
113
|
-
owner.touch
|
114
|
-
ActiveRecord::Base.no_touching do
|
115
|
-
pet1.touch
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
it "can update nonstandard columns" do
|
122
|
-
expect_updates ["owners" => { ids: owner, columns: ["updated_at", "happy_at"] }] do
|
123
|
-
ActiveRecord::Base.transaction do
|
124
|
-
owner.touch :happy_at
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
it "treats string column names and symbol column names as the same" do
|
130
|
-
expect_updates ["owners" => { ids: owner, columns: ["updated_at", "happy_at"] }] do
|
131
|
-
ActiveRecord::Base.transaction do
|
132
|
-
owner.touch :happy_at
|
133
|
-
owner.touch "happy_at"
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
it "splits up nonstandard column touches and standard column touches" do
|
139
|
-
owner2 = Owner.create name: "Guybrush"
|
140
|
-
|
141
|
-
expect_updates [{ "owners" => { ids: owner, columns: ["updated_at", "happy_at"] } },
|
142
|
-
{ "owners" => { ids: owner2, columns: ["updated_at"] } }] do
|
143
|
-
|
144
|
-
ActiveRecord::Base.transaction do
|
145
|
-
owner.touch :happy_at
|
146
|
-
owner2.touch
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
it "can update multiple nonstandard columns of a single record in different calls to touch" do
|
152
|
-
expect_updates [{ "owners" => { ids: owner, columns: ["updated_at", "happy_at"] } },
|
153
|
-
{ "owners" => { ids: owner, columns: ["updated_at", "sad_at"] } }] do
|
154
|
-
|
155
|
-
ActiveRecord::Base.transaction do
|
156
|
-
owner.touch :happy_at
|
157
|
-
owner.touch :sad_at
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
it "can update multiple nonstandard columns of a single record in a single call to touch" do
|
163
|
-
expect_updates [{ "owners" => { ids: owner, columns: [ "updated_at", "happy_at", "sad_at"] } }] do
|
164
|
-
|
165
|
-
ActiveRecord::Base.transaction do
|
166
|
-
owner.touch :happy_at, :sad_at
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
it "consolidates touch: true touches" do
|
172
|
-
expect_updates [{ "pets" => { ids: [pet1, pet2] } }, { "owners" => { ids: owner } }] do
|
173
|
-
ActiveRecord::Base.transaction do
|
174
|
-
pet1.touch
|
175
|
-
pet2.touch
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
it "does not touch the owning record via touch: true if it was already touched explicitly" do
|
181
|
-
expect(owner).to receive(:_run_touch_callbacks).once.and_call_original
|
182
|
-
expect(pet1).to receive(:_run_touch_callbacks).once.and_call_original
|
183
|
-
expect(pet2).to receive(:_run_touch_callbacks).once.and_call_original
|
184
|
-
|
185
|
-
expect_updates [{ "pets" => { ids: [pet1, pet2] } }, { "owners" => { ids: owner } }] do
|
186
|
-
ActiveRecord::Base.transaction do
|
187
|
-
owner.touch
|
188
|
-
pet1.touch
|
189
|
-
pet2.touch
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
it "does not consolidate touches when outside a transaction" do
|
195
|
-
expect_updates [{ "owners" => { ids: owner } },
|
196
|
-
{ "owners" => { ids: owner } }] do
|
197
|
-
owner.touch
|
198
|
-
owner.touch
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
it "nested transactions get consolidated into a single set of touches" do
|
203
|
-
expect(owner).to receive(:_run_touch_callbacks).once.and_call_original
|
204
|
-
expect(pet1).to receive(:_run_touch_callbacks).once.and_call_original
|
205
|
-
expect(pet2).to receive(:_run_touch_callbacks).once.and_call_original
|
206
|
-
|
207
|
-
expect_updates [{ "pets" => { ids: [pet1, pet2] } }, { "owners" => { ids: owner } }] do
|
208
|
-
ActiveRecord::Base.transaction do
|
209
|
-
pet1.touch
|
210
|
-
ActiveRecord::Base.transaction do
|
211
|
-
pet2.touch
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
it "rolling back from a nested transaction without :requires_new touches the records in the inner transaction" do
|
218
|
-
expect_updates [{ "pets" => { ids: [pet1, pet2] } }, { "owners" => { ids: owner } }] do
|
219
|
-
ActiveRecord::Base.transaction do
|
220
|
-
pet1.touch
|
221
|
-
ActiveRecord::Base.transaction do
|
222
|
-
pet2.touch
|
223
|
-
raise ActiveRecord::Rollback
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
it "rolling back from a nested transaction with :requires_new does not touch the records in the inner transaction" do
|
230
|
-
expect_updates [{ "pets" => { ids: pet1 } }, { "owners" => { ids: owner } }] do
|
231
|
-
ActiveRecord::Base.transaction do
|
232
|
-
pet1.touch
|
233
|
-
ActiveRecord::Base.transaction(requires_new: true) do
|
234
|
-
pet2.touch
|
235
|
-
raise ActiveRecord::Rollback
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
it "touching a record in an outer and inner new transaction, then rolling back the inner one, still touches the record" do
|
242
|
-
expect_updates [{ "pets" => { ids: pet1 } }, { "owners" => { ids: owner } }] do
|
243
|
-
ActiveRecord::Base.transaction do
|
244
|
-
pet1.touch
|
245
|
-
ActiveRecord::Base.transaction(requires_new: true) do
|
246
|
-
pet1.touch
|
247
|
-
raise ActiveRecord::Rollback
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
it "rolling back from an outer transaction does not touch any records" do
|
254
|
-
expect_updates [] do
|
255
|
-
ActiveRecord::Base.transaction do
|
256
|
-
pet1.touch
|
257
|
-
ActiveRecord::Base.transaction do
|
258
|
-
pet2.touch :neutered_at
|
259
|
-
end
|
260
|
-
raise ActiveRecord::Rollback
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
it "consolidates touch: :column_name touches" do
|
266
|
-
pet_klass = Class.new(ActiveRecord::Base) do
|
267
|
-
def self.name; 'Pet'; end
|
268
|
-
belongs_to :owner, :touch => :happy_at
|
269
|
-
after_touch :after_touch_callback
|
270
|
-
def after_touch_callback; end
|
271
|
-
end
|
272
|
-
|
273
|
-
pet = pet_klass.first
|
274
|
-
owner = pet.owner
|
275
|
-
|
276
|
-
expect_updates [{ "owners" => { ids: owner, columns: ["updated_at", "happy_at"] } }, { "pets" => { ids: pet } }] do
|
277
|
-
ActiveRecord::Base.transaction do
|
278
|
-
pet.touch
|
279
|
-
pet.touch
|
280
|
-
end
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
it "keeps iterating as long as after_touch keeps causing more records to be touched" do
|
285
|
-
pet_klass = Class.new(ActiveRecord::Base) do
|
286
|
-
def self.name; 'Pet'; end
|
287
|
-
belongs_to :owner
|
288
|
-
|
289
|
-
# Touch the owner in after_touch instead of using touch: true
|
290
|
-
after_touch :touch_owner
|
291
|
-
def touch_owner; owner.touch; end
|
292
|
-
end
|
293
|
-
|
294
|
-
pet = pet_klass.first
|
295
|
-
owner = pet.owner
|
296
|
-
|
297
|
-
expect_updates [{ "owners" => { ids: owner } }, { "pets" => { ids: pet } }] do
|
298
|
-
ActiveRecord::Base.transaction do
|
299
|
-
pet.touch
|
300
|
-
end
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
it "increments the optimistic lock column in memory and in the DB" do
|
305
|
-
car1 = Car.create(name: "Ferrari", lock_version: 1)
|
306
|
-
car2 = Car.create(name: "Lambo", lock_version: 2)
|
307
|
-
|
308
|
-
ActiveRecord::Base.transaction do
|
309
|
-
car1.touch
|
310
|
-
car2.touch
|
311
|
-
end
|
312
|
-
|
313
|
-
expect(car1.lock_version).to equal(2)
|
314
|
-
expect(car2.lock_version).to equal(3)
|
315
|
-
|
316
|
-
expect(car1.reload.lock_version).to equal(2)
|
317
|
-
expect(car2.reload.lock_version).to equal(3)
|
318
|
-
end
|
319
|
-
|
320
|
-
private
|
321
|
-
|
322
|
-
def expect_updates(tables_ids_and_columns)
|
323
|
-
expected_sql = expected_sql_for(tables_ids_and_columns)
|
324
|
-
expect(ActiveRecord::Base.connection).to receive(:update).exactly(expected_sql.length).times do |stmt, _, _|
|
325
|
-
if stmt.to_sql =~ /UPDATE /i
|
326
|
-
index = expected_sql.index { |expected_stmt| stmt.to_sql =~ expected_stmt }
|
327
|
-
expect(index).to be, "An unexpected touch occurred: #{stmt.to_sql}"
|
328
|
-
expected_sql.delete_at(index)
|
329
|
-
end
|
330
|
-
end
|
331
|
-
|
332
|
-
yield
|
333
|
-
|
334
|
-
expect(expected_sql).to be_empty, "Some of the expected updates were not executed."
|
335
|
-
end
|
336
|
-
|
337
|
-
# Creates an array of regular expressions to match the SQL statements that we expect
|
338
|
-
# to execute.
|
339
|
-
#
|
340
|
-
# Each element in the tables_ids_and_columns array is in this form:
|
341
|
-
#
|
342
|
-
# { "table_name" => { ids: id_or_array_of_ids, columns: column_name_or_array } }
|
343
|
-
#
|
344
|
-
# 'columns' is optional. If it's missing it is assumed that "updated_at" is the only
|
345
|
-
# column that gets touched.
|
346
|
-
def expected_sql_for(tables_ids_and_columns)
|
347
|
-
tables_ids_and_columns.map do |entry|
|
348
|
-
entry.map do |table, options|
|
349
|
-
ids = Array.wrap(options[:ids])
|
350
|
-
columns = Array.wrap(options[:columns]).presence || ["updated_at"]
|
351
|
-
columns = columns.sort
|
352
|
-
Regexp.new(touch_sql(table, columns, ids))
|
353
|
-
end
|
354
|
-
end.flatten
|
355
|
-
end
|
356
|
-
|
357
|
-
# in: array of records or record ids
|
358
|
-
# out: "( = 1|= \?|= \$1)" or " IN (1, 2)"
|
359
|
-
#
|
360
|
-
# In some cases, such as SQLite3 when outside a transaction, the logged SQL uses ? instead of record ids.
|
361
|
-
def ids_sql(ids)
|
362
|
-
ids = ids.map { |id| id.class.respond_to?(:primary_key) ? id.send(id.class.primary_key) : id }
|
363
|
-
ids.length > 1 ? %{ IN \\(#{Array.new(ids.length, '\?').join(", ")}\\)} : %{( = #{ids.first}|= \\?|= \\$1)}
|
364
|
-
end
|
365
|
-
|
366
|
-
def touch_sql(table, columns, ids)
|
367
|
-
%{UPDATE \\"#{table}"\\ SET #{columns.map { |column| %{\\"#{column}\\" =.+} }.join(", ") } .+#{ids_sql(ids)}\\Z}
|
368
|
-
end
|
369
|
-
end
|
data/spec/rcov_exclude_list.rb
DELETED
data/spec/spec_helper.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
if ENV["COVERAGE"]
|
2
|
-
require_relative 'rcov_exclude_list.rb'
|
3
|
-
exlist = Dir.glob(@exclude_list)
|
4
|
-
require 'simplecov'
|
5
|
-
require 'simplecov-rcov'
|
6
|
-
SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
|
7
|
-
SimpleCov.start do
|
8
|
-
exlist.each do |p|
|
9
|
-
add_filter p
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
15
|
-
require 'active_record'
|
16
|
-
require 'activerecord/batch_touching'
|
17
|
-
require 'timecop'
|
18
|
-
|
19
|
-
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
20
|
-
|
21
|
-
load File.dirname(__FILE__) + '/support/schema.rb'
|
22
|
-
require File.dirname(__FILE__) + '/support/models.rb'
|
data/spec/support/models.rb
DELETED
data/spec/support/schema.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
ActiveRecord::Schema.define do
|
2
|
-
self.verbose = false
|
3
|
-
|
4
|
-
create_table :owners, :force => true do |t|
|
5
|
-
t.string :name
|
6
|
-
|
7
|
-
t.timestamps
|
8
|
-
t.datetime :happy_at
|
9
|
-
t.datetime :sad_at
|
10
|
-
end
|
11
|
-
|
12
|
-
create_table :pets, :force => true do |t|
|
13
|
-
t.string :name
|
14
|
-
t.integer :owner_id
|
15
|
-
t.datetime :neutered_at
|
16
|
-
|
17
|
-
t.timestamps
|
18
|
-
end
|
19
|
-
|
20
|
-
create_table :cars, force: true do |t|
|
21
|
-
t.string :name
|
22
|
-
t.column :lock_version, :integer, null: false, default: 0
|
23
|
-
|
24
|
-
t.timestamps
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|