inline_forms 8.1.15 → 8.1.17

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: 58e6670831f8bce627752b9797371fb20d2f3b14f571a4bc8c879478b71f5ff6
4
- data.tar.gz: 32fd5f2f3503126e9113d10a3bea9ba23468667f11cc4db8252d78078b101af8
3
+ metadata.gz: b7336d7c761e22dd4378a0c2d01b54f7558a2a29d9818a085b4478c17e664ead
4
+ data.tar.gz: 3e08954932efda044a18cf4f4a68ad3299ffdb382581c9369bc733023ff57906
5
5
  SHA512:
6
- metadata.gz: d9e59af7820b05e5ef046c1771a85f37ee1d7dd4ab9affbe0f8e2dfda03fe49db57d9452972406a889f699732165b8117bb56ecdee6c6a5edf9c05b0d14642f8
7
- data.tar.gz: 55be4d675321e511079de4a362688b664e65dce704dd56ef5929c8db2a286f0fef034312a2572016138808155a18e4f7150472a26aa904adb65e926d762da949
6
+ metadata.gz: 93339e380b5d224fe1b1d3b5459cc6675682ed81bd87f7a28594afafaa9304b50467dc8cb2b628641c78fb39ba68649c02fd9a38b154388815458e51e2781afe
7
+ data.tar.gz: e33e98b2676db0ec7d0af1196a1bf9be4b423b231d52f9636391fd830fdb29779a6e03381a5150bdde4258374ec13c4ad3b908f55cdd1e3ebf2d03ad86ac2cbc
data/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [8.1.17] - 2026-05-28
8
+
9
+ ### Fixed
10
+
11
+ - **`RecordNotUnique` on `photos.id` (and any primary record) when restoring a `destroy` version while the row still exists.** PaperTrail records a non-empty changeset for `destroy` events, so the versions panel shows a Restore link on `destroy` rows. Reverting a `destroy` reifies a record with the original primary key and `new_record? == true`; the normal undo (row currently deleted) path correctly INSERTs it, but once the row had already been restored a blind `save!` re-INSERTed the existing id and raised `SQLite3::ConstraintException: UNIQUE constraint failed: photos.id`. `revert` now upserts the primary record by PK (mirroring the 8.1.16 rich-text fix): when a row with that id already exists, the reified column values are copied onto it and saved in place, so restoring a `destroy` version is idempotent across repeated delete/undo cycles.
12
+
13
+ ### Added
14
+
15
+ - **Regression test:** restore a Photo `destroy` version while the row still exists (panel Restore / replayed undo) must not raise `RecordNotUnique`.
16
+
17
+ ## [8.1.16] - 2026-05-28
18
+
19
+ ### Fixed
20
+
21
+ - **`RecordNotUnique` on `action_text_rich_texts.id` when undoing a delete more than once.** Restoring rich text after parent undo used `reify.save!`, which INSERTs the old primary key. A second delete/undo cycle (or an already-restored row) hit SQLite's unique constraint. Restoration now upserts by `(record_type, record_id, name)` and copies `body` only, and only the latest PaperTrail `destroy` version per attribute is applied.
22
+
23
+ ### Added
24
+
25
+ - **Regression test:** nested Photo delete → undo → delete → undo.
26
+
7
27
  ## [8.1.15] - 2026-05-28
8
28
 
9
29
  ### Fixed
@@ -318,8 +318,7 @@ class InlineFormsController < ApplicationController
318
318
  @rich_text_record.save!
319
319
  @parent.touch if @parent.respond_to?(:touch)
320
320
  else
321
- @parent = @object
322
- @parent.save!
321
+ @parent = persist_reverted_primary!(@object)
323
322
  restore_rich_texts_for_reverted_parent!(@parent)
324
323
  @parent.reload
325
324
  end
@@ -328,26 +327,79 @@ class InlineFormsController < ApplicationController
328
327
 
329
328
  private
330
329
 
