Skip to content

Commit 9a20df9

Browse files
committed
Add useSession for reactively tracking Meteor Session variables
Closes #2
1 parent 574810c commit 9a20df9

File tree

5 files changed

+151
-2
lines changed

5 files changed

+151
-2
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,29 @@ and bound like any writable store using the `$` operator:
130130
131131
<p>Value is {$store}</p>
132132
```
133+
134+
### Session variables
135+
136+
If you are using Meteor [Session](https://docs.meteor.com/api/session.html)
137+
variables, these can be exposed as a reactive Svelte store using the
138+
`useSession` hook. The first argument is the session key to expose, and the
139+
optional second argument allows you to set a default value for the session
140+
variable, as an added convenience.
141+
142+
This function is only available if the `session` package has been added.
143+
144+
```svelte
145+
<script>
146+
import { useSession } from 'meteor/rdb:svelte-meteor-data';
147+
148+
const store = useSession('mySessionKey', 'initial');
149+
150+
// The above is equivalent to:
151+
//Session.setDefault('mySessionKey', 'initial')
152+
//const store = useSession('mySessionKey');
153+
</script>
154+
155+
<input type="text" bind:value={$store} />
156+
157+
<p>Value is {$store}</p>
158+
```

index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,9 @@ if (Package['reactive-var']) {
1010
import './reactive-var';
1111
}
1212

13+
if (Package['session'] && Meteor.isClient) {
14+
export { default as useSession } from './use-session';
15+
}
16+
1317
// Import this last, since it overwrites the built-in Tracker.autorun
1418
import './autorun';

package.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package.describe({
22
name: 'rdb:svelte-meteor-data',
3-
version: '0.0.1',
3+
version: '0.1.0',
44
summary: 'Reactively track Meteor data inside Svelte components',
55
git: 'https://github.com/rdb/svelte-meteor-data',
66
documentation: 'README.md'
@@ -12,6 +12,7 @@ Package.onUse(function(api) {
1212
api.use('tracker');
1313
api.use('svelte:[email protected]_1');
1414
api.use('reactive-var', {weak: true});
15+
api.use('session', 'client', {weak: true});
1516
api.use('mongo', {weak: true});
1617
api.mainModule('index.js');
1718
});
@@ -21,5 +22,7 @@ Package.onTest(function(api) {
2122
api.use('tinytest');
2223
api.use('rdb:svelte-meteor-data');
2324
api.use('reactive-var');
24-
api.mainModule('reactive-var.tests.js');
25+
api.use('session', 'client');
26+
api.addFiles('reactive-var.tests.js');
27+
api.addFiles('use-session.tests.js', 'client');
2528
});

use-session.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* This function wraps a Meteor Session variable as a Svelte store.
3+
*/
4+
5+
import { Session } from "meteor/session";
6+
import { EJSON } from "meteor/ejson";
7+
8+
let nextId = 1;
9+
10+
const parse = serialized =>
11+
(serialized !== undefined && serialized !== 'undefined')
12+
? EJSON.parse(serialized)
13+
: undefined;
14+
15+
export default function useSession(key, defaultValue) {
16+
if (arguments.length > 1) {
17+
Session.setDefault(key, defaultValue);
18+
}
19+
20+
return {
21+
subscribe(set) {
22+
Session._ensureKey(key);
23+
const dep = Session.keyDeps[key];
24+
if (Object.prototype.hasOwnProperty.call(Session.keys, key)) {
25+
set(parse(Session.keys[key]));
26+
}
27+
28+
const id = `svelte-session-${nextId++}`;
29+
dep._dependentsById[id] = {
30+
_id: id,
31+
invalidate: () => {
32+
set(parse(Session.keys[key]));
33+
},
34+
};
35+
36+
return () => {
37+
delete dep._dependentsById[id];
38+
};
39+
},
40+
set(value) {
41+
Session.set(key, value);
42+
},
43+
};
44+
};

use-session.tests.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { Tinytest } from "meteor/tinytest";
2+
import { Session } from "meteor/session";
3+
4+
import { default as useSession } from './use-session';
5+
6+
7+
Tinytest.add('useSession default value', function (test) {
8+
Session.delete('test1');
9+
10+
useSession('test1');
11+
12+
test.isFalse(Object.prototype.hasOwnProperty.call(Session.keys, 'test1'),
13+
'Should not set default value without second arg');
14+
15+
useSession('test2', 'value1')
16+
test.equal(Session.keys['test2'], '"value1"',
17+
'Should set default value with second arg');
18+
19+
useSession('test2', 'value2');
20+
test.equal(Session.keys['test2'], '"value1"',
21+
'Second arg should not overwrite existing set value');
22+
23+
useSession('test2');
24+
test.equal(Session.keys['test2'], '"value1"',
25+
'Undefined second arg should overwrite existing value');
26+
});
27+
28+
29+
Tinytest.add('useSession reactivity', function (test) {
30+
Session.delete('test3');
31+
32+
const store = useSession('test3', 'initial');
33+
34+
let setterCalled = 0;
35+
let setterCalledWith;
36+
37+
function setter(value) {
38+
setterCalled += 1;
39+
setterCalledWith = value;
40+
}
41+
42+
const unsub = store.subscribe(setter);
43+
test.equal(setterCalled, 1, 'Subscribe should have called setter once');
44+
test.equal(setterCalledWith, "initial", 'Subscribe should have set initial value');
45+
46+
store.set("initial");
47+
test.equal(setterCalled, 1, 'Setter should not be called if value is not changed');
48+
49+
Session.set("test3", "initial");
50+
test.equal(setterCalled, 1, 'Setter should not be called if value is not changed via Session');
51+
52+
Session.get("test3");
53+
test.equal(setterCalled, 1, 'Setter should not be called on Session.get()');
54+
55+
store.set("new");
56+
test.equal(setterCalled, 2, 'Setter should be called if value is changed via set()');
57+
test.equal(setterCalledWith, "new", 'Setter should be called with new value on set()');
58+
59+
Session.set("test3", "newer");
60+
test.equal(setterCalled, 3, 'Setter should be called if value is changed via Session.set()');
61+
test.equal(setterCalledWith, "newer", 'Setter should be called with new value on Session.set()');
62+
63+
unsub();
64+
65+
test.equal(setterCalled, 3, 'Unsubscribe should not call setter');
66+
67+
store.set("newest");
68+
test.equal(setterCalled, 3, 'Setter may not be called after unsubscribe');
69+
70+
Session.set("test3", "newest");
71+
test.equal(setterCalled, 3, 'Setter may not be called after unsubscribe');
72+
});

0 commit comments

Comments
 (0)