activerecord-duplicator 0.3.0 → 0.5.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: ee612e84f80aefec6371c6e900d058e9934b7b7d095a981e2f52d22291586210
4
- data.tar.gz: 2aafb86816d74188a18a82e36c74e1a6c1dcb65b52523c3ee4bafa9db45e1fa3
3
+ metadata.gz: 017f95574ff90a7fdae2b54b38fd5be60fd6afa33b53a3a59486e159031c9bd1
4
+ data.tar.gz: a06425a620bae40393fc97b555f0616a8b5bfc87d6e90806089480f4506d3bdf
5
5
  SHA512:
6
- metadata.gz: f167f08df7e50cc605ff85d3663f3e51cb1b2a11ea88201bf50726b20721e263b8b9f96fee38ad64e42b97cd895f2217fb1968760bdf085351959632c7fec0d1
7
- data.tar.gz: 652d33cc38a670dc0eafad296157d6f56c5fd3e4a435082b1bec45706c9a01624730be36992ebd22ac88f335a70def57aad3234480d77816297f879460462cda
6
+ metadata.gz: 6dd54a10072d5521a176d1d5957246e033a42a2671e220ffc718f53f6442c4d6329241d0ab41aa1cc852585c697a36666d629d9c5f60c03d5b7dede47f986a97
7
+ data.tar.gz: afc907dc6fb3026b820eb1273500c53f7d51f0bd8ea960092956f81108253faa3d4795473f1a137133cade12f24365a4f8fb8136ff5831a0196e1cc3526f4bad
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2026-07-07
4
+
5
+ ### Added
6
+
7
+ - `Session#mark_skip_id(klass, *ids)` and `HandlerApi#mark_skip_id` —
8
+ register specific ids as skipped without loading records. Equivalent to
9
+ calling `mark_skip` on each record but avoids the database round-trip
10
+ when the ids are already known. Composite primary key ids are passed as
11
+ Arrays.
12
+
13
+ ## [0.4.0] - 2026-07-06
14
+
15
+ ### Fixed
16
+
17
+ - `Duplicator#duplicate` now accepts `transaction_options: {}` and forwards
18
+ it to `Session#run`, which forwards it to `root_record.transaction`. In
19
+ v0.3.0 this plumbing was only wired at the Session#run layer, so callers
20
+ using the documented `Duplicator#duplicate` entry point had no way to
21
+ override the outer transaction options.
22
+
3
23
  ## [0.3.0] - 2026-07-06
4
24
 
5
25
  ### Changed (breaking)
data/README.md CHANGED
@@ -97,6 +97,28 @@ working), but the default `bulk_insert` path becomes a no-op and any
97
97
  echoes the old id back unchanged. `mark_skip_class` only differs from
98
98
  per-record `mark_skip` in cost: no enumeration is needed to opt each row in.
99
99
 
100
+ ### Skipping by id without loading records (`mark_skip_id`)
101
+
102
+ When the ids to skip are already known (a foreign key column, a prior
103
+ pluck, a config constant), use `mark_skip_id` to register them directly
104
+ without issuing a `SELECT`:
105
+
106
+ ```ruby
107
+ duplicator.duplicate(old_post, associations: [:taggings]) do |session|
108
+ session.mark_skip_id(Tag, 1, 2, 3)
109
+ end
110
+ ```
111
+
112
+ For composite primary keys pass each id as an Array:
113
+
114
+ ```ruby
115
+ session.mark_skip_id(Item, [tenant_id, 10], [tenant_id, 11])
116
+ ```
117
+
118
+ `mark_skip_id` has the same effect as calling `mark_skip` on the
119
+ corresponding records: `fetch_new_id` returns the old id unchanged for
120
+ each registered id.
121
+
100
122
  ### Registering an id from outside (`store_new_id` / `fetch_new_id`)
101
123
 
102
124
  If you produce a duplicate yourself (for example a model with a uniqueness
@@ -19,6 +19,11 @@ module ActiveRecord
19
19
  @session.mark_skip(...)
20
20
  end
21
21
 
22
+ #: (Class, *untyped) -> void
23
+ def mark_skip_id(...)
24
+ @session.mark_skip_id(...)
25
+ end
26
+
22
27
  #: (*Class) -> void
23
28
  def mark_skip_class(...)
24
29
  @session.mark_skip_class(...)
@@ -45,9 +45,7 @@ module ActiveRecord
45
45
  # Duplicator-level handler is intentional and does not raise.
46
46
  #: (Class) { (HandlerApi, Class, untyped) -> void } -> void
47
47
  def on(klass, &block)
48
- if @user_handlers.key?(klass)
49
- raise DuplicateHandlerError, "handler for #{klass} is already overridden in this session"
50
- end
48
+ raise DuplicateHandlerError, "handler for #{klass} is already overridden in this session" if @user_handlers.key?(klass)
51
49
 
52
50
  @user_handlers[klass] = block
53
51
  end
@@ -63,6 +61,16 @@ module ActiveRecord
63
61
  end
64
62
  end
65
63
 
64
+ # Declare that the given ids of the given class must not be duplicated,
65
+ # without loading the records. Equivalent to calling mark_skip on each
66
+ # record but avoids the database round-trip when the ids are already
67
+ # known. Each id should have the same shape as the class's primary key
68
+ # (scalar for a single-column pk, Array for a composite pk).
69
+ #: (Class, *untyped) -> void
70
+ def mark_skip_id(klass, *ids)
71
+ ids.each { |id| store_new_id(klass, old_id: id, new_id: id) }
72
+ end
73
+
66
74
  # Class-wide equivalent of mark_skip: it is exactly as if every record of
67
75
  # each given class had been passed to mark_skip. Handlers still fire
68
76
  # (matching the mark_skip case where a handler for the class is invoked
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  class Duplicator
5
- VERSION = "0.3.0"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
@@ -63,11 +63,15 @@ module ActiveRecord
63
63
  # If a block is given it is called with the new Session before duplication
64
64
  # starts, letting the caller mark_skip or store_new_id for records that
65
65
  # were duplicated elsewhere.
66
- #: (ActiveRecord::Base, ?associations: untyped) ?{ (Session) -> void } -> ActiveRecord::Base
67
- def duplicate(root_record, associations: [], &block)
66
+ #
67
+ # transaction_options is forwarded verbatim to Session#run, which forwards
68
+ # it to `root_record.transaction`. Default is `{}` (plain
69
+ # `transaction do ... end`).
70
+ #: (ActiveRecord::Base, ?associations: untyped, ?transaction_options: Hash[Symbol, untyped]) ?{ (Session) -> void } -> ActiveRecord::Base
71
+ def duplicate(root_record, associations: [], transaction_options: {}, &block)
68
72
  session = Session.new(handlers: @handlers, skipped_classes: @skipped_classes)
69
73
  block&.call(session)
70
- session.run(root_record, associations:)
74
+ session.run(root_record, associations:, transaction_options:)
71
75
  end
72
76
  end
73
77
  end
@@ -15,6 +15,9 @@ module ActiveRecord
15
15
  # : (*ActiveRecord::Base) -> void
16
16
  def mark_skip: (*ActiveRecord::Base) -> void
17
17
 
18
+ # : (Class, *untyped) -> void
19
+ def mark_skip_id: (Class, *untyped) -> void
20
+
18
21
  # : (*Class) -> void
19
22
  def mark_skip_class: (*Class) -> void
20
23
 
@@ -47,6 +47,14 @@ module ActiveRecord
47
47
  # : (*ActiveRecord::Base) -> void
48
48
  def mark_skip: (*ActiveRecord::Base) -> void
49
49
 
50
+ # Declare that the given ids of the given class must not be duplicated,
51
+ # without loading the records. Equivalent to calling mark_skip on each
52
+ # record but avoids the database round-trip when the ids are already
53
+ # known. Each id should have the same shape as the class's primary key
54
+ # (scalar for a single-column pk, Array for a composite pk).
55
+ # : (Class, *untyped) -> void
56
+ def mark_skip_id: (Class, *untyped) -> void
57
+
50
58
  # Class-wide equivalent of mark_skip: it is exactly as if every record of
51
59
  # each given class had been passed to mark_skip. Handlers still fire
52
60
  # (matching the mark_skip case where a handler for the class is invoked
@@ -49,7 +49,11 @@ module ActiveRecord
49
49
  # If a block is given it is called with the new Session before duplication
50
50
  # starts, letting the caller mark_skip or store_new_id for records that
51
51
  # were duplicated elsewhere.
52
- # : (ActiveRecord::Base, ?associations: untyped) ?{ (Session) -> void } -> ActiveRecord::Base
53
- def duplicate: (ActiveRecord::Base, ?associations: untyped) ?{ (Session) -> void } -> ActiveRecord::Base
52
+ #
53
+ # transaction_options is forwarded verbatim to Session#run, which forwards
54
+ # it to `root_record.transaction`. Default is `{}` (plain
55
+ # `transaction do ... end`).
56
+ # : (ActiveRecord::Base, ?associations: untyped, ?transaction_options: Hash[Symbol, untyped]) ?{ (Session) -> void } -> ActiveRecord::Base
57
+ def duplicate: (ActiveRecord::Base, ?associations: untyped, ?transaction_options: Hash[Symbol, untyped]) ?{ (Session) -> void } -> ActiveRecord::Base
54
58
  end
55
59
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-duplicator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SmartHR