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 +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +22 -0
- data/lib/active_record/duplicator/handler_api.rb +5 -0
- data/lib/active_record/duplicator/session.rb +11 -3
- data/lib/active_record/duplicator/version.rb +1 -1
- data/lib/active_record/duplicator.rb +7 -3
- data/sig/generated/active_record/duplicator/handler_api.rbs +3 -0
- data/sig/generated/active_record/duplicator/session.rbs +8 -0
- data/sig/generated/active_record/duplicator.rbs +6 -2
- 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: 017f95574ff90a7fdae2b54b38fd5be60fd6afa33b53a3a59486e159031c9bd1
|
|
4
|
+
data.tar.gz: a06425a620bae40393fc97b555f0616a8b5bfc87d6e90806089480f4506d3bdf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
@@ -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
|
|
@@ -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
|
-
|
|
67
|
-
|
|
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
|
-
#
|
|
53
|
-
|
|
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
|