330
+ # Persist a reified *primary* record (Apartment, Photo, ...) for +revert+.
331
+ #
332
+ # +reify+ on a +destroy+ version returns a record with +new_record? == true+
333
+ # carrying the original primary key. The normal undo (row currently deleted)
334
+ # path INSERTs that id and is correct. But the versions panel also offers
335
+ # Restore on +destroy+ rows (their changeset is non-empty in apps that track
336
+ # +object_changes+ on destroy), and the post-delete undo banner can be
337
+ # replayed. Once the row has been restored, a blind +save!+ re-INSERTs the
338
+ # existing id and raises +RecordNotUnique+ (SQLite: +UNIQUE constraint failed:
339
+ # photos.id+). Mirror the rich-text upsert (8.1.16): when a row with that PK
340
+ # already exists, copy the reified column values onto it and +save!+ that
341
+ # instead, so reverting is idempotent across repeated delete/undo cycles.
342
+ def persist_reverted_primary!(object)
343
+ klass = object.class
344
+ primary_key = klass.primary_key
345
+
346
+ if object.new_record? && object.id.present? && klass.exists?(object.id)
347
+ existing = klass.find(object.id)
348
+ existing.assign_attributes(object.attributes.except(primary_key))
349
+ existing.save!
350
+ existing
351
+ else
352
+ object.save!
353
+ object
354
+ end
355
+ end
356
+
331
357
  # Undoing a parent +destroy+ reifies the Apartment/Photo row only. ActionText
332
358
  # bodies live in +action_text_rich_texts+ and get their own PaperTrail
333
359
  # +destroy+ versions; those rows are gone after +parent.destroy+, so we also
334
- # reify and save each matching +ActionText::RichText+ destroy snapshot.
360
+ # reify each matching +ActionText::RichText+ destroy snapshot.
361
+ #
362
+ # Use +find_or_initialize_by(record_type, record_id, name)+ and copy +body+
363
+ # only. Reified rows carry the old PK; +save!+ on a new record would INSERT
364
+ # that id and raise +RecordNotUnique+ on a second delete/undo cycle when the
365
+ # row already exists. Only the newest destroy version per attribute name is
366
+ # applied (repeat delete/undo leaves multiple RT destroy versions in the table).
335
367
  def restore_rich_texts_for_reverted_parent!(parent)
336
368
  return unless defined?(ActionText::RichText)
337
369
 
338
370
  record_type = parent.class.base_class.name
339
371
  record_id = parent.id
372
+ versions_by_name = {}
373
+
374
+ PaperTrail::Version
375
+ .where(item_type: "ActionText::RichText", event: "destroy")
376
+ .order(id: :desc)
377
+ .each do |version|
378
+ attrs = version.object_deserialized
379
+ next unless attrs.is_a?(Hash)
340
380
 
341
- PaperTrail::Version.where(item_type: "ActionText::RichText", event: "destroy").find_each do |version|
342
- attrs = version.object_deserialized
343
- next unless attrs.is_a?(Hash)
381
+ type = attrs["record_type"] || attrs[:record_type]
382
+ rid = attrs["record_id"] || attrs[:record_id]
383
+ next unless type == record_type && rid.to_i == record_id
344
384
 
345
- type = attrs["record_type"] || attrs[:record_type]
346
- rid = attrs["record_id"] || attrs[:record_id]
347
- next unless type == record_type && rid.to_i == record_id
385
+ name = (attrs["name"] || attrs[:name]).to_s
386
+ next if name.empty?
387
+
388
+ versions_by_name[name] ||= version
389
+ end
348
390
 
349
- rich_text = version.reify
350
- rich_text&.save!
391
+ versions_by_name.each_value do |version|
392
+ reified = version.reify
393
+ next unless reified
394
+
395
+ name = reified.name.to_s
396
+ record = ActionText::RichText.find_or_initialize_by(
397
+ record_type: record_type,
398
+ record_id: record_id,
399
+ name: name
400
+ )
401
+ record.body = reified.body if reified.respond_to?(:body)
402
+ record.save!
351
403
  end
352
404
  end
353
405
 
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module InlineForms
3
- VERSION = "8.1.15"
3
+ VERSION = "8.1.17"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inline_forms
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.15
4
+ version: 8.1.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ace Suares