From be301a9bbe565d3956ac74787408713ca164cef6 Mon Sep 17 00:00:00 2001
From: Hunter Perrin
Date: Fri, 14 Jun 2024 13:08:11 -0700
Subject: [PATCH] Add more information on properly using transactions in Nymph.
---
.../user-guide/creating-entities/+page.svelte | 6 +-
.../user-guide/entity-class/+page.svelte | 5 +-
.../user-guide/entity-querying/+page.svelte | 13 +-
.../user-guide/transactions/+page.svelte | 154 ++++++++++++++++++
4 files changed, 164 insertions(+), 14 deletions(-)
diff --git a/src/routes/user-guide/creating-entities/+page.svelte b/src/routes/user-guide/creating-entities/+page.svelte
index 0b49b69..581ec5d 100644
--- a/src/routes/user-guide/creating-entities/+page.svelte
+++ b/src/routes/user-guide/creating-entities/+page.svelte
@@ -39,7 +39,7 @@ if (someBlogPost.guid == null) {
await blogPost.$save();
let superPosts = await nymph.getEntities(
- { class: BlogPost.class },
+ { class: BlogPost },
{ type: '&', tag: 'super-post' }
);
@@ -106,8 +106,8 @@ console.log(entity.foo.bar); // Outputs undefined.`}
Nymph. Instead, it creates instances without data called "sleeping
references". You can use `$wake` on the entity or `$wakeAll` on the parent
entity to get the entity's data from the DB. The $wakeAll
- method will awaken all sleeping references in the entity's data. You can
- call $clearCache
+ method will awaken all sleeping references in the entity's data. You can call
+ $clearCache
in Node.js or $refresh
in the client to turn all the entities back
into sleeping references.
diff --git a/src/routes/user-guide/entity-class/+page.svelte b/src/routes/user-guide/entity-class/+page.svelte
index f2fa28f..b978e52 100644
--- a/src/routes/user-guide/entity-class/+page.svelte
+++ b/src/routes/user-guide/entity-class/+page.svelte
@@ -179,8 +179,9 @@ await nymph.deleteEntities([entity]);`}
if it's true, the function uses $equals
.
- $arraySearch
- Search an array for the entity and return the
- corresponding key. Takes two arguments, the array and a boolean
+ $arraySearch
- Search an array for the entity and return its
+ index, or -1
if it's not found. Takes two arguments, the
+ array and a boolean
strict
. If strict
is false or undefined, the
function uses
$is
diff --git a/src/routes/user-guide/entity-querying/+page.svelte b/src/routes/user-guide/entity-querying/+page.svelte
index cbec977..cdc9a9e 100644
--- a/src/routes/user-guide/entity-querying/+page.svelte
+++ b/src/routes/user-guide/entity-querying/+page.svelte
@@ -25,13 +25,12 @@
class
typeof Entity |
Entity |
- The class used to create each entity. It must have a factory static method that returns a new instance. |
+ The Entity class to query. |
limit |
@@ -247,7 +242,7 @@ if (cronUser.guid == null) {
>The named property contains the value (its JSON string is found
within the property's JSON string).
- {"{type: '&', array: ['foo', 'bar']}"} |
+ {"{type: '&', contain: ['foo', 'bar']}"} |
{"entity.foo = ['bar', 'baz']"} |
diff --git a/src/routes/user-guide/transactions/+page.svelte b/src/routes/user-guide/transactions/+page.svelte
index b8759d6..d0f873d 100644
--- a/src/routes/user-guide/transactions/+page.svelte
+++ b/src/routes/user-guide/transactions/+page.svelte
@@ -1,5 +1,6 @@
Transactions - User Guide - Nymph.js
+ {@html github}
@@ -41,6 +42,12 @@
instance. It will be tied to a specific connection to the database.
+
+ The transaction instance of Nymph has its own set of classes. You can use
+ its getEntityClass
method to get the proper classes for that instance
+ of Nymph.
+
+
When you start a new transaction, entities retrieved from that transaction's
Nymph instance will have that instance within their static nymph
+
+
+ Here is an example of a class that uses a transaction to delete all of its
+ children when it is deleted. If any of its children cannot be deleted, then
+ the transaction is rolled back, meaning none of its children get deleted.
+
+
+ {
+ static ETYPE = 'todo';
+ static class = 'Todo';
+
+ protected $clientEnabledMethods = [];
+ protected $allowlistData? = ['name', 'done', 'parent'];
+ protected $protectedTags = [];
+ protected $allowlistTags? = [];
+
+ constructor() {
+ super();
+
+ this.$data.name = '';
+ this.$data.done = false;
+ }
+
+ async $getUniques() {
+ return [
+ \`\${this.$data.user?.guid}:\${this.$data.parent?.guid}:\${this.$data.name}\`,
+ ];
+ }
+
+ /**
+ * Set a new Nymph instance on this and all contained entities.
+ */
+ $setNymph(nymph: Nymph) {
+ this.$nymph = nymph;
+ if (!this.$asleep()) {
+ if (this.$data.user) {
+ this.$data.user.$nymph = nymph;
+ }
+ if (this.$data.group) {
+ this.$data.group.$nymph = nymph;
+ }
+ if (this.$data.parent) {
+ this.$data.parent.$setNymph(nymph);
+ }
+ }
+ }
+
+ async $save() {
+ const tilmeld = enforceTilmeld(this);
+ if (!tilmeld.gatekeeper()) {
+ // Only allow logged in users to save.
+ throw new Error('You are not logged in.');
+ }
+
+ // Validate the entity's data.
+ Joi.attempt(
+ this.$getValidatable(),
+ Joi.object().keys({
+ ...nymphJoiProps,
+ ...tilmeldJoiProps,
+
+ name: Joi.string().trim(false).max(500, 'utf8').required(),
+ done: Joi.boolean().required(),
+ parent: Joi.object().instance(Todo),
+ }),
+ 'Invalid Todo: ',
+ );
+
+ try {
+ return await super.$save();
+ } catch (e: any) {
+ if (e instanceof EntityUniqueConstraintError) {
+ throw new Error('There is already a todo for that.');
+ }
+ throw e;
+ }
+ }
+
+ async $delete() {
+ const transaction = 'todo-delete-' + this.guid;
+ const nymph = this.$nymph;
+ const tnymph = await nymph.startTransaction(transaction);
+ this.$setNymph(tnymph);
+
+ try {
+ // Delete this todo's children.
+ const children = await tnymph.getEntities(
+ {
+ class: tnymph.getEntityClass(Todo),
+ skipAc: true,
+ },
+ {
+ type: '&',
+ ref: ['parent', this],
+ },
+ );
+
+ for (let child of children) {
+ if (!(await child.$delete())) {
+ throw new Error("Couldn't delete child todo.");
+ }
+ }
+
+ // Delete todo.
+ let success = await super.$delete();
+ if (success) {
+ success = await tnymph.commit(transaction);
+ } else {
+ await tnymph.rollback(transaction);
+ }
+ this.$setNymph(nymph);
+ return success;
+ } catch (e: any) {
+ await tnymph.rollback(transaction);
+ this.$setNymph(nymph);
+ throw e;
+ }
+ }
+}
+`}
+ />
+
+
+ The $setNymph
method is used to make sure the entity and all
+ referenced entities use the transactional Nymph instance. The
+ tnymph
Nymph instance is used during the transaction, and the
+ children are retrieved using the proper class with
+ tnymph.getEntityClass(Todo)
.
+