Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions lib/attributes-internal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { createContext } from "./context.ts";
import { useScope } from "./scope.ts";
import type { Operation, Scope } from "./types.ts";

/**
* Serializable name/value pairs that can be used for visualizing and
* inpsecting Effection scopes. There will always be at least a name
* in the attributes.
*/
export type Attributes =
& { name: string }
& Record<string, string | number | boolean>;

const AttributesContext = createContext<Attributes>(
"@effection/attributes",
{ name: "anonymous" },
);

/**
* Add metadata to the current {@link Scope} that can be used for
* display and debugging purposes.
*
* Calling `useAttributes()` multiple times will add new attributes
* and overwrite attributes of the same name, but it will not erase
* old ones.
*
* @example
* ```ts
* function useServer(port: number): Operation<Server> {
* return resource(function*(provide) {
* yield* useAttributes({ name: "Server", port });
* let server = createServer();
* server.listen();
* try {
* yield* provide(server);
* } finally {
* server.close();
* }
* });
* }
* ```
*
* @param attrs - attributes to add to this {@link Scope}
* @returns an Oeration adding `attrs` to the current scope
* @since 4.1
*/
export function* useAttributes(attrs: Partial<Attributes>): Operation<void> {
let scope = yield* useScope();

let current = scope.hasOwn(AttributesContext)
? scope.expect(AttributesContext)
: AttributesContext.defaultValue!;

scope.set(AttributesContext, { ...current, ...attrs });
}

/**
* Get the unique attributes of this {@link Scope}. Attributes are not
* inherited and only the attributes explicitly assigned to this scope
* will be returned.
*/
export function getAttributes(scope: Scope) {
if (scope.hasOwn(AttributesContext)) {
return scope.expect(AttributesContext);
}
return AttributesContext.defaultValue as Attributes;
}
1 change: 1 addition & 0 deletions lib/attributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useAttributes } from "./attributes-internal.ts";
1 change: 1 addition & 0 deletions lib/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from "./types.ts";
export * from "./result.ts";
export * from "./action.ts";
export * from "./context.ts";
export * from "./attributes.ts";
export * from "./scope.ts";
export * from "./suspend.ts";
export * from "./sleep.ts";
Expand Down
45 changes: 45 additions & 0 deletions test/attributes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { run, spawn, useAttributes, useScope } from "@effection/effection";
import { describe, expect, it } from "./suite.ts";
import { getAttributes } from "../lib/attributes-internal.ts";

describe("useAttributes", () => {
it("adds attributes to the current scope", async () => {
let scope = await run(function* main() {
yield* useAttributes({ name: "Main", awesome: true });

return yield* useScope();
});

let attrs = getAttributes(scope);

expect(attrs).toEqual({ name: "Main", awesome: true });
});

it("does not cause any attributes to be inherited from the parent", async () => {
let scope = await run(function* main() {
yield* useAttributes({ awesome: true });
let child = yield* spawn(function* () {
yield* useAttributes({ name: "Child" });
return yield* useScope();
});

return yield* child;
});

let attrs = getAttributes(scope);

expect(attrs).toEqual({ name: "Child" });
});

it("adds new attributes to existing ones", async () => {
let scope = await run(function* main() {
yield* useAttributes({ name: "Main" });
yield* useAttributes({ awesome: true });
return yield* useScope();
});

let attrs = getAttributes(scope);

expect(attrs).toEqual({ name: "Main", awesome: true });
});
});
Loading