Skip to content

Commit

Permalink
Merge pull request pnp#1962 from patrick-rodgers/version-3
Browse files Browse the repository at this point in the history
WIP: v3 clean up and testing improvements
  • Loading branch information
patrick-rodgers authored Dec 3, 2021
2 parents 62fed50 + 175423b commit 882329c
Show file tree
Hide file tree
Showing 60 changed files with 5,464 additions and 4,975 deletions.
6 changes: 3 additions & 3 deletions .mocharc.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ const retries = yargs.noretries ? "0" : "2";

const config = {
package: "./package.json",
reporter: reporter,
slow: 3000,
reporter,
slow: 2000,
timeout: 40000,
ui: "bdd",
retries: retries,
retries,
require: [
resolve("./", "build/testing/tools/local-module-resolver/register.js"),
],
Expand Down
2 changes: 1 addition & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
List of Patterns and Practices PnPjs v2 contributors. Updated before every release. This builds on all of the work done by everyone that helped, contributed, and reported issues for v1.
List of Patterns and Practices PnPjs contributors. Updated before every release. This builds on all of the work done by everyone that helped, contributed, and reported issues.

Patrick Rodgers, Microsoft (patrick-rodgers)
Julie Turner (juliemturner)
Expand Down
8 changes: 4 additions & 4 deletions debug/launch/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import { Example } from "./v3-patrick.js";
import(findup("settings.js")).then((settings: ITestingSettings) => {

// // setup console logger
Logger.subscribe(ConsoleListener("Debug - ", {
Logger.subscribe(ConsoleListener("Debug", {
color: "skyblue",
errorColor: "red",
verboseColor: "lightslategray",
warningColor: "yellow",
error: "red",
verbose: "lightslategray",
warning: "yellow",
}));

Example(settings);
Expand Down
6 changes: 3 additions & 3 deletions docs/core/behaviors.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export function MyBehavior(specialSecret: string): TimelinePipe {
obj.using(MyBehavior("HereIsMySuperSecretValue"));
```

[](#assignfrom)

## Core Behaviors

Please also see available behaviors in [@pnp/queryable]("../../../queryable/behaviors.md), [@pnp/sp]("../../../sp/behaviors.md), and [@pnp/graph]("../../../graph/behaviors.md)
Expand All @@ -55,13 +57,11 @@ target.on.log(console.log);

### CopyFrom

Similar to AssignFrom, this method creates a copy (using [cloneDeep from lodash](https://www.npmjs.com/package/lodash.clonedeep)) of all the observers on the source and applies them to the target. This can be done either as a `replace` or `append` operation using the second parameter. The default is "append".
Similar to AssignFrom, this method creates a copy of all the observers on the source and applies them to the target. This can be done either as a `replace` or `append` operation using the second parameter. The default is "append".

- "replace" will first clear each source moment's registered observers then apply each in source-order via the `on` operation.
- "append" will apply each source moment's registered observers in source-order via the `on` operation

> In both cases no effort is made to determine if the obserers being registered are against valid moments on the target.
```TypeScript
import { CopyFrom } from "@pnp/core";

Expand Down
107 changes: 106 additions & 1 deletion docs/core/observers.md
Original file line number Diff line number Diff line change
@@ -1 +1,106 @@
# @pnp/core : observers
# @pnp/core : observers

Observers are used to implement all of the functionality within a [Timeline's](./timeline.md) [moments](./moments.md). Each moment defines the signature of observers you can register, and calling the observers is orchestrated by the implementation of the moment. A few facts about observers:

- All observers are functions
- The "this" of an observer is always the Timeline implementation that emitted the moment
- Do not handle non-recoverable errors in observers, let them throw and they will be handled by the library appropriately and routed to the `error` moment.

> For details on implementing observers for Queryable, [please see this article](../queryable/queryable.md).
## Observer Inheritance

Timelines created from other timelines (i.e. how sp and graph libraries work) inherit all of the observers from the parent. Observers added to the parent will appear for all children. This is why when you create a new sp or graph and setup the defaults you do not need to do it for every object.

When you make a change to the set of observers through any of the subscription methods outlined below that inheritance is broken. Meaning changes to the parent will no longer apply to that child, and changes to a child never affect a parent. This applies to ALL moments if one changes, there is no per-moment inheritance concept.

```TypeScript
const sp = new spfi().using(...lots of behaviors);

// web is current inheriting all observers from "sp"
const web = sp.web;

// at this point web no longer inherits from "sp" and has its own observers
// but still includes everything that was registered in sp before this call
web.on.log(...);

// web2 inherits from sp as each invocation of .web creates a fresh IWeb
const web2 = sp.web;

// list inherits from web's observers and will contain the extra `log` observer added above
const list = web.lists.getById("");

// this new behavior will apply to web2 and any subsequent objects created from sp
sp.using(AnotherBehavior());

// web will again inherit from sp through web2, the extra log handler is gone
// list now ALSO is reinheriting from sp as it was pointing to web
web.using(AssignFrom(web2));
// see below for more information on AssignFrom
```

## Obserever Subscriptions

All timeline moments are exposed through the `on` property with three options for subscription.

### Append

This is the default, and adds your observer to the end of the array of subscribed observers.

```TypeScript
obj.on.log(function(this: Queryable, message: string, level: number) {
if (level > 1) {
console.log(message);
}
});
```

### Prepend

Using prepend will place your observer as the first item in the array of subscribed observers. There is no gaurantee it will always remain first, other code can also use prepend.

```TypeScript
obj.on.log.prepend(function(this: Queryable, message: string, level: number) {
if (level > 1) {
console.log(message);
}
});
```

### Replace

Replace will remove all other subscribed observers from a moment and add the supplied observer as the only one in the array of subscribed observers.

```TypeScript
obj.on.log.replace(function(this: Queryable, message: string, level: number) {
if (level > 1) {
console.log(message);
}
});
```

### ToArray

The ToArray method creates a cloned copy of the array of registered observers for a given moment. Note that because it is a clone changes to the returned array do not affect the registered observers.

```TypeScript
const arr = obj.on.log.toArray();
```

### Clear

This clears ALL observers for a given moment, returning true if any observers were removed, and false if no changes were made.

```TypeScript
const didChange = obj.on.log.clear();
```

## Special Behaviors

The core library includes two special behaviors used to help manage observer inheritance. The best case is to manage inheritance using the methods described above, but these provide quick shorthand to help in certain scenarios. These are [AssignFrom](./behaviors.md#assignfrom) and [CopyFrom](./behaviors.md#copyfrom).






22 changes: 12 additions & 10 deletions docs/core/timeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type FirstObserver = (this: any, counter: number) => Promise<[number]>;
// the second observer is a function taking a number and returning void
export type SecondObserver = (this: any, result: number) => void;

// this is a custom moment definition as an example. Please read more about moments on the moments page
// this is a custom moment definition as an example.
export function report<T extends ObserverAction>(): (observers: T[], ...args: any[]) => void {

return function (observers: T[], ...args: any[]): void {
Expand Down Expand Up @@ -142,18 +142,20 @@ Now that you implemented a simple timeline let's take a minute to understand the

### Timeline Lifecycle

- init
- your moments as defined in execute, in our example
- first
- second
- dispose
- .on.init (always)
- your moments as defined in execute, in our example:
- .on.first
- .on.second
- .on.dispose (always)

As well the moments log and error exist on every Timeline derived class and can occur at any point during the lifecycle.

## Observer Inheritance

Let's say that you want to contruct a system whereby you can create Timeline based instances from other Timeline based instances - which is what [Queryable](../queryable/queryable.md) does. Imagine we have a class with a pseudo-signature like:

```TypeScript
class ATimeline extends Timeline<typeof SomeMoments> {
class ExampleTimeline extends Timeline<typeof SomeMoments> {

// we create two unique refs for our implementation we will use
// to resolve the execute promise
Expand All @@ -173,12 +175,12 @@ class ATimeline extends Timeline<typeof SomeMoments> {
We can then use it like:

```TypeScript
const tl1 = new ATimeline();
const tl1 = new ExampleTimeline();
tl1.on.first(async (n) => [++n]);
tl1.on.second(async (n) => [++n]);

// at this point tl2's observer collection is a pointer/reference to the same collection as tl1
const tl2 = new ATimeline(tl1);
// at this point tl2's observer collection is a pointer to the same collection as tl1
const tl2 = new ExampleTimeline(tl1);

// we add a second observer to first, it is applied to BOTH tl1 and tl2
tl1.on.first(async (n) => [++n]);
Expand Down
Loading

0 comments on commit 882329c

Please sign in to comment.