Skip to content

Commit

Permalink
Extended observe events (see also #215), introduced intercept events.
Browse files Browse the repository at this point in the history
  • Loading branch information
mweststrate committed Apr 26, 2016
1 parent 5126c14 commit 267d740
Show file tree
Hide file tree
Showing 15 changed files with 360 additions and 132 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"lodash.intersection": "^3.2.0",
"tape": "^4.2.2",
"tape-run": "^2.1.0",
"typescript": "^1.7.5",
"typescript": "^1.8.10",
"uglify-js": "^2.6.1"
},
"dependencies": {},
Expand All @@ -68,4 +68,4 @@
"state management",
"data flow"
]
}
}
4 changes: 4 additions & 0 deletions src/api/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export function action<T extends Function>(fn: T): T;
export function action<T extends Function>(name: string, fn: T): T;
export function action(target: any, propertyKey: string, descriptor: PropertyDescriptor): void;
export function action(arg1, arg2?, arg3?): any {
// TODO: introduce reaction as well?
// TODO: untracked?
// TODO: empty derivation stack warning?
// TODO: introduce transiationTracker event
switch (arguments.length) {
case 1:
return actionImplementation(arg1.name || "<unnamed action>", arg1);
Expand Down
1 change: 1 addition & 0 deletions src/api/observable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function observable<T, S extends Object>(value: () => T, thisArg?: S): IO
export function observable<T extends string|number|boolean|Date|RegExp|Function|void>(value: T): IObservableValue<T>;
export function observable<T extends Object>(value: T): T;
export function observable(v: any, keyOrScope?: string | any) {
// TOOD: mirror structure of interceptable?
if (typeof arguments[1] === "string")
return observableDecorator.apply(null, arguments);

Expand Down
15 changes: 7 additions & 8 deletions src/api/observe.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import {IObservableArray, IArrayChange, IArraySplice, isObservableArray} from "../types/observablearray";
import {ObservableMap, IObservableMapChange, isObservableMap} from "../types/observablemap";
import {IObjectChange, isObservableObject, observeObservableObject} from "../types/observableobject";
import {IObservableArray, IArrayDidChange, IArrayDidSplice, isObservableArray} from "../types/observablearray";
import {ObservableMap, IMapDidChange, isObservableMap} from "../types/observablemap";
import {IObjectDidChange, isObservableObject, observeObservableObject} from "../types/observableobject";
import {IObservableValue, observable} from "./observable";
import {ComputedValue} from "../core/computedvalue";
import {ObservableValue} from "../types/observablevalue";
import {Lambda, isPlainObject, invariant} from "../utils/utils";
import {autorun} from "../api/autorun";
import {isObservable} from "./isobservable";
import {extendObservable} from "./extendobservable";

export function observe<T>(value: IObservableValue<T>, listener: (newValue: T, oldValue: T) => void, fireImmediately?: boolean): Lambda;
export function observe<T>(observableArray: IObservableArray<T>, listener: (change: IArrayChange<T> | IArraySplice<T>) => void, fireImmediately?: boolean): Lambda;
export function observe<T>(observableMap: ObservableMap<T>, listener: (change: IObservableMapChange<T>) => void, fireImmediately?: boolean): Lambda;
export function observe<T>(observableArray: IObservableArray<T>, listener: (change: IArrayDidChange<T> | IArrayDidSplice<T>) => void, fireImmediately?: boolean): Lambda;
export function observe<T>(observableMap: ObservableMap<T>, listener: (change: IMapDidChange<T>) => void, fireImmediately?: boolean): Lambda;
export function observe<T>(observableMap: ObservableMap<T>, property: string, listener: (newValue: T, oldValue: T) => void, fireImmediately?: boolean): Lambda;
export function observe<T extends Object>(object: T, listener: (change: IObjectChange<any, T>) => void, fireImmediately?: boolean): Lambda;
export function observe<T extends Object>(object: T, property: string, listener: (newValue: T, oldValue: T) => void, fireImmediately?: boolean): Lambda;
export function observe(object: Object, listener: (change: IObjectDidChange) => void, fireImmediately?: boolean): Lambda;
export function observe(object: Object, property: string, listener: (newValue: any, oldValue: any) => void, fireImmediately?: boolean): Lambda;
export function observe(thing, propOrCb?, cbOrFire?, fireImmediately?): Lambda {
if (typeof cbOrFire === "function")
return observeObservableProperty(thing, propOrCb, cbOrFire, fireImmediately);
Expand Down
12 changes: 11 additions & 1 deletion src/core/globalstate.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {IAtom} from "./atom";
import {IDerivation} from "./derivation";
import {Reaction} from "./reaction";
import {IInterceptable, IInterceptor, registerInterceptor} from "./interceptable";

declare const global: any;

export class MobXGlobals {
export class MobXGlobals implements IInterceptable<any> {
/**
* MobXGlobals version.
* MobX compatiblity with other versions loaded in memory as long as this version matches.
Expand Down Expand Up @@ -60,6 +61,15 @@ export class MobXGlobals {
* Used by createTransformer to detect that the global state has been reset.
*/
resetId = 0;

/**
* Global interceptors that will be run before applying state changes
*/
interceptors: IInterceptor<any>[] = [];

intercept(handler) {
return registerInterceptor(this, handler);
}
}

export const globalState = (() => {
Expand Down
39 changes: 39 additions & 0 deletions src/core/interceptable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {globalState} from "./globalstate";
import {Lambda, once, invariant} from "../utils/utils";

export type IInterceptor<T> = (change: T) => T;

export interface IInterceptable<T> {
interceptors: IInterceptor<T>[];
intercept(handler: IInterceptor<T>): Lambda;
}

export function hasInterceptors(interceptable: IInterceptable<any>) {
return globalState.interceptors.length > 0 || (interceptable.interceptors && interceptable.interceptors.length > 0);
}

export function registerInterceptor<T>(interceptable: IInterceptable<T>, handler: IInterceptor<T>): Lambda {
interceptable.interceptors.push(handler);
return once(() => {
const idx = interceptable.interceptors.indexOf(handler);
if (idx !== -1)
interceptable.interceptors.splice(idx, 1);
});
}

export function interceptChange<T>(interceptable: IInterceptable<T>, change: T): T {
return interceptChangeRunner(globalState, interceptChangeRunner(interceptable, change));
}

function interceptChangeRunner<T>(interceptable: IInterceptable<T>, change: T): T {
const interceptors = interceptable.interceptors;
if (interceptors) {
for (let i = 0, l = interceptors.length; i < l; i++) {
change = interceptors[i](change);
//invariant(!!change, "Intercept handlers should always return a change object");
if (!change)
return null;
}
}
return change;
}
7 changes: 4 additions & 3 deletions src/mobx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ export { SimpleEventEmitter, ISimpleEventListener } from "./utils/si
export { ITransitionEvent, IObserverTree, IDependencyTree } from "./api/extras";
export { IObservable, IDepTreeNode, untracked } from "./core/observable";
export { IDerivation } from "./core/derivation";
export { IInterceptable, IInterceptor } from "./core/interceptable";

export { asReference, asFlat, asStructure } from "./types/modifiers";
export { IObjectChange, isObservableObject } from "./types/observableobject";
export { IObservableArray, IArrayChange, IArraySplice, isObservableArray, fastArray } from "./types/observablearray";
export { IObservableMapChange, IKeyValueMap, ObservableMap, IMapEntries, isObservableMap, map } from "./types/observablemap"
export { IObjectDidChange, isObservableObject } from "./types/observableobject";
export { IObservableArray, IArrayDidChange, IArrayDidSplice, isObservableArray, fastArray } from "./types/observablearray";
export { IKeyValueMap, ObservableMap, IMapEntries, IMapWillChange, IMapDidChange, isObservableMap, map } from "./types/observablemap"

export { action } from "./api/action";
export { IObservableValue, observable } from "./api/observable";
Expand Down

0 comments on commit 267d740

Please sign in to comment.