Skip to content

Commit 2c5f695

Browse files
committed
update
1 parent 1c4087b commit 2c5f695

File tree

4 files changed

+221
-9
lines changed

4 files changed

+221
-9
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
language: node_js
22
sudo: false
33
node_js:
4-
- '6.6.0'
4+
- '7.10.0'
55

66
cache:
77
yarn: true

README.md

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,73 @@
1-
## TODO
1+
## stone.js
2+
3+
[![Build Status](https://travis-ci.org/axetroy/stone.svg?branch=master)](https://travis-ci.org/axetroy/stone)
4+
[![Dependency](https://david-dm.org/axetroy/stone.svg)](https://david-dm.org/axetroy/stone)
5+
![License](https://img.shields.io/badge/license-MIT-green.svg)
6+
[![Prettier](https://img.shields.io/badge/Code%20Style-Prettier-green.svg)](https://github.com/prettier/prettier)
7+
![Node](https://img.shields.io/badge/node-%3E=6.0-blue.svg?style=flat-square)
8+
[![npm version](https://badge.fury.io/js/stone.svg)](https://badge.fury.io/js/stone)
9+
10+
build data stuck like a stone, Sturdy!
11+
12+
## Usage
13+
14+
```bash
15+
npm install @axetroy/stone
16+
```
17+
18+
```javascript
19+
import Stone from '@axetroy/stone';
20+
21+
const userInfo = new Stone({username:'axetroy'});
22+
23+
// back
24+
userInfo.username = 'admin'; // it will throw an error.
25+
26+
// good
27+
userInfo.set('username', 'admin'); // this is ok.
28+
29+
console.log(userInfo.username); // print "admin"
30+
```
31+
32+
## API
33+
34+
### .set(key:string,value:any):this
35+
36+
set a value.
37+
38+
### .remove(key:string):this
39+
40+
remove a key
41+
42+
### .subscribe(func:Function):Function
43+
44+
subscribe the stone change
45+
46+
### .watch(key:string,func:Function):Function
47+
48+
watch specify key change, base on ``.subscribe``
49+
50+
### .keys():string[]
51+
52+
return the keys list you set
53+
54+
### .values():any[]
55+
56+
return the values list you set
57+
58+
- stringify():string
59+
60+
convert to string, base on ``JSON.stringify()``
61+
62+
## Contributing
63+
64+
```bash
65+
git clone https://github.com/axetroy/stone.git
66+
cd ./stone
67+
yarn
68+
yarn run dev
69+
```
70+
71+
## License
72+
73+
The [MIT License](https://github.com/axetroy/stone/blob/master/LICENSE)

index.js

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ export default class Stone {
33
this.subscriber = [];
44
this.state = { ...{}, ...obj };
55

6-
Object.defineProperty(this, 'state', { enumerable: false });
7-
Object.defineProperty(this, 'subscriber', { enumerable: false });
6+
Object.defineProperty(this, 'state', {
7+
enumerable: false,
8+
configurable: false
9+
});
10+
Object.defineProperty(this, 'subscriber', {
11+
enumerable: false,
12+
configurable: false
13+
});
814
Object.defineProperty(this, 'length', {
15+
configurable: true,
916
enumerable: false,
10-
configurable: false,
1117
get() {
1218
return this.keys().length;
1319
}
@@ -28,7 +34,7 @@ export default class Stone {
2834
if (!Object.getOwnPropertyDescriptor(this, key)) {
2935
Object.defineProperty(this, key, {
3036
enumerable: true,
31-
configurable: false,
37+
configurable: true,
3238
get() {
3339
return this.state[key];
3440
},
@@ -54,8 +60,24 @@ export default class Stone {
5460
}
5561

5662
subscribe(func) {
63+
func.__id__ = Math.random().toFixed(5);
5764
this.subscriber.push(func);
58-
return this;
65+
return () => {
66+
const index = this.subscriber.findIndex(handler => {
67+
return handler.__id__ && handler.__id__ === func.__id__;
68+
});
69+
if (index >= 0) {
70+
this.subscriber.splice(index, 1);
71+
}
72+
};
73+
}
74+
75+
watch(key, callback) {
76+
return this.subscribe((_key, oldValue, newValue) => {
77+
if (key === _key) {
78+
callback.call(this, oldValue, newValue);
79+
}
80+
});
5981
}
6082

6183
// Object method
@@ -64,10 +86,20 @@ export default class Stone {
6486
}
6587

6688
values() {
67-
return Object.values(this.state);
89+
let values = [];
90+
for (let key in this.state) {
91+
if (this.state.hasOwnProperty(key)) {
92+
values.push(this.state[key]);
93+
}
94+
}
95+
return values;
6896
}
6997

7098
toString() {
7199
return this.state.toString();
72100
}
101+
102+
stringify() {
103+
return JSON.stringify(this.state);
104+
}
73105
}

test/unit/stone.test.js

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,117 @@ test('basic', t => {
2525
});
2626
});
2727

28-
test('set an prototype property', t => {
28+
test('set an prototype property, like length', t => {
2929
stone.set('length', 333); // can't cover the private property
3030
t.is(stone.length, 2);
3131
t.is(Object.keys(stone.state).length, 2);
3232
t.deepEqual(Object.keys(stone), ['username']);
3333
});
34+
35+
test('.remove(), delete a property', t => {
36+
stone.remove('username');
37+
t.is(Object.keys(stone).length, 0);
38+
t.deepEqual(stone.username, void 0);
39+
t.pass();
40+
});
41+
42+
test('.keys(), get the keys of stone', t => {
43+
t.is(Object.keys(stone).length, 1);
44+
stone.set('age', 18);
45+
t.is(Object.keys(stone).length, 2);
46+
t.pass();
47+
});
48+
49+
test('.values(), get the values of stone', t => {
50+
t.is(stone.values().length, 1);
51+
t.deepEqual(stone.values(), ['axetroy']);
52+
t.pass();
53+
});
54+
55+
test('.stringify(), get the string of this stone', t => {
56+
t.deepEqual(stone.stringify(), JSON.stringify({ username: 'axetroy' }));
57+
t.pass();
58+
});
59+
60+
test('.toString(), covert the object to string, cover native method', t => {
61+
t.deepEqual(stone.toString(), '[object Object]');
62+
t.pass();
63+
});
64+
65+
test('.subscribe(), subscribe the stone change', t => {
66+
stone.subscribe((key, oldValue, newValue) => {
67+
t.deepEqual(key, 'age');
68+
t.deepEqual(oldValue, void 0);
69+
t.deepEqual(newValue, 18);
70+
});
71+
stone.set('age', 18);
72+
t.pass();
73+
});
74+
75+
test('.subscribe(), trigger the handler like expect', t => {
76+
let invokeTimes = 0;
77+
stone.subscribe((key, oldValue, newValue) => {
78+
invokeTimes++;
79+
});
80+
t.deepEqual(invokeTimes, 0);
81+
stone.set('age', 18);
82+
t.deepEqual(invokeTimes, 1);
83+
stone.set('username', 'hello world');
84+
t.deepEqual(invokeTimes, 2);
85+
t.pass();
86+
});
87+
88+
test('.subscribe(), it should return a function the cancel subscribe', t => {
89+
let invokeTimes = 0;
90+
let cancelSubscribe = stone.subscribe((key, oldValue, newValue) => {
91+
invokeTimes++;
92+
});
93+
t.deepEqual(invokeTimes, 0);
94+
stone.set('age', 18);
95+
t.deepEqual(invokeTimes, 1);
96+
cancelSubscribe();
97+
stone.set('age', 11);
98+
stone.set('age', 12);
99+
stone.set('age', 13);
100+
stone.set('age', 14);
101+
t.deepEqual(invokeTimes, 1); // will not call function again
102+
t.pass();
103+
});
104+
105+
test('.watch(key), watch specify key change, base on .subscribe()', t => {
106+
let usernameWatcherCallTimes = 0;
107+
const cancel = stone.watch('username', () => {
108+
usernameWatcherCallTimes = usernameWatcherCallTimes + 1;
109+
});
110+
t.deepEqual(usernameWatcherCallTimes, 0);
111+
stone.set('age', 18);
112+
t.deepEqual(usernameWatcherCallTimes, 0);
113+
stone.set('username', 'hello world');
114+
t.deepEqual(usernameWatcherCallTimes, 1);
115+
116+
// cancel the watcher
117+
cancel();
118+
119+
stone.set('username', 'a');
120+
stone.set('username', 'b');
121+
stone.set('username', 'c');
122+
t.deepEqual(usernameWatcherCallTimes, 1);
123+
t.deepEqual(stone.username, 'c');
124+
t.pass();
125+
});
126+
127+
test('.set(), set a property', t => {
128+
stone.set('age', 18);
129+
t.is(Object.keys(stone).length, 2);
130+
t.deepEqual(stone.username, 'axetroy');
131+
t.deepEqual(stone.age, 18);
132+
133+
// can't reset the value
134+
t.throws(() => {
135+
stone.age = 22; // it should throw an error
136+
});
137+
138+
t.deepEqual(stone.age, 18);
139+
140+
t.pass();
141+
});

0 commit comments

Comments
 (0)