##// END OF EJS Templates
Merge
cin -
r154:2a5720a0816e merge default
parent child
Show More
@@ -1,56 +1,57
1 fc9f82c082ef432137da086a1fe9c37a12ba16a2 v1.0.0-rc2
1 fc9f82c082ef432137da086a1fe9c37a12ba16a2 v1.0.0-rc2
2 6d80d7901b4c8ffe8728e4a7bf5f4b7e7a669bb5 v1.0.0-rc3
2 6d80d7901b4c8ffe8728e4a7bf5f4b7e7a669bb5 v1.0.0-rc3
3 5a2c44d8e1f34dd30c2b50f92b7dc2e8f3247c43 v1.0.0-rc5
3 5a2c44d8e1f34dd30c2b50f92b7dc2e8f3247c43 v1.0.0-rc5
4 6c01fabe9ea9fb5e753fbeae8b0d2664e7072a66 v1.0.0-rc6
4 6c01fabe9ea9fb5e753fbeae8b0d2664e7072a66 v1.0.0-rc6
5 9e546fe36fdddc8324f1098ee950fa1a7ba19b93 v1.0.0-rc7
5 9e546fe36fdddc8324f1098ee950fa1a7ba19b93 v1.0.0-rc7
6 8f4d5e2c719a20ae6d65f1f4b5e2141ed765e975 v1.0.0-rc8
6 8f4d5e2c719a20ae6d65f1f4b5e2141ed765e975 v1.0.0-rc8
7 a1ab2b5975ad4b19599fb61538e7aaf329fb528c v1.0.0-rc10
7 a1ab2b5975ad4b19599fb61538e7aaf329fb528c v1.0.0-rc10
8 9b77ac3bf8f200876450ad50c308a9441a7f39c7 v1.0.0-rc11
8 9b77ac3bf8f200876450ad50c308a9441a7f39c7 v1.0.0-rc11
9 32b72f33756d3d10553b743f0b9f4504148cf97d v1.0.0-rc12
9 32b72f33756d3d10553b743f0b9f4504148cf97d v1.0.0-rc12
10 b88fac0e76c0e61e397e2995f468f7cf342afbc9 v1.0.0-rc13
10 b88fac0e76c0e61e397e2995f468f7cf342afbc9 v1.0.0-rc13
11 a46488b209e8aac583c1634043147d87740c63b4 v1.0.0-rc14
11 a46488b209e8aac583c1634043147d87740c63b4 v1.0.0-rc14
12 1174538197f6796384e643f62100292f1377b137 v1.0.0-rc15
12 1174538197f6796384e643f62100292f1377b137 v1.0.0-rc15
13 e8012fdf09ae442094f3831abe70649f8520659e 1.0.0-rc16
13 e8012fdf09ae442094f3831abe70649f8520659e 1.0.0-rc16
14 a1a1ef050ecc9d2a780e308cbffc2ad915a5c13d 1.0.0-rc17
14 a1a1ef050ecc9d2a780e308cbffc2ad915a5c13d 1.0.0-rc17
15 5c6c7e16919cff4019a55661725789d287439b75 v1.0.0-rc18
15 5c6c7e16919cff4019a55661725789d287439b75 v1.0.0-rc18
16 3b6c4159c66cecf6c5957d33ad474919608489c6 v1.0.0
16 3b6c4159c66cecf6c5957d33ad474919608489c6 v1.0.0
17 18383b2dcc1ae97fe3673242c84fe8d4b50a55f0 v1.0.1
17 18383b2dcc1ae97fe3673242c84fe8d4b50a55f0 v1.0.1
18 ed3c20c09b000386b5204b483955eb61ee662eff v1.0.2
18 ed3c20c09b000386b5204b483955eb61ee662eff v1.0.2
19 030ea350f98bb7a06e636251ea5cad403217e868 v1.0.3
19 030ea350f98bb7a06e636251ea5cad403217e868 v1.0.3
20 346ba910a5425ed95f7a67ef4db1dbbf599e2cab v1.0.5
20 346ba910a5425ed95f7a67ef4db1dbbf599e2cab v1.0.5
21 deb0ed6fb68015d89ba1287f6b766d1f80b6e4ff v1.0.7
21 deb0ed6fb68015d89ba1287f6b766d1f80b6e4ff v1.0.7
22 dd0d589acfbbe938a5630547e83ad934ab597b64 v1.0.8
22 dd0d589acfbbe938a5630547e83ad934ab597b64 v1.0.8
23 f2499237b5bf0ac2d73a8e437b8f15fa8a6306a5 v1.0.9
23 f2499237b5bf0ac2d73a8e437b8f15fa8a6306a5 v1.0.9
24 d4f0cdae9577caa605d9317d0ceb93f7b67895f3 v1.0.10
24 d4f0cdae9577caa605d9317d0ceb93f7b67895f3 v1.0.10
25 1a0018655d1c3dafc16ae50971cb535b8555e246 v1.1.0
25 1a0018655d1c3dafc16ae50971cb535b8555e246 v1.1.0
26 ff3695b0a48f00155ad6c3d7e000fe185ee6f95a v1.1.1
26 ff3695b0a48f00155ad6c3d7e000fe185ee6f95a v1.1.1
27 c276b9b7fa833242d1bf75eeeaae9924feee51e8 v1.1.2
27 c276b9b7fa833242d1bf75eeeaae9924feee51e8 v1.1.2
28 fdde09e66c009f4950ec69a393a8e789725da758 v1.2.0
28 fdde09e66c009f4950ec69a393a8e789725da758 v1.2.0
29 16678c6055f20ce1a49bdca5e6dabce88ee8deed v1.2.1
29 16678c6055f20ce1a49bdca5e6dabce88ee8deed v1.2.1
30 bc7556143fe536e3df374bc0070146311884284e v1.2.2
30 bc7556143fe536e3df374bc0070146311884284e v1.2.2
31 e5bb5e80ce96fc4ca0fbbf0b5390443a616e99fa v1.2.3
31 e5bb5e80ce96fc4ca0fbbf0b5390443a616e99fa v1.2.3
32 cc5be30e84f8a0d9a8e25447651576d9e46ab154 v1.2.4
32 cc5be30e84f8a0d9a8e25447651576d9e46ab154 v1.2.4
33 2807ab11174c7446830d91d5f1b652a18c6ecae5 v1.2.5
33 2807ab11174c7446830d91d5f1b652a18c6ecae5 v1.2.5
34 35a7b6319ebe24973fe10b8d82c19d3d86857b4e v1.2.6
34 35a7b6319ebe24973fe10b8d82c19d3d86857b4e v1.2.6
35 70058deb750dc18fcd9be83c28cb8371530fffd8 v1.2.7
35 70058deb750dc18fcd9be83c28cb8371530fffd8 v1.2.7
36 367f8caa5bf8fd2d4a56a6cff40be31ab82a2727 v1.2.8
36 367f8caa5bf8fd2d4a56a6cff40be31ab82a2727 v1.2.8
37 15c829aa08a2cf65cc074640199e79c5e49d2c93 v1.3.0
37 15c829aa08a2cf65cc074640199e79c5e49d2c93 v1.3.0
38 1a190b3a757dc650c2c612073cbffaaa720b6090 v1.4.0
38 1a190b3a757dc650c2c612073cbffaaa720b6090 v1.4.0
39 2ccfaae984e9a458580664923c87f258cff9890f v1.4.4
39 2ccfaae984e9a458580664923c87f258cff9890f v1.4.4
40 e9a9ed6d7647848e9c7f28e6e9f92d009259607d v1.5.0
40 e9a9ed6d7647848e9c7f28e6e9f92d009259607d v1.5.0
41 aac297dda27dac1e86bc93617ba172a2c92415f5 v1.6.0
41 aac297dda27dac1e86bc93617ba172a2c92415f5 v1.6.0
42 e07418577cbcaa6e31ccbf87c4495324ecbd88bd v1.6.1
42 e07418577cbcaa6e31ccbf87c4495324ecbd88bd v1.6.1
43 bc1b4dd8ca1a03af095ea1a61169c6025ef37fce v1.6.2
43 bc1b4dd8ca1a03af095ea1a61169c6025ef37fce v1.6.2
44 fb2ea4d6aabac2b374f3b2bfddaca6a83dedf34f v1.6.3
44 fb2ea4d6aabac2b374f3b2bfddaca6a83dedf34f v1.6.3
45 cede47727a1b0867d19be1d168aceb00b5e74e3f v1.7.0
45 cede47727a1b0867d19be1d168aceb00b5e74e3f v1.7.0
46 8095aad89415099d8e1942a8e67050c22e111d8b v1.7.1
46 8095aad89415099d8e1942a8e67050c22e111d8b v1.7.1
47 66546e70973248de01601aa364eadf11dd2f3a9f v1.8.0
47 66546e70973248de01601aa364eadf11dd2f3a9f v1.8.0
48 c7d9ad82b374c2a20710eba8fa40a9b1472ddc77 v1.8.1
48 c7d9ad82b374c2a20710eba8fa40a9b1472ddc77 v1.8.1
49 f139e2153e0da5649558b1ed9369da860d442c0d v1.9.0-rc1
49 f139e2153e0da5649558b1ed9369da860d442c0d v1.9.0-rc1
50 435ce00ba2452d565f1ed3bb884643b4b88b9cde v1.9.0-rc2
50 435ce00ba2452d565f1ed3bb884643b4b88b9cde v1.9.0-rc2
51 98b2c550c676ff6acf16f5bd56ee3804342f80b7 v1.9.0-rc3
51 98b2c550c676ff6acf16f5bd56ee3804342f80b7 v1.9.0-rc3
52 515d1b83ebdfaa93599451175055a94f711c079f v1.9.0-rc4
52 515d1b83ebdfaa93599451175055a94f711c079f v1.9.0-rc4
53 894b8239b953c0384cf32cab46cf41dac97ea03b v1.9.0-rc5
53 894b8239b953c0384cf32cab46cf41dac97ea03b v1.9.0-rc5
54 63215d91ae4b711eeb2145b9685240e1afada904 v1.9.0-rc6
54 63215d91ae4b711eeb2145b9685240e1afada904 v1.9.0-rc6
55 af4f8424e83d56e89a64f39e19514ca10dbd43c6 v1.9.0
55 af4f8424e83d56e89a64f39e19514ca10dbd43c6 v1.9.0
56 63f3ad8e6cffcf32fc5a555e55e8544227293d4c v1.10.0
56 63f3ad8e6cffcf32fc5a555e55e8544227293d4c v1.10.0
57 d9c99ae7dec87ef47aded05def6a094a892df0e6 v1.10.1
@@ -1,152 +1,171
1 import { PromiseOrValue } from "@implab/core-amd/interfaces";
1 import { PromiseOrValue } from "@implab/core-amd/interfaces";
2 import { isCancellable, isPromise } from "@implab/core-amd/safe";
2 import { isCancellable, isPromise } from "@implab/core-amd/safe";
3 import { observe, Observable, empty } from "./observable";
3 import { observe, Observable, empty } from "./observable";
4 import { after } from "dojo/aspect";
4 import { after } from "dojo/aspect";
5
5
6 export interface OrderedUpdate<T> {
6 export interface OrderedUpdate<T> {
7 /** The item is being updated */
7 /** The item is being updated */
8 readonly item: T;
8 readonly item: T;
9
9
10 /** The previous index of the item, -1 in case it is inserted */
10 /** The previous index of the item, -1 in case it is inserted */
11 readonly prevIndex: number;
11 readonly prevIndex: number;
12
12
13 /** The new index of the item, -1 in case it is deleted */
13 /** The new index of the item, -1 in case it is deleted */
14 readonly newIndex: number;
14 readonly newIndex: number;
15
15
16 }
16 }
17
17
18 export type QueryResults<T> = Observable<OrderedUpdate<T>>;
18 export type QueryResults<T> = Observable<OrderedUpdate<T>>;
19
19
20 interface DjObservableResults<T> {
20 interface DjObservableResults<T> {
21 /**
21 /**
22 * Allows observation of results
22 * Allows observation of results
23 */
23 */
24 observe(listener: (object: T, previousIndex: number, newIndex: number) => void, includeUpdates?: boolean): {
24 observe(listener: (object: T, previousIndex: number, newIndex: number) => void, includeUpdates?: boolean): {
25 remove(): void;
25 remove(): void;
26 };
26 };
27 }
27 }
28
28
29 interface Queryable<T, Q, O> {
29 interface Queryable<T, Q, O> {
30 query(query?: Q, options?: O): PromiseOrValue<T[]>;
30 query(query?: Q, options?: O): PromiseOrValue<T[]>;
31 }
31 }
32
32
33 export const isDjObservableResults = <T>(v: object): v is DjObservableResults<T> =>
33 export const isDjObservableResults = <T>(v: object): v is DjObservableResults<T> =>
34 v && (typeof (v as { observe?: unknown; }).observe === "function");
34 v && (typeof (v as { observe?: unknown; }).observe === "function");
35
35
36 export const query = <T, Q, O>(store: Queryable<T, Q, O>, includeUpdates = true) => {
36 export const query = <T, Q, O>(store: Queryable<T, Q, O>, includeUpdates = true) => {
37 const q = queryEx(store, includeUpdates);
37 const q = queryEx(store, includeUpdates);
38 return (query?: Q, options?: O & { observe?: boolean }) => {
38 return (query?: Q, options?: O & { observe?: boolean }) => {
39 const [data, updates] = q(query, options);
39 const [data, updates] = q(query, options);
40
40
41 return options?.observe === false ? data : data.cat(updates);
41 return options?.observe === false ? data : data.cat(updates);
42 };
42 };
43 };
43 };
44
44
45 /**
46 * Wraps the query method of the store, the resulting method takes a query
47 * expression and returns two observable sequences. The first sequence represents
48 * the results of the query, the second sequence provides the updates to the
49 * query results.
50 *
51 * @param store The store used to query data
52 * @param includeUpdates The flag to include item updates not only additions and
53 * deletions. By default this flag is set to true.
54 * @returns Two observable sequences
55 */
45 export const queryEx = <T, Q, O>(store: Queryable<T, Q, O>, includeUpdates = true) =>
56 export const queryEx = <T, Q, O>(store: Queryable<T, Q, O>, includeUpdates = true) =>
46 (query?: Q, options?: O): [data: QueryResults<T>, updates: QueryResults<T>] => {
57 (query?: Q, options?: O): [data: QueryResults<T>, updates: QueryResults<T>] => {
47
58
48 const pending: T[] = [];
59 /** count active observers */
49
60 let listeners = 0;
50 let results: PromiseOrValue<T[]> = pending;
61 let results: PromiseOrValue<T[]> = [];
51
62
52 const data = observe<OrderedUpdate<T>>(({ next, complete, error }) => {
63 const data = observe<OrderedUpdate<T>>(({ next, complete, error }) => {
53 const processResults = (items: T[]) =>
64 const processResults = (items: T[]) =>
54 items.forEach((item, newIndex) => next({ item, newIndex, prevIndex: -1 }));
65 items.forEach((item, newIndex) => next({ item, newIndex, prevIndex: -1 }));
55
66
56 try {
67 try {
57 if (results === pending)
68 // is there are no active observers here, we need to query actual
69 // data from the store.
70 if (listeners === 0)
58 results = store.query(query, options);
71 results = store.query(query, options);
59
72
60 if (isPromise(results)) {
73 if (isPromise(results)) {
61 results.then(processResults).then(complete, error);
74 results.then(processResults).then(complete, error);
62
75
63 if (isCancellable(results))
76 if (isCancellable(results))
64 return results.cancel.bind(results);
77 return results.cancel.bind(results);
65 } else {
78 } else {
66 processResults(results);
79 processResults(results);
67 complete();
80 complete();
68 }
81 }
69 } catch (e) {
82 } catch (e) {
70 error(e);
83 error(e);
71 }
84 }
72 });
85 });
73
86
74 const updates = observe<OrderedUpdate<T>>(({ next, complete, error, isClosed }) => {
87 const updates = observe<OrderedUpdate<T>>(({ next, complete, error, isClosed }) => {
75 try {
88 try {
76 if (!isClosed() && isDjObservableResults<T>(results)) {
89 if (!isClosed() && isDjObservableResults<T>(results)) {
90 // subscribe fot the changes
91 listeners++;
77 const h = results.observe((item, prevIndex, newIndex) => next({ item, prevIndex, newIndex }), includeUpdates);
92 const h = results.observe((item, prevIndex, newIndex) => next({ item, prevIndex, newIndex }), includeUpdates);
78 return () => h.remove();
93 return () => {
94 // unsubscribe from changes
95 listeners--;
96 h.remove();
97 };
79 } else {
98 } else {
80 complete();
99 complete();
81 }
100 }
82 } catch (e) {
101 } catch (e) {
83 error(e);
102 error(e);
84 }
103 }
85 });
104 });
86
105
87 return [data, updates];
106 return [data, updates];
88 };
107 };
89
108
90
109
91 interface IndexedStore<T> {
110 interface IndexedStore<T> {
92 get(id: string | number): PromiseLike<T> | T | null | undefined;
111 get(id: string | number): PromiseLike<T> | T | null | undefined;
93 }
112 }
94
113
95 interface Notifications<T> {
114 interface Notifications<T> {
96 notify(item: T | undefined, id: string | number | undefined): void;
115 notify(item: T | undefined, id: string | number | undefined): void;
97 }
116 }
98
117
99 const hasNotifications = <T>(x: unknown): x is Notifications<T> =>
118 const hasNotifications = <T>(x: unknown): x is Notifications<T> =>
100 typeof x === "object" && x !== null && (typeof (x as Notifications<T>).notify === "function");
119 typeof x === "object" && x !== null && (typeof (x as Notifications<T>).notify === "function");
101
120
102 interface GetOpts {
121 interface GetOpts {
103 observe?: boolean;
122 observe?: boolean;
104 }
123 }
105
124
106 export type ItemUpdate<T> = [item: NonNullable<T>, id: string | number | undefined] |
125 export type ItemUpdate<T> = [item: NonNullable<T>, id: string | number | undefined] |
107 [item: undefined | null, id: string | number];
126 [item: undefined | null, id: string | number];
108
127
109 const filterItem = (itemId: string | number) =>
128 const filterItem = (itemId: string | number) =>
110 <T>(source: Observable<ItemUpdate<T>>) =>
129 <T>(source: Observable<ItemUpdate<T>>) =>
111 observe<T>(({ next, complete, error }) => {
130 observe<T>(({ next, complete, error }) => {
112 const subscription = source
131 const subscription = source
113 .filter(([, id]) => id === itemId)
132 .filter(([, id]) => id === itemId)
114 .subscribe({
133 .subscribe({
115 next: ([item]) => item !== null && item !== undefined ? next(item) : complete(),
134 next: ([item]) => item !== null && item !== undefined ? next(item) : complete(),
116 complete,
135 complete,
117 error
136 error
118 });
137 });
119 return () => subscription.unsubscribe();
138 return () => subscription.unsubscribe();
120 });
139 });
121
140
122 export const get = <T>(store: IndexedStore<T>) => {
141 export const get = <T>(store: IndexedStore<T>) => {
123 const changes = hasNotifications<T>(store) ?
142 const changes = hasNotifications<T>(store) ?
124 observe<ItemUpdate<T>>(({ next }) => {
143 observe<ItemUpdate<T>>(({ next }) => {
125 const handle = after(store, "notify", (...args: ItemUpdate<T>) => next(args), true);
144 const handle = after(store, "notify", (...args: ItemUpdate<T>) => next(args), true);
126 return () => handle.remove();
145 return () => handle.remove();
127 }) : empty;
146 }) : empty;
128
147
129 return (id: string | number, opts: GetOpts = {}) =>
148 return (id: string | number, opts: GetOpts = {}) =>
130 observe<T>(({ next, complete, error }) => {
149 observe<T>(({ next, complete, error }) => {
131 try {
150 try {
132 const result = store.get(id);
151 const result = store.get(id);
133
152
134 const handle = (x: T | null | undefined) => {
153 const handle = (x: T | null | undefined) => {
135 if (x !== null && x !== undefined)
154 if (x !== null && x !== undefined)
136 next(x);
155 next(x);
137 complete();
156 complete();
138 };
157 };
139
158
140 if (isPromise(result)) {
159 if (isPromise(result)) {
141 result.then(handle).then(undefined, error);
160 result.then(handle).then(undefined, error);
142
161
143 if (isCancellable(result))
162 if (isCancellable(result))
144 return () => result.cancel();
163 return () => result.cancel();
145 } else {
164 } else {
146 handle(result);
165 handle(result);
147 }
166 }
148 } catch (e) {
167 } catch (e) {
149 error(e);
168 error(e);
150 }
169 }
151 }).cat(opts.observe !== false ? changes.pipe(filterItem(id)) : empty);
170 }).cat(opts.observe !== false ? changes.pipe(filterItem(id)) : empty);
152 }; No newline at end of file
171 };
@@ -1,173 +1,191
1 import { empty, observe, of } from "./observable";
1 import { empty, observe, of } from "./observable";
2 import * as tap from "tap";
2 import * as tap from "tap";
3 import { Cancellation } from "@implab/core-amd/Cancellation";
3 import { Cancellation } from "@implab/core-amd/Cancellation";
4 import { delay } from "@implab/core-amd/safe";
4 import { delay } from "@implab/core-amd/safe";
5
5
6 const subj1 = observe<number>(({ next, complete }) => {
6 const subj1 = observe<number>(({ next, complete }) => {
7 next(1);
7 next(1);
8 complete();
8 complete();
9 next(2);
9 next(2);
10 });
10 });
11
11
12 const consumer1 = {
12 const consumer1 = {
13 sum: 0,
13 sum: 0,
14 next(v: number) {
14 next(v: number) {
15 this.sum += v;
15 this.sum += v;
16 }
16 }
17 };
17 };
18
18
19 subj1.subscribe(consumer1);
19 subj1.subscribe(consumer1);
20 tap.equal(consumer1.sum, 1, "Should get only one value");
20 tap.equal(consumer1.sum, 1, "Should get only one value");
21
21
22 subj1.subscribe(consumer1);
22 subj1.subscribe(consumer1);
23 tap.equal(consumer1.sum, 2, "Should get the value again");
23 tap.equal(consumer1.sum, 2, "Should get the value again");
24
24
25 const consumer2 = {
25 const consumer2 = {
26 value: 0,
26 value: 0,
27 completed: false,
27 completed: false,
28 next(v: number) { this.value = v; },
28 next(v: number) { this.value = v; },
29 complete() { this.completed = true; }
29 complete() { this.completed = true; }
30 };
30 };
31
31
32 let maps = 0;
32 let maps = 0;
33
33
34 subj1
34 subj1
35 .map(v => {
35 .map(v => {
36 tap.comment(`map1: ${v * 2}`);
36 tap.comment(`map1: ${v * 2}`);
37 maps++;
37 maps++;
38 return v * 2;
38 return v * 2;
39 })
39 })
40 .map(v => {
40 .map(v => {
41 tap.comment(`map2: ${v * 2}`);
41 tap.comment(`map2: ${v * 2}`);
42 maps++;
42 maps++;
43 return v * 2;
43 return v * 2;
44 })
44 })
45 .map(v => {
45 .map(v => {
46 tap.comment(`map3: ${v * 2}`);
46 tap.comment(`map3: ${v * 2}`);
47 maps++;
47 maps++;
48 return v * 2;
48 return v * 2;
49 })
49 })
50 .subscribe(consumer2);
50 .subscribe(consumer2);
51
51
52 tap.equal(consumer2.value, 8, "Should map");
52 tap.equal(consumer2.value, 8, "Should map");
53 tap.equal(maps, 3, "The map chain should not be executed after completion");
53 tap.equal(maps, 3, "The map chain should not be executed after completion");
54 tap.ok(consumer2.completed, "The completion signal should pass through");
54 tap.ok(consumer2.completed, "The completion signal should pass through");
55
55
56 const subj2 = observe<number>(({ next, complete }) => {
56 const subj2 = observe<number>(({ next, complete }) => {
57 [1, 2, 3, 4, 5].forEach(next);
57 [1, 2, 3, 4, 5].forEach(next);
58 complete();
58 complete();
59 return () => {
59 return () => {
60 tap.comment("subj2: unsubscribe");
60 tap.comment("subj2: unsubscribe");
61 };
61 };
62 });
62 });
63
63
64 const consumer3 = {
64 const consumer3 = {
65 even: 0,
65 even: 0,
66 odd: 0,
66 odd: 0,
67 completed: false,
67 completed: false,
68 subscribed: 0,
68 subscribed: 0,
69 unsubscribed: 0,
69 unsubscribed: 0,
70 next(v: "even" | "odd") {
70 next(v: "even" | "odd") {
71 this[v]++;
71 this[v]++;
72 },
72 },
73 complete() {
73 complete() {
74 this.completed = true;
74 this.completed = true;
75 },
75 },
76 subscribe() {
76 subscribe() {
77 this.subscribed++;
77 this.subscribed++;
78 },
78 },
79 unsubscribe() {
79 unsubscribe() {
80 this.unsubscribed++;
80 this.unsubscribed++;
81 }
81 }
82 };
82 };
83
83
84
84
85 const subj3 = subj2.pipe<"even" | "odd">(self => observe(({ next, complete, error }) => {
85 const subj3 = subj2.pipe<"even" | "odd">(self => observe(({ next, complete, error }) => {
86 consumer3.subscribe();
86 consumer3.subscribe();
87 let count = 0;
87 let count = 0;
88 const h = self.subscribe({
88 const h = self.subscribe({
89 next: val => {
89 next: val => {
90 if (val % 2 === 0)
90 if (val % 2 === 0)
91 next("odd");
91 next("odd");
92 else
92 else
93 next("even");
93 next("even");
94 if (++count === 4)
94 if (++count === 4)
95 complete();
95 complete();
96 },
96 },
97 complete,
97 complete,
98 error
98 error
99 });
99 });
100 return () => {
100 return () => {
101 consumer3.unsubscribe();
101 consumer3.unsubscribe();
102 h.unsubscribe();
102 h.unsubscribe();
103 };
103 };
104 }));
104 }));
105
105
106 subj3.subscribe(consumer3);
106 subj3.subscribe(consumer3);
107
107
108 tap.equal(consumer3.odd, 2, "Should get 2 odd elements");
108 tap.equal(consumer3.odd, 2, "Should get 2 odd elements");
109 tap.equal(consumer3.even, 2, "Should get 2 even elements");
109 tap.equal(consumer3.even, 2, "Should get 2 even elements");
110 tap.ok(consumer3.completed, "The sequence should completed");
110 tap.ok(consumer3.completed, "The sequence should completed");
111 tap.equal(consumer3.subscribed, 1, "The subscription should be done once");
111 tap.equal(consumer3.subscribed, 1, "The subscription should be done once");
112 tap.equal(consumer3.unsubscribed, 1, "The cleanup should be done after completion");
112 tap.equal(consumer3.unsubscribed, 1, "The cleanup should be done after completion");
113
113
114 subj2.reduce((a, b) => a + b).subscribe({
114 subj2.reduce((a, b) => a + b).subscribe({
115 next: val => tap.comment("subj2: reduce =", val),
115 next: val => tap.comment("subj2: reduce =", val),
116 complete: () => tap.comment("subj2: complete")
116 complete: () => tap.comment("subj2: complete")
117 });
117 });
118
118
119 tap.test("of(...) tests", async t => {
119 tap.test("of(...) tests", async t => {
120 await subj2.reduce((a, b) => a + b).next()
120 await subj2.reduce((a, b) => a + b).next()
121 .then(value => t.comment("subj2: next reduce=", value));
121 .then(value => t.comment("subj2: next reduce=", value));
122
122
123 await subj2.next().then(val => t.equal(val, 1, "Should peek the first element"));
123 await subj2.next().then(val => t.equal(val, 1, "Should peek the first element"));
124
124
125 const cancelled = new Cancellation(cancel => cancel());
125 const cancelled = new Cancellation(cancel => cancel());
126 await t.rejects(subj2.next(cancelled), "Cancelled next() method should fail");
126 await t.rejects(subj2.next(cancelled), "Cancelled next() method should fail");
127
127
128 await t.rejects(empty.next(), "Empty sequence should fail to get next element");
128 await t.rejects(empty.next(), "Empty sequence should fail to get next element");
129
129
130 await of(delay(1).then(() => 1), Promise.resolve(2), 3)
130 await of(delay(1).then(() => 1), Promise.resolve(2), 3)
131 .reduce<number[]>((a, x) => [...a, x], [])
131 .reduce<number[]>((a, x) => [...a, x], [])
132 .next()
132 .next()
133 .then(res => t.same(res, [1, 2, 3], "of(...) should keep the order"));
133 .then(res => t.same(res, [1, 2, 3], "of(...) should keep the order"));
134
134
135 const rejected = Promise.reject("DIE!");
135 const rejected = Promise.reject("DIE!");
136 rejected.catch(() => { }); // SAFE AND SOUND
136 rejected.catch(() => { }); // SAFE AND SOUND
137
137
138 await t.resolves(
138 await t.resolves(
139 of(Promise.resolve(1), rejected).next(),
139 of(Promise.resolve(1), rejected).next(),
140 "of(...) should emit non-rejected items"
140 "of(...) should emit non-rejected items"
141 );
141 );
142 await t.rejects(
142 await t.rejects(
143 of(1, Promise.reject("DIE!")).reduce((a) => a).next(),
143 of(1, Promise.reject("DIE!")).reduce((a) => a).next(),
144 "of(...) should terminate with error when a parameter is rejected"
144 "of(...) should terminate with error when a parameter is rejected"
145 );
145 );
146
146
147 t.same(await of(1,2,3).collect(), [1,2,3], ".collect() should return the collected sequence");
147 t.same(await of(1, 2, 3).collect(), [1, 2, 3], ".collect() should return the collected sequence");
148 await t.rejects(of(1,2,3).collect(cancelled), ".collect() should support cancellation");
148 await t.rejects(of(1, 2, 3).collect(cancelled), ".collect() should support cancellation");
149
149
150 }).catch(() => { });
150 }).catch(() => { });
151
151
152 tap.test(".tap() tests", async t => {
152 tap.test(".tap() tests", async t => {
153 const side: number[] = [];
153 const side: number[] = [];
154
154
155 of(1,2)
155 of(1, 2)
156 .tap({next: v => side.push(v), complete: () => side.push(0)})
156 .tap({ next: v => side.push(v), complete: () => side.push(0) })
157 .tap({next: v => side.push(v*v)})
157 .tap({ next: v => side.push(v * v) })
158 .subscribe({});
158 .subscribe({});
159
159
160 t.same(side, [1,1,2,4,0], ".tap() should be called in the order of registration");
160 t.same(side, [1, 1, 2, 4, 0], ".tap() should be called in the order of registration");
161
161
162 side.length = 0;
162 side.length = 0;
163
163
164 await new Promise<void>(resolve => {
164 await new Promise<void>(resolve => {
165 of(1,2,delay(1).then(() => 3))
165 of(1, 2, delay(1).then(() => 3))
166 .tap({next: v => side.push(v)})
166 .tap({ next: v => side.push(v) })
167 .tap({ next: v => v === 1 && resolve()})
167 .tap({ next: v => v === 1 && resolve() })
168 .subscribe({});
168 .subscribe({});
169 });
169 });
170
170
171 t.same(side, [1,2], ".tap() should be processed synchronously");
171 t.same(side, [1, 2], ".tap() should be processed synchronously");
172
172
173 }).catch(() => { });
174
175 tap.test(".while() tests", async t => {
176
177 const seq = of(1, 2, 3, 4).while(v => v <= 2);
178
179 t.same(await seq.collect(), [1, 2], "Should collect only taken elements");
180
181 const data: number[] = [];
182 let complete = 0;
183 seq.subscribe({
184 next: v => data.push(v),
185 complete: () => complete++
186 });
187
188 t.same(data, [1, 2], "Should receive only taken elements");
189 t.equal(complete, 1, "Complete should run once");
190
173 }).catch(() => {}); No newline at end of file
191 }).catch(() => { });
@@ -1,133 +1,133
1 plugins {
1 plugins {
2 id "org.implab.gradle-typescript" version "1.3.4"
2 id "org.implab.gradle-typescript" version "1.3.4"
3 id "ivy-publish"
3 id "ivy-publish"
4 }
4 }
5
5
6 def container = "djx-playground"
6 def container = "djx-playground"
7
7
8 configurations {
8 configurations {
9 npmLocal
9 npmLocal
10 }
10 }
11
11
12 dependencies {
12 dependencies {
13 npmLocal project(":djx")
13 npmLocal project(":djx")
14 }
14 }
15
15
16 def bundleDir = fileTree(layout.buildDirectory.dir("bundle")) {
16 def bundleDir = fileTree(layout.buildDirectory.dir("bundle")) {
17 builtBy "bundle"
17 builtBy "bundle"
18 }
18 }
19
19
20 typescript {
20 typescript {
21 compilerOptions {
21 compilerOptions {
22 lib = ["es5", "dom", "scripthost", "es2015.promise", "es2015.symbol", "es2015.iterable"]
22 lib = ["es5", "dom", "scripthost", "es2015.promise", "es2015.symbol", "es2015.iterable"]
23 // listFiles = true
23 // listFiles = true
24 strict = true
24 strict = true
25 types = ["requirejs", "@implab/dojo-typings", "@implab/djx"]
25 types = ["requirejs", "@implab/dojo-typings", "@implab/djx"]
26 module = "amd"
26 module = "amd"
27 it.target = "es5"
27 it.target = "es5"
28 experimentalDecorators = true
28 experimentalDecorators = true
29 noUnusedLocals = false
29 noUnusedLocals = false
30 jsx = "react"
30 jsx = "react"
31 jsxFactory = "createElement"
31 jsxFactory = "createElement"
32 moduleResolution = "node"
32 moduleResolution = "node"
33 // dojo-typings are sick
33 // dojo-typings are sick
34 skipLibCheck = true
34 skipLibCheck = true
35 // traceResolution = true
35 // traceResolution = true
36 // baseUrl = "./"
36 // baseUrl = "./"
37 // paths = [ "*": [ "$projectDir/src/typings/*" ] ]
37 // paths = [ "*": [ "$projectDir/src/typings/*" ] ]
38 // baseUrl = "$projectDir/src/typings"
38 // baseUrl = "$projectDir/src/typings"
39 // typeRoots = ["$projectDir/src/typings"]
39 // typeRoots = ["$projectDir/src/typings"]
40 }
40 }
41 tscCmd = "$projectDir/node_modules/.bin/tsc"
41 tscCmd = "$projectDir/node_modules/.bin/tsc"
42 tsLintCmd = "$projectDir/node_modules/.bin/tslint"
42 tsLintCmd = "$projectDir/node_modules/.bin/tslint"
43 esLintCmd = "$projectDir/node_modules/.bin/eslint"
43 esLintCmd = "$projectDir/node_modules/.bin/eslint"
44 }
44 }
45
45
46 tasks.matching{ it.name =~ /^configureTs/ }.configureEach {
46 tasks.matching{ it.name =~ /^configureTs/ }.configureEach {
47 compilerOptions {
47 compilerOptions {
48 if (symbols != 'none') {
48 if (symbols != 'none') {
49 sourceMap = true
49 sourceMap = true
50 switch(symbols) {
50 switch(symbols) {
51 case "local":
51 case "local":
52 sourceRoot = ( isWindows ? "file:///" : "file://" ) + it.rootDir
52 sourceRoot = ( isWindows ? "file:///" : "file://" ) + it.rootDir
53 break;
53 break;
54 }
54 }
55 }
55 }
56 }
56 }
57 }
57 }
58
58
59 npmInstall {
59 task npmInstallLocalDeps {
60 //npmInstall.dependsOn it
60 npmInstall.dependsOn it
61 dependsOn configurations.npmLocal
61 dependsOn configurations.npmLocal
62
62
63 doFirst {
63 doFirst {
64 configurations.npmLocal.each { f ->
64 configurations.npmLocal.each { f ->
65 exec {
65 exec {
66 commandLine "npm", "install", f, "--save-dev"
66 commandLine "npm", "install", f, "--save-dev"
67 }
67 }
68 }
68 }
69 }
69 }
70 }
70 }
71
71
72 clean {
72 clean {
73 doFirst {
73 doFirst {
74 delete "$buildDir/bundle"
74 delete "$buildDir/bundle"
75 }
75 }
76 }
76 }
77
77
78
78
79 task processResourcesBundle(type: Copy) {
79 task processResourcesBundle(type: Copy) {
80 from "src/bundle"
80 from "src/bundle"
81 into layout.buildDirectory.dir("bundle")
81 into layout.buildDirectory.dir("bundle")
82 }
82 }
83
83
84 task copyModules(type: Copy) {
84 task copyModules(type: Copy) {
85 dependsOn npmInstall
85 dependsOn npmInstall
86 into layout.buildDirectory.dir("bundle/js");
86 into layout.buildDirectory.dir("bundle/js");
87
87
88 def pack = { String jsmod ->
88 def pack = { String jsmod ->
89 into(jsmod) {
89 into(jsmod) {
90 from npm.module(jsmod)
90 from npm.module(jsmod)
91 }
91 }
92 }
92 }
93
93
94
94
95 pack("@implab/djx")
95 pack("@implab/djx")
96 pack("@implab/core-amd")
96 pack("@implab/core-amd")
97 into("@js-joda/core") {
97 into("@js-joda/core") {
98 from(npm.module("@js-joda/core/dist"))
98 from(npm.module("@js-joda/core/dist"))
99 }
99 }
100 pack("dojo")
100 pack("dojo")
101 pack("dijit")
101 pack("dijit")
102 into("rxjs") {
102 into("rxjs") {
103 from(npm.module("rxjs/dist/bundles"))
103 from(npm.module("rxjs/dist/bundles"))
104 }
104 }
105 from npm.module("requirejs/require.js")
105 from npm.module("requirejs/require.js")
106 }
106 }
107
107
108 npmPublish {
108 npmPublish {
109 enabled = false
109 enabled = false
110 }
110 }
111
111
112 task copyApp(type: Copy) {
112 task copyApp(type: Copy) {
113 dependsOn assemble
113 dependsOn assemble
114 from typescript.assemblyDir
114 from typescript.assemblyDir
115 into layout.buildDirectory.dir("bundle/js/app")
115 into layout.buildDirectory.dir("bundle/js/app")
116 }
116 }
117
117
118 task bundle {
118 task bundle {
119 dependsOn copyModules, processResourcesBundle, copyApp
119 dependsOn copyModules, processResourcesBundle, copyApp
120 }
120 }
121
121
122 task up(type: Exec) {
122 task up(type: Exec) {
123 dependsOn bundle
123 dependsOn bundle
124 commandLine "podman", "run", "--rm", "-d",
124 commandLine "podman", "run", "--rm", "-d",
125 "--name", container,
125 "--name", container,
126 "-p", "2078:80",
126 "-p", "2078:80",
127 "-v", "$buildDir/bundle:/srv/www/htdocs",
127 "-v", "$buildDir/bundle:/srv/www/htdocs",
128 "registry.implab.org/implab/apache2:latest"
128 "registry.implab.org/implab/apache2:latest"
129 }
129 }
130
130
131 task stop(type: Exec) {
131 task stop(type: Exec) {
132 commandLine "podman", "stop", container
132 commandLine "podman", "stop", container
133 } No newline at end of file
133 }
General Comments 0
You need to be logged in to leave comments. Login now