mongo_trails 17.0.0 → 17.1.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 +4 -4
- data/README.md +15 -0
- data/gemfiles/rails_7.gemfile.lock +2 -2
- data/gemfiles/rails_8.gemfile.lock +2 -2
- data/gemfiles/rails_8_1.gemfile.lock +1 -1
- data/gemfiles/rails_8_paper_trail_16.gemfile.lock +2 -2
- data/lib/mongo_trails/config.rb +5 -1
- data/lib/mongo_trails/mongo_support/version.rb +74 -11
- data/lib/mongo_trails/mongo_support/write_version_worker.rb +5 -1
- data/lib/mongo_trails/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 56dbb9f210528411b5b75400afafe8cedb67a22c9fe815534cf5702ffde9dfc0
|
|
4
|
+
data.tar.gz: 1b7e64ade5beddbaedb94c28ce4643373314994776e94a67e4af4b2340773b53
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 87875ed778feadc9e4d0b36716e12beb84fddf893aae69b222140cbaccf2dfb9ad600c2ef12da86d76c5c1340b25308ba1c138e13d07cfa11ca23f2a10175159
|
|
7
|
+
data.tar.gz: f034ef6b1babac2f7952d12865730e9bf7cbc1c5bcd00f8f1e570d5ad7ae2773c83aaec8167e40d3341b2670d709854ae8973191de018e63ee13343f3b5883d8
|
data/README.md
CHANGED
|
@@ -43,6 +43,21 @@ Done!
|
|
|
43
43
|
`PaperTrail.config.sidekiq_worker = PaperTrail::WriteVersionWorker` - Worker class
|
|
44
44
|
`PaperTrail.config.sidekiq_options = { queue: :default }` - Options for Sidekiq
|
|
45
45
|
|
|
46
|
+
`PaperTrail.config.enable_integer_id = true` - Give every version a sequential `integer_id` from a per-collection counter, and order previous/next versions by it. With `false` no counter is used, `integer_id` stays empty and versions are ordered by `created_at`. The first write to a collection with no counter scans the whole collection for its highest `integer_id`, which is expensive on large collections (and on DocumentDB, where no index serves it).
|
|
47
|
+
|
|
48
|
+
`PaperTrail.config.on_write_error = nil` - A callable receiving `(error, version)` when a version write fails because MongoDB is unreachable or drops the connection (`MongoTrails::Version::WRITE_ERRORS`). Inside a transaction versions are written from the after-commit callbacks, so a raising write skips every after-commit callback behind it; a handler lets them run. Without a handler the error raises. Other errors always raise.
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
PaperTrail.config.on_write_error = lambda do |error, version|
|
|
52
|
+
ErrorReporter.report(error, item_type: version.item_type, item_id: version.item_id)
|
|
53
|
+
version.enqueue_write unless PaperTrail.config.enable_sidekiq # retry through Sidekiq
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`PaperTrail::WriteVersionWorker` rebuilds the queued version with `MongoTrails::Version.rebuild` (its `_id`, `created_at` and metadata are kept, `#initialize` is not run again), and treats a version whose `_id` is already stored as written, so a retried write is safe.
|
|
58
|
+
|
|
59
|
+
`MongoTrails::Version.bulk_insert(versions)` - Inserts many versions in one `insert_many`, allocating their `integer_id`s in one counter update when `enable_integer_id` is on.
|
|
60
|
+
|
|
46
61
|
## Releasing
|
|
47
62
|
|
|
48
63
|
Update `MongoTrails::VERSION` and all appraisal lockfiles, then validate and release:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
mongo_trails (17.
|
|
4
|
+
mongo_trails (17.1.0)
|
|
5
5
|
mongoid (>= 7, < 10)
|
|
6
6
|
ostruct
|
|
7
7
|
paper_trail (>= 16, < 18)
|
|
@@ -172,7 +172,7 @@ CHECKSUMS
|
|
|
172
172
|
mcp (0.8.0) sha256=ae8bd146bb8e168852866fd26f805f52744f6326afb3211e073f78a95e0c34fb
|
|
173
173
|
minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
|
|
174
174
|
mongo (2.25.0) sha256=b4663884d23523d24f50c579846956444410f939093e8c2a95fa4b76bb929053
|
|
175
|
-
mongo_trails (17.
|
|
175
|
+
mongo_trails (17.1.0)
|
|
176
176
|
mongoid (9.1.0) sha256=ef7b50f4c84e636850c3431bc3c92f98481f4e1d147ff06a1c21242d0b242a9e
|
|
177
177
|
ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
|
|
178
178
|
paper_trail (17.0.0) sha256=1c2842061d3874ca7015908e821e2aa14f9b982af2acb2a7974713bf79021c85
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
mongo_trails (17.
|
|
4
|
+
mongo_trails (17.1.0)
|
|
5
5
|
mongoid (>= 7, < 10)
|
|
6
6
|
ostruct
|
|
7
7
|
paper_trail (>= 16, < 18)
|
|
@@ -172,7 +172,7 @@ CHECKSUMS
|
|
|
172
172
|
mcp (0.8.0) sha256=ae8bd146bb8e168852866fd26f805f52744f6326afb3211e073f78a95e0c34fb
|
|
173
173
|
minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
|
|
174
174
|
mongo (2.25.0) sha256=b4663884d23523d24f50c579846956444410f939093e8c2a95fa4b76bb929053
|
|
175
|
-
mongo_trails (17.
|
|
175
|
+
mongo_trails (17.1.0)
|
|
176
176
|
mongoid (9.1.0) sha256=ef7b50f4c84e636850c3431bc3c92f98481f4e1d147ff06a1c21242d0b242a9e
|
|
177
177
|
ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
|
|
178
178
|
paper_trail (17.0.0) sha256=1c2842061d3874ca7015908e821e2aa14f9b982af2acb2a7974713bf79021c85
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
mongo_trails (17.
|
|
4
|
+
mongo_trails (17.1.0)
|
|
5
5
|
mongoid (>= 7, < 10)
|
|
6
6
|
ostruct
|
|
7
7
|
paper_trail (>= 16, < 18)
|
|
@@ -175,7 +175,7 @@ CHECKSUMS
|
|
|
175
175
|
mcp (0.8.0) sha256=ae8bd146bb8e168852866fd26f805f52744f6326afb3211e073f78a95e0c34fb
|
|
176
176
|
minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
|
|
177
177
|
mongo (2.25.0) sha256=b4663884d23523d24f50c579846956444410f939093e8c2a95fa4b76bb929053
|
|
178
|
-
mongo_trails (17.
|
|
178
|
+
mongo_trails (17.1.0)
|
|
179
179
|
mongoid (9.1.0) sha256=ef7b50f4c84e636850c3431bc3c92f98481f4e1d147ff06a1c21242d0b242a9e
|
|
180
180
|
ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
|
|
181
181
|
paper_trail (16.0.0) sha256=e9b9f0fb1b8b590c8231cfa931b282ba92f90e066e393930a5e1c61ae4c5019d
|
data/lib/mongo_trails/config.rb
CHANGED
|
@@ -8,7 +8,9 @@ module PaperTrail
|
|
|
8
8
|
:enable_sidekiq,
|
|
9
9
|
:sidekiq_worker,
|
|
10
10
|
:sidekiq_options,
|
|
11
|
-
:mongo_trails_config
|
|
11
|
+
:mongo_trails_config,
|
|
12
|
+
:enable_integer_id,
|
|
13
|
+
:on_write_error
|
|
12
14
|
)
|
|
13
15
|
|
|
14
16
|
def initialize
|
|
@@ -19,6 +21,8 @@ module PaperTrail
|
|
|
19
21
|
@has_paper_trail_defaults = {}
|
|
20
22
|
@enable_sidekiq = false
|
|
21
23
|
@sidekiq_options = { queue: :default, retry: true, backtrace: false }
|
|
24
|
+
@enable_integer_id = true
|
|
25
|
+
@on_write_error = nil
|
|
22
26
|
end
|
|
23
27
|
end
|
|
24
28
|
end
|
|
@@ -24,9 +24,35 @@ PaperTrail.config.has_paper_trail_defaults = { versions: { class_name: 'MongoTra
|
|
|
24
24
|
|
|
25
25
|
module MongoTrails
|
|
26
26
|
class Version
|
|
27
|
+
# Errors a version write can raise when MongoDB is unreachable or drops the connection. Only these reach
|
|
28
|
+
# PaperTrail.config.on_write_error; anything else (a validation error, a bug) still raises.
|
|
29
|
+
WRITE_ERRORS = [Mongo::Error, IOError, SystemCallError, Timeout::Error].freeze
|
|
30
|
+
DUPLICATE_KEY_ERROR_CODE = 11_000
|
|
31
|
+
|
|
27
32
|
class << self
|
|
28
33
|
def find(id)
|
|
29
|
-
find_by(integer_id: id)
|
|
34
|
+
integer_id_enabled? ? find_by(integer_id: id) : super
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def integer_id_enabled?
|
|
38
|
+
PaperTrail.config.enable_integer_id != false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# PaperTrail orders previous/next versions by primary key when it is an integer, which here means integer_id.
|
|
42
|
+
# Without integer_id they are ordered by created_at.
|
|
43
|
+
def primary_key_is_int?
|
|
44
|
+
integer_id_enabled?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Rebuilds a version from the JSON of one that was already built (see #enqueue_write), keeping its _id,
|
|
48
|
+
# created_at and metadata. It does not run #initialize, so build-time defaults are not applied a second time.
|
|
49
|
+
def rebuild(attributes)
|
|
50
|
+
document = attributes.to_h.stringify_keys.to_h { |name, value| [name, from_json_value(name, value)] }
|
|
51
|
+
instantiate(document).tap { |version| version.new_record = true }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def duplicate_key_error?(error)
|
|
55
|
+
error.respond_to?(:code) && error.code == DUPLICATE_KEY_ERROR_CODE
|
|
30
56
|
end
|
|
31
57
|
|
|
32
58
|
def incrementing_fields
|
|
@@ -62,6 +88,17 @@ module MongoTrails
|
|
|
62
88
|
((max_integer_id - count + 1)..max_integer_id).to_a
|
|
63
89
|
end
|
|
64
90
|
|
|
91
|
+
def bulk_insert(versions)
|
|
92
|
+
return [] if versions.empty?
|
|
93
|
+
|
|
94
|
+
documents = versions.map { |version| bulk_insert_attributes(version) }
|
|
95
|
+
if integer_id_enabled?
|
|
96
|
+
integer_ids = next_integer_ids(documents.count { |document| document['integer_id'].nil? })
|
|
97
|
+
documents.each { |document| document['integer_id'] ||= integer_ids.shift }
|
|
98
|
+
end
|
|
99
|
+
collection.insert_many(documents)
|
|
100
|
+
end
|
|
101
|
+
|
|
65
102
|
def bulk_insert_attributes(version)
|
|
66
103
|
version = version.is_a?(self) ? version : new(version)
|
|
67
104
|
attrs = version.attributes.dup
|
|
@@ -91,6 +128,17 @@ module MongoTrails
|
|
|
91
128
|
|
|
92
129
|
private
|
|
93
130
|
|
|
131
|
+
# as_json turns ObjectIds and times into strings; every other field type survives JSON as is.
|
|
132
|
+
def from_json_value(name, value)
|
|
133
|
+
return value unless value.is_a?(String)
|
|
134
|
+
|
|
135
|
+
type = fields[name]&.type
|
|
136
|
+
return BSON::ObjectId.legal?(value) ? BSON::ObjectId.from_string(value) : value if type == BSON::ObjectId
|
|
137
|
+
return (Time.zone || Time).parse(value) if [Time, DateTime].include?(type)
|
|
138
|
+
|
|
139
|
+
value
|
|
140
|
+
end
|
|
141
|
+
|
|
94
142
|
def counter_key(scope)
|
|
95
143
|
"#{name}:#{scope || prefix_map}"
|
|
96
144
|
end
|
|
@@ -152,6 +200,17 @@ module MongoTrails
|
|
|
152
200
|
schedule_version_persistence(bang: true)
|
|
153
201
|
end
|
|
154
202
|
|
|
203
|
+
# Queues the write of this version for PaperTrail::WriteVersionWorker (or the configured sidekiq_worker).
|
|
204
|
+
def enqueue_write
|
|
205
|
+
worker = defined?(PaperTrail.config.sidekiq_worker.queue) ? PaperTrail.config.sidekiq_worker : PaperTrail::WriteVersionWorker
|
|
206
|
+
args = attributes.as_json
|
|
207
|
+
if worker == PaperTrail::WriteVersionWorker
|
|
208
|
+
worker.set(PaperTrail.config.sidekiq_options).perform_async(args)
|
|
209
|
+
else
|
|
210
|
+
worker.perform_async(args)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
155
214
|
def initialize(data)
|
|
156
215
|
data = data.to_h.deep_symbolize_keys
|
|
157
216
|
item = data.delete(:item)
|
|
@@ -236,6 +295,8 @@ module MongoTrails
|
|
|
236
295
|
end
|
|
237
296
|
|
|
238
297
|
def assign_integer_id
|
|
298
|
+
return unless self.class.integer_id_enabled?
|
|
299
|
+
|
|
239
300
|
self.integer_id ||= self.class.next_integer_id
|
|
240
301
|
end
|
|
241
302
|
|
|
@@ -248,10 +309,19 @@ module MongoTrails
|
|
|
248
309
|
current_time
|
|
249
310
|
end
|
|
250
311
|
|
|
312
|
+
# Inside a transaction this runs from VersionCommitWrap#committed!, among the source record's after-commit
|
|
313
|
+
# callbacks, so an error raised here skips every callback queued behind it. PaperTrail.config.on_write_error
|
|
314
|
+
# receives the connection errors instead of letting them raise; without a handler they raise as before.
|
|
251
315
|
def persist_version(version, bang: false)
|
|
252
|
-
return version.
|
|
316
|
+
return version.enqueue_write if sidekiq_enabled?
|
|
253
317
|
|
|
254
318
|
bang ? version.save! : version.save
|
|
319
|
+
rescue *WRITE_ERRORS => e
|
|
320
|
+
handler = PaperTrail.config.on_write_error
|
|
321
|
+
raise unless handler
|
|
322
|
+
|
|
323
|
+
handler.call(e, version)
|
|
324
|
+
nil
|
|
255
325
|
end
|
|
256
326
|
|
|
257
327
|
def parse_time(value)
|
|
@@ -273,14 +343,7 @@ module MongoTrails
|
|
|
273
343
|
@parser ||= URI::Parser.new
|
|
274
344
|
end
|
|
275
345
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
args = attributes.as_json
|
|
279
|
-
if worker == PaperTrail::WriteVersionWorker
|
|
280
|
-
worker.set(PaperTrail.config.sidekiq_options).perform_async(args)
|
|
281
|
-
else
|
|
282
|
-
worker.perform_async(args)
|
|
283
|
-
end
|
|
284
|
-
end
|
|
346
|
+
# Kept for callers of the old private name; use #enqueue_write.
|
|
347
|
+
alias async_save! enqueue_write
|
|
285
348
|
end
|
|
286
349
|
end
|
|
@@ -4,8 +4,12 @@ module PaperTrail
|
|
|
4
4
|
class WriteVersionWorker
|
|
5
5
|
include Sidekiq::Job
|
|
6
6
|
|
|
7
|
+
# The job carries a version that was already built, so it is rebuilt as is rather than built again. A retry after a
|
|
8
|
+
# write whose acknowledgement was lost finds its _id already stored: the version is written, so the job is done.
|
|
7
9
|
def perform(obj)
|
|
8
|
-
MongoTrails::Version.
|
|
10
|
+
MongoTrails::Version.rebuild(obj).save!
|
|
11
|
+
rescue Mongo::Error::OperationFailure => e
|
|
12
|
+
raise unless MongoTrails::Version.duplicate_key_error?(e)
|
|
9
13
|
end
|
|
10
14
|
end
|
|
11
15
|
end
|
data/lib/mongo_trails/version.rb
CHANGED