Skip to content

Commit a1ce9bd

Browse files
committed
first commit
0 parents  commit a1ce9bd

File tree

8 files changed

+1068
-0
lines changed

8 files changed

+1068
-0
lines changed

.gitignore

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.idea
2+
*.iml
3+
npm-debug.log
4+
dump.rdb
5+
node_modules
6+
results.tap
7+
results.xml
8+
npm-shrinkwrap.json
9+
config.json
10+
.DS_Store
11+
*/.DS_Store
12+
*/*/.DS_Store
13+
._*
14+
*/._*
15+
*/*/._*
16+
coverage.*
17+
lib-cov
18+

LICENSE

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Copyright (c) 2012-2014, Walmart and other contributors.
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above copyright
9+
notice, this list of conditions and the following disclaimer in the
10+
documentation and/or other materials provided with the distribution.
11+
* The names of any contributors may not be used to endorse or promote
12+
products derived from this software without specific prior written
13+
permission.
14+
15+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY
19+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
26+
* * *
27+
28+
The complete list of contributors can be found at: https://github.com/codedmart/catbox-rethinkdb/graphs/contributors

Makefile

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
test:
2+
@node node_modules/lab/bin/lab
3+
test-cov:
4+
@node node_modules/lab/bin/lab -t 100
5+
test-cov-html:
6+
@node node_modules/lab/bin/lab -r html -o coverage.html
7+
8+
.PHONY: test test-cov test-cov-html

README.md

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
catbox-redis
2+
============
3+
4+
RethinkDB adapter for catbox
5+
6+
## Options
7+
8+
- `host` - the Redis server hostname. Defaults to `'127.0.0.1'`.
9+
- `port` - the Redis server port or unix domain socket path. Defaults to `28015`.
10+
- `db` - the Redis database. Defaults to `catbox`
11+
- `table` - The RethinkDB table under the db to store. Defaults to `catbox`
12+
13+
## Tests
14+
15+
The test suite expects a RethinkDB server to be running on port 28015.
16+
17+
```sh
18+
rethinkdb && npm test
19+
```

index.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module.exports = require('./lib');
2+

lib/index.js

+283
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
var Hoek = require('hoek');
2+
var RethinkDB = require('rethinkdb');
3+
4+
// Declare internals
5+
6+
var internals = {};
7+
8+
9+
// TODO: add authKey options
10+
internals.defaults = {
11+
host: '127.0.0.1',
12+
port: 28015,
13+
db: 'catbox',
14+
table: 'catbox'
15+
};
16+
17+
18+
exports = module.exports = internals.Connection = function (options) {
19+
20+
Hoek.assert(this.constructor === internals.Connection, 'RethinkDB cache client must be instantiated using new');
21+
22+
this.settings = Hoek.applyToDefaults(internals.defaults, options);
23+
this.table = RethinkDB.db(this.settings.db).table(this.settings.table);
24+
this.client = null;
25+
this.isConnected = false;
26+
return this;
27+
};
28+
29+
30+
internals.Connection.prototype.createDb = function () {
31+
32+
var self = this;
33+
34+
return RethinkDB.dbList().run(this.client)
35+
.then(function(dbs) {
36+
37+
if (!Hoek.contain(dbs, self.settings.db)) {
38+
39+
return RethinkDB.dbCreate(self.settings.db).run(self.client);
40+
}
41+
});
42+
};
43+
44+
45+
internals.Connection.prototype.createTable = function () {
46+
47+
var self = this;
48+
49+
return RethinkDB.tableList().run(this.client)
50+
.then(function(tables) {
51+
52+
if (!Hoek.contain(tables, self.settings.table)) {
53+
54+
return RethinkDB.tableCreate(self.settings.table).run(self.client);
55+
}
56+
});
57+
};
58+
59+
60+
internals.Connection.prototype.createIndex = function () {
61+
62+
var self = this;
63+
64+
return RethinkDB.table(this.settings.table).indexList().run(this.client)
65+
.then(function(indexes) {
66+
67+
if (!Hoek.contain(indexes, 'expiresAt')) {
68+
69+
return self.table.indexCreate('expiresAt').run(self.client);
70+
}
71+
});
72+
};
73+
74+
75+
internals.Connection.prototype.start = function (callback) {
76+
77+
var self = this;
78+
79+
if (this.client) {
80+
return Hoek.nextTick(callback)();
81+
}
82+
83+
// Create client
84+
return RethinkDB.connect({
85+
86+
host: this.settings.host,
87+
port: this.settings.port,
88+
db: this.settings.db
89+
}, function(err, conn) {
90+
91+
if (err) {
92+
self.stop();
93+
return callback(new Error(err));
94+
}
95+
96+
self.isConnected = true;
97+
self.client = conn;
98+
99+
// Ensure table is created
100+
return self.createDb()
101+
.then(function() {
102+
return self.createTable();
103+
})
104+
.then(function() {
105+
return self.createIndex();
106+
})
107+
.then(function() {
108+
return callback();
109+
})
110+
.error(function(err) {
111+
return callback(new Error(err));
112+
});
113+
});
114+
};
115+
116+
117+
internals.Connection.prototype.stop = function () {
118+
119+
if (this.client) {
120+
this.client.close();
121+
this.client = null;
122+
this.isConnected = false;
123+
}
124+
};
125+
126+
127+
internals.Connection.prototype.isReady = function () {
128+
129+
return this.isConnected;
130+
};
131+
132+
133+
internals.Connection.prototype.validateSegmentName = function (name) {
134+
135+
if (!name) {
136+
return new Error('Empty string');
137+
}
138+
139+
if (name.indexOf('\0') !== -1) {
140+
return new Error('Includes null character');
141+
}
142+
143+
return null;
144+
};
145+
146+
147+
internals.Connection.prototype.insert = function(record, callback) {
148+
149+
try {
150+
this.table.insert(record).run(this.client, function (err, result) {
151+
152+
if (err) {
153+
return callback(err);
154+
}
155+
156+
return callback();
157+
});
158+
}
159+
160+
catch(err) {
161+
162+
return callback(new Error(err));
163+
}
164+
};
165+
166+
167+
internals.Connection.prototype.replace = function(record, callback) {
168+
169+
try {
170+
171+
this.table.replace(record).run(this.client, function (err, result) {
172+
173+
if (err) {
174+
return callback(err);
175+
}
176+
177+
return callback();
178+
});
179+
}
180+
181+
catch(err) {
182+
183+
return callback(new Error(err));
184+
}
185+
};
186+
187+
188+
internals.Connection.prototype.get = function (key, callback) {
189+
190+
if (!this.client) {
191+
return callback(new Error('Connection not started'));
192+
}
193+
194+
var cacheKey = this.generateKey(key);
195+
196+
this.table.get(cacheKey).run(this.client, function (err, result) {
197+
198+
if (err) {
199+
return callback(err);
200+
}
201+
202+
if (!result) {
203+
return callback(null, null);
204+
}
205+
206+
if (!result.value || !result.stored) {
207+
208+
return callback(new Error('Incorrect result structure'));
209+
}
210+
211+
var envelope = {
212+
item: result.value,
213+
stored: result.stored.getTime(),
214+
ttl: result.ttl
215+
};
216+
217+
return callback(null, envelope);
218+
});
219+
};
220+
221+
222+
internals.Connection.prototype.set = function (key, value, ttl, callback) {
223+
224+
var self = this;
225+
226+
if (!this.client) {
227+
return callback(new Error('Connection not started'));
228+
}
229+
230+
var cacheKey = this.generateKey(key);
231+
232+
var expiresAt = new Date();
233+
expiresAt.setMilliseconds(expiresAt.getMilliseconds() + ttl);
234+
235+
var record = {
236+
id: cacheKey,
237+
value: value,
238+
stored: new Date(),
239+
ttl: ttl,
240+
expiresAt: expiresAt
241+
};
242+
243+
this.get(key, function(err, result) {
244+
245+
if (err) {
246+
return callback(err);
247+
}
248+
249+
if (!result) {
250+
251+
self.insert(record, callback);
252+
}
253+
254+
else {
255+
256+
self.replace(record, callback);
257+
}
258+
});
259+
};
260+
261+
262+
internals.Connection.prototype.drop = function (key, callback) {
263+
264+
if (!this.client) {
265+
return callback(new Error('Connection not started'));
266+
}
267+
268+
var cacheKey = this.generateKey(key);
269+
270+
this.table.get(cacheKey).delete().run(this.client, function (err, result) {
271+
272+
if (err) {
273+
return callback(err);
274+
}
275+
276+
return callback(null);
277+
});
278+
};
279+
280+
internals.Connection.prototype.generateKey = function (key) {
281+
282+
return encodeURIComponent(key.segment) + encodeURIComponent(key.id);
283+
};

0 commit comments

Comments
 (0)