Skip to content

Commit dd84b9a

Browse files
committed
update
1 parent e90dac7 commit dd84b9a

9 files changed

+223
-73
lines changed

example/be.js

-32
This file was deleted.

example/index.html

+3-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<title>Title</title>
66
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.1.3/axios.min.js" type="text/javascript"></script>
77
<script src="https://cdn.jsdelivr.net/npm/[email protected]/jsonfn.min.js"></script>
8-
<script type="module" src="../src/builder.js"></script>
8+
<script type="module" src="../src/builder.browser.js"></script>
99
</head>
1010
<body>
1111
<script>
@@ -16,9 +16,8 @@
1616
return data
1717
}
1818
const Model = HmmBuilder(send)
19-
const UserModel = Model('user');
20-
await UserModel.insertOne({user: 'admin', password: '123'})
21-
const admin = await UserModel.findOne({user: 'admin'})
19+
await Model('user').insertOne({user: 'admin', password: '123'})
20+
const admin = await Model('user').findOne({user: 'admin'})
2221
console.log('admin', admin)
2322
}
2423
</script>

example/index.js

+28-18
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,32 @@
1-
const HmmBuilderFactory = require('../src/builder')
1+
const express = require('express');
2+
const bodyParser = require('body-parser');
3+
const {MongoClient} = require('mongodb');
4+
const jsonFn = require('json-fn');
5+
const executorFac = require('../src/executor')
26

