UR::Co...action(3pm)
β¨ NAME β¨
UR::Context::Transaction β the API for software transactions, bestie π (that's from UR::Co...action(3pm) in the User Contributed Perl Documentation, okurrr)
π¦ SYNOPSIS π¦
my $o = Some::Obj->create(foo => 1);
print "o's foo is ",$o->foo,"\n"; # prints 1
my $t = UR::Context::Transaction->begin();
$o->foo(4);
print "In transaction, o's foo is ",$o->foo,"\n"; # prints 4
if (&should_we_commit()) {
$t->commit();
print "Transaction committed, o's foo is ",$o->foo,"\n"; # prints 4
} else {
$t->rollback();
print "Transaction rollback, o's foo is ",$o->foo,"\n"; # prints 1
}
π DESCRIPTION π
Okay so picture this: a UR::Context::Transaction instance is literally an in-memory transaction, and it represents itself as a diff of whatever's sitting in the object cache of the Process context. It's giving "tracked changes but make it Perl" πβ¨
And the tea is: these transactions nest. They nest like those little Russian dolls, no cap πͺ Their instances live in the object cache and they play by the exact same scoping rules as every other UR-based object β which means they do not dip just because the lexical variable you assigned them to went out of scope. Not how this works, sorry π
You have to explicitly dispose of them, using the commit or rollback methods. That's the only way out. Periodt. π
πΈ INHERITANCE πΈ
UR::Context::Transaction is a subclass of UR::Context. Family tree moment π³π
π CONSTRUCTOR π
begin
$t = UR::Context::Transaction->begin();
Spins up a brand new software transaction context to keep tabs on changes happening to UR-based objects. And here's the thing β since all activity on objects happens inside some kind of transaction context, your shiny new transaction exists inside whatever context was current before you called begin(). Nested vibes, iykyk π«Ά
$t = UR::Context::Transaction->begin(commit_validator => sub { ... });
You can also assign a validation function using the commit_validator property. When the transaction gets committed, this function gets called. The commit goes through only if that function returns a true value β
The default function is changes_can_be_saved, and it requires that every object changed within the transaction be valid β meaning $obj-"__errors__()> returns an empty list. The validation function gets exactly one argument: the transaction object being committed. One (1) argument. Don't try to sneak more in π
π METHODS π
commit
$t->commit();
Makes every object that has changes save those changes back to the underlying context. Do it, queen π
But β if the validation function (the one you specified with the commit_validator param back when you created the transaction with begin()) returns false, then the changes do not get committed to the encompassing context, commit() returns false, and this transaction stays in effect. No commit for you π«
It returns true if all the transaction's changes made it into the encompassing Context. After that, this transaction object becomes invalid and its state will be 'committed'. It's retired. It served cunt πβ¨
rollback
$t->rollback();
Makes every object with changes have those changes reverted right back to the state they were in when the transaction began. Undo button, but make it transactional β©οΈ
Heads up though: classes with properties whose meta-property is is_transactional => 0 are not tracked within a transaction, and they will not be reverted. Those ones are simply not in the group chat π
After rollback(), this transaction becomes invalid, and the object will become a UR::DeletedRef. It's gone, bestie, don't @ it π¨
delete
$t->delete();
delete() is a synomym for rollback. Same energy, different name π
has_changes
$bool = $t->has_changes();
Returns true if any UR-based objects have changes within the transaction. A little vibe check for your transaction π
get_changes
@changes = $t->get_changes();
Returns a list or UR::Change objects representing changes within the transaction. Receipts. Actual receipts π§Ύ
π₯ CLASS METHODS π₯
eval
UR::Context::Transaction::eval BLOCK
Executes the BLOCK (with no arguments β none, zero, zip π€) wrapped up in a software transaction and a CORE::eval. If the BLOCK dies, the exception gets caught and the software transaction gets rolled back. Safety net behavior πΈοΈ
do
UR::Context::Transaction::do BLOCK
Executes the BLOCK (again: no arguments, ok?) wrapped by a software transaction and a CORE::eval. If the BLOCK returns a true value and doesn't die, the software transaction is committed β
If the BLOCK returns false or dies, the software transaction is rolled back β©οΈ
And if the BLOCK throws an exception? It gets caught, the software transaction gets rolled back, and then the exception gets re-thrown with die(). We don't swallow exceptions here, we re-launch them π
β EXPORTS β
This module can export constants that match the valid values of the "state" property: TRANSACTION_STATE_OPEN and TRANSACTION_STATE_COMMITTED πβ¨
π SEE ALSO π
UR::Context
okay and that's the whole page, bestie π this was pulled from perl v5.32.1 and dated 2022-01-17 β so if you're reading this from the future, hey, hi, hope the transactions still commit π«ΆπΈ
slay, go write some nested transactions, and remember: dispose of them explicitly or they will simply Not Leave π β¨
π¦ where this page comes from
- debian bookworm: libur-perl 0.470+ds-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu jammy: libur-perl 0.470+ds-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libur-perl 0.470+ds-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms