Skip to content

Commit 5e20231

Browse files
committed
✨ Inspectable attributes (#1098)
1 parent 0821cfc commit 5e20231

File tree

4 files changed

+114
-0
lines changed

4 files changed

+114
-0
lines changed

lib/attributes-internal.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { createContext } from "./context.ts";
2+
import { useScope } from "./scope.ts";
3+
import type { Operation, Scope } from "./types.ts";
4+
5+
/**
6+
* Serializable name/value pairs that can be used for visualizing and
7+
* inpsecting Effection scopes. There will always be at least a name
8+
* in the attributes.
9+
*/
10+
export type Attributes =
11+
& { name: string }
12+
& Record<string, string | number | boolean>;
13+
14+
const AttributesContext = createContext<Attributes>(
15+
"@effection/attributes",
16+
{ name: "anonymous" },
17+
);
18+
19+
/**
20+
* Add metadata to the current {@link Scope} that can be used for
21+
* display and debugging purposes.
22+
*
23+
* Calling `useAttributes()` multiple times will add new attributes
24+
* and overwrite attributes of the same name, but it will not erase
25+
* old ones.
26+
*
27+
* @example
28+
* ```ts
29+
* function useServer(port: number): Operation<Server> {
30+
* return resource(function*(provide) {
31+
* yield* useAttributes({ name: "Server", port });
32+
* let server = createServer();
33+
* server.listen();
34+
* try {
35+
* yield* provide(server);
36+
* } finally {
37+
* server.close();
38+
* }
39+
* });
40+
* }
41+
* ```
42+
*
43+
* @param attrs - attributes to add to this {@link Scope}
44+
* @returns an Oeration adding `attrs` to the current scope
45+
* @since 4.1
46+
*/
47+
export function* useAttributes(attrs: Partial<Attributes>): Operation<void> {
48+
let scope = yield* useScope();
49+
50+
let current = scope.hasOwn(AttributesContext)
51+
? scope.expect(AttributesContext)
52+
: AttributesContext.defaultValue!;
53+
54+
scope.set(AttributesContext, { ...current, ...attrs });
55+
}
56+
57+
/**
58+
* Get the unique attributes of this {@link Scope}. Attributes are not
59+
* inherited and only the attributes explicitly assigned to this scope
60+
* will be returned.
61+
*/
62+
export function getAttributes(scope: Scope) {
63+
if (scope.hasOwn(AttributesContext)) {
64+
return scope.expect(AttributesContext);
65+
}
66+
return AttributesContext.defaultValue as Attributes;
67+
}

lib/attributes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { useAttributes } from "./attributes-internal.ts";

lib/mod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export * from "./types.ts";
22
export * from "./result.ts";
33
export * from "./action.ts";
44
export * from "./context.ts";
5+
export * from "./attributes.ts";
56
export * from "./scope.ts";
67
export * from "./suspend.ts";
78
export * from "./sleep.ts";

test/attributes.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { run, spawn, useAttributes, useScope } from "@effection/effection";
2+
import { describe, expect, it } from "./suite.ts";
3+
import { getAttributes } from "../lib/attributes-internal.ts";
4+
5+
describe("useAttributes", () => {
6+
it("adds attributes to the current scope", async () => {
7+
let scope = await run(function* main() {
8+
yield* useAttributes({ name: "Main", awesome: true });
9+
10+
return yield* useScope();
11+
});
12+
13+
let attrs = getAttributes(scope);
14+
15+
expect(attrs).toEqual({ name: "Main", awesome: true });
16+
});
17+
18+
it("does not cause any attributes to be inherited from the parent", async () => {
19+
let scope = await run(function* main() {
20+
yield* useAttributes({ awesome: true });
21+
let child = yield* spawn(function* () {
22+
yield* useAttributes({ name: "Child" });
23+
return yield* useScope();
24+
});
25+
26+
return yield* child;
27+
});
28+
29+
let attrs = getAttributes(scope);
30+
31+
expect(attrs).toEqual({ name: "Child" });
32+
});
33+
34+
it("adds new attributes to existing ones", async () => {
35+
let scope = await run(function* main() {
36+
yield* useAttributes({ name: "Main" });
37+
yield* useAttributes({ awesome: true });
38+
return yield* useScope();
39+
});
40+
41+
let attrs = getAttributes(scope);
42+
43+
expect(attrs).toEqual({ name: "Main", awesome: true });
44+
});
45+
});

0 commit comments

Comments
 (0)