3-
function fetch(url, data) {
4-
return new Promise((resolve, reject) => {
5-
// resolve(data)
6-
reject('Oops, something bad')
7-
})
8-
}
7+
const start = async () => {
8+
// db init
9+
const client = new MongoClient('mongodb://localhost:27017');
10+
const database = client.db('hmm')
11+
const dbDriver = new Proxy({}, {
12+
get(__, p) {return database.collection(p)}
13+
})
14+
const app = express()
15+
16+
// hmm init
17+
const hmm = executorFac(dbDriver, { logLevel: 'log' })
18+
app.post('/api', bodyParser.raw({limit: '50mb', type: () => true}),
19+
async (req, res) =>
20+
hmm(jsonFn.parse(req.body.toString()))
21+
.then(rs => res.json(rs))
22+
.catch(e => res.status(400).send(e.message)));
923

10-
async function main() {
11-
const send = data => fetch('/execute', data)
12-
const Model = HmmBuilderFactory(send)
24+
// these stuff must call after hmm
25+
app.use(bodyParser.json())
26+
app.use(bodyParser.urlencoded({ extended: true }))
27+
app.use(express.static('.'))
1328

14-
try {
15-
const FileModel = Model('file');
16-
const files = await FileModel.find({name: 'x'}).sort({date: -1})
17-
console.log('file', files)
18-
} catch (e) {
19-
console.error('failed to get files', e)
20-
}
29+
app.listen(3000, () => console.log('http://localhost:3000/example'))
2130
}
22-
main()
31+
32+
start()

example/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "example",
33
"version": "1.0.0",
44
"description": "",
5-
"main": "index.js",
5+
"main": "../src/test1.js",
66
"scripts": {
77
"test": "echo \"Error: no test specified\" && exit 1"
88
},

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "client side mongoose like database query interface",
55
"main": "./src/executor.js",
66
"scripts": {
7-
"dev": "node example/be.js"
7+
"dev": "node example/index.js"
88
},
99
"repository": {
1010
"type": "git",

src/builder.browser.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
function HmmBuilderFactory(send) {
2+
return function Model(name) {
3+
const model = {__name: `Model_${name}`}
4+
let payload = {model: name, fns: []}
5+
return new Proxy(model, {
6+
get(target, p) {
7+
switch (p) {
8+
case 'then':
9+
return (resolve, reject) => send(payload).then(resolve).catch(reject)
10+
case 'catch':
11+
return (reject) => send(payload).then(() => {}).catch(reject)
12+
case 'toString':
13+
return () => JSON.stringify({model, payload})
14+
default:
15+
return function next(...args) {
16+
payload.fns.push({method: p, args})
17+
return this;
18+
}
19+
}
20+
}
21+
})
22+
}
23+
}
24+
window.HmmBuilder = HmmBuilderFactory
25+
26+
27+

src/builder.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
function HmmBuilderFactory(send) {
22
return function Model(name) {
33
const model = {__name: `Model_${name}`}
4-
let payload = {model: name, fns: []}
4+
const payload = {model: name, fns: []}
5+
const noop = () => {}
56
return new Proxy(model, {
67
get(target, p) {
78
switch (p) {
89
case 'then':
9-
return (resolve, reject) => {
10-
let sendPayload = payload;
11-
payload = {model: name, fns: []}
12-
send(sendPayload).then(resolve).catch(reject)
13-
}
10+
return (resolve, reject) => send(payload).then(resolve).catch(reject)
11+
case 'catch':
12+
return (reject) => send(payload).then(noop).catch(reject)
13+
case 'toString':
14+
return () => JSON.stringify({model, payload})
1415
default:
1516
return function next(...args) {
16-
payload.fns.push({method: p, args})
17+
payload.fns.push({m: p, args})
1718
return this;
1819
}
1920
}
2021
}
2122
})
2223
}
2324
}
24-
window.HmmBuilder = HmmBuilderFactory
25+
module.exports = HmmBuilderFactory
2526

2627

2728

src/builder.test.js

+153-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,157 @@
1-
const hmmBuilder = require('./builder')
1+
const HmmBuild = require("./builder");
2+
3+
const delay = ms => new Promise(rs => setTimeout(rs, ms))
4+
const fetchOk = (url, data) => new Promise((resolve, reject) => resolve(data))
5+
const fetchNOk = (url, data) => new Promise((resolve, reject) => reject('something bad'))
26

37
describe('hmm-builder', function () {
4-
it('should call send method', async () => {
5-
const mockPost = jest.fn(payload => "RESPONSE:" + JSON.stringify(payload))
6-
const Model = hmmBuilder(mockPost)
7-
const UserModel = Model('user')
8-
const rs = await UserModel.find({_id: 1}, { username: 1, password: 1 }).sort({createdDate: -1}).limit(10)
9-
expect(mockPost.mock.calls.length).toBe(1)
8+
describe('send', function () {
9+
it('should fail on sync send method', async () => {
10+
const send = payload => "RESPONSE:" + JSON.stringify(payload)
11+
const mockSend = jest.fn(send)
12+
const Model = HmmBuild(mockSend)
13+
let throwException;
14+
try {
15+
await Model('user').find({_id: 1}, { username: 1, password: 1 }).sort({createdDate: -1}).limit(10)
16+
throwException = false;
17+
} catch (e) {
18+
throwException = true;
19+
}
20+
expect(mockSend.mock.calls.length).toBe(1)
21+
expect(throwException).toBe(true)
22+
})
23+
it('should work on async send method', async () => {
24+
const send = async payload => "RESPONSE:" + JSON.stringify(payload)
25+
const mockSend = jest.fn(send)
26+
const Model = HmmBuild(mockSend)
27+
let throwException;
28+
try {
29+
await Model('user').find({_id: 1}, { username: 1, password: 1 }).sort({createdDate: -1}).limit(10)
30+
throwException = false;
31+
} catch (e) {
32+
throwException = true;
33+
}
34+
expect(mockSend.mock.calls.length).toBe(1)
35+
expect(throwException).toBe(false)
36+
})
37+
it('should work on Promise send method', async () => {
38+
const send = payload => new Promise(resolve => resolve("RESPONSE:" + JSON.stringify(payload)))
39+
const mockSend = jest.fn(send)
40+
const Model = HmmBuild(mockSend)
41+
let throwException;
42+
try {
43+
await Model('user').find({_id: 1}, { username: 1, password: 1 }).sort({createdDate: -1}).limit(10)
44+
throwException = false;
45+
} catch (e) {
46+
throwException = true;
47+
}
48+
expect(mockSend.mock.calls.length).toBe(1)
49+
expect(throwException).toBe(false)
50+
})
51+
})
52+
describe('usage', function () {
53+
describe('invoke', function () {
54+
describe('Promise', () => {
55+
it('should work with Promise#then', async () => {
56+
const send = data => fetchOk('/execute', data)
57+
const Model = HmmBuild(send)
58+
const datas = []
59+
Model('file').updateOne({name: 'x'}, {age: 1}).then(data => datas.push(data))
60+
await delay(10)
61+
expect(datas.length).toBe(1)
62+
})
63+
it('should catch exception with Promise#catch', async () => {
64+
const send = data => fetchNOk('/execute', data)
65+
const Model = HmmBuild(send)
66+
const datas = []
67+
const errors = []
68+
Model('file').updateOne({name: 'x'}, {age: 1}).catch(e => errors.push(e))
69+
await delay(10)
70+
expect(datas.length).toBe(0)
71+
expect(errors.length).toBe(1)
72+
})
73+
it('should work with Promise#then#catch (1)', async () => {
74+
const send = data => fetchOk('/execute', data)
75+
const Model = HmmBuild(send)
76+
const datas = []
77+
const errors = []
78+
Model('file').updateOne({name: 'x'}, {age: 1}).then(data => datas.push(data)).catch(e => errors.push(e))
79+
await delay(10)
80+
expect(datas.length).toBe(1)
81+
expect(errors.length).toBe(0)
82+
})
83+
it('should work with Promise#then#catch (1)', async () => {
84+
const send = data => fetchNOk('/execute', data)
85+
const Model = HmmBuild(send)
86+
const datas = []
87+
const errors = []
88+
Model('file').updateOne({name: 'x'}, {age: 1}).then(data => datas.push(data)).catch(e => errors.push(e))
89+
await delay(10)
90+
expect(datas.length).toBe(0)
91+
expect(errors.length).toBe(1)
92+
})
93+
});
94+
95+
describe('async/await', () => {
96+
it('should work', async () => {
97+
const send = data => fetchOk('/execute', data)
98+
const Model = HmmBuild(send)
99+
const datas = []
100+
const errors = []
101+
try {
102+
const data = await Model('file').updateOne({name: 'x'}, {age: 1})
103+
datas.push(data)
104+
} catch (e) {
105+
errors.push(e)
106+
}
107+
expect(datas.length).toBe(1)
108+
expect(errors.length).toBe(0)
109+
})
110+
it('should catch exception', async () => {
111+
const send = data => fetchNOk('/execute', data)
112+
const Model = HmmBuild(send)
113+
const datas = []
114+
const errors = []
115+
try {
116+
const data = await Model('file').updateOne({name: 'x'}, {age: 1})
117+
datas.push(data)
118+
} catch (e) {
119+
errors.push(e)
120+
}
121+
expect(datas.length).toBe(0)
122+
expect(errors.length).toBe(1)
123+
})
124+
})
125+
})
126+
127+
describe('payload', function() {
128+
it('should correct when using isolate Model instance', async () => {
129+
const send = data => fetchOk('/execute', data)
130+
const Model = HmmBuild(send)
131+
const datas = {}
132+
Model('file').updateOne({name: 'x'}, {age: 1}).then(data => datas[0] = data)
133+
Model('file').find({age: {$lte: 20}}).sort({date: -1}).skip(20).limit(10).then(data => datas[1] = data)
134+
Model('file').find({name: 'Josh'}).sort({age: 1}).limit(100).then(data => datas[2] = data)
135+
await delay(50)
136+
// console.log(datas)
137+
expect(datas[0].fns.length).toBe(1)
138+
expect(datas[1].fns.length).toBe(4)
139+
expect(datas[2].fns.length).toBe(3)
140+
})
141+
it('should messed-up when using shared Model instance', async () => {
142+
const send = data => fetchOk('/execute', data)
143+
const Model = HmmBuild(send)
144+
const FileModel = Model('file') // <- shared model instance
145+
const datas = {}
146+
FileModel.updateOne({name: 'x'}, {age: 1}).then(data => datas[0] = data)
147+
FileModel.find({age: {$lte: 20}}).sort({date: -1}).skip(20).limit(10).then(data => datas[1] = data)
148+
FileModel.find({name: 'Josh'}).sort({age: 1}).limit(100).then(data => datas[2] = data)
149+
await delay(50)
150+
// console.log(datas)
151+
expect(datas[0].fns.length).not.toBe(1)
152+
expect(datas[1].fns.length).not.toBe(4)
153+
expect(datas[2].fns.length).not.toBe(3)
154+
})
155+
})
10156
})
11157
})

src/executor.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ module.exports = dbDriver => async payload => {
22
const {model, fns} = payload
33
let imme = dbDriver[model]
44
for (const fn of fns) {
5-
const fnI = imme[fn.method]
6-
imme = fnI.apply(imme, fn.args)
5+
imme = imme[fn.m].apply(imme, fn.args)
76
}
87
return imme
98
}

0 commit comments

Comments
 (0)