Permalink
Browse files

Add support for expiry time to set and get

  • Loading branch information...
1 parent c5955f8 commit 1165953d3d9a6277127b809d8c5685324e89fcd9 @wezm committed Apr 5, 2011
Showing with 57 additions and 30 deletions.
  1. +22 −17 src/db.coffee
  2. +26 −13 src/rest_client.coffee
  3. +9 −0 test/db_test.coffee
View
@@ -10,9 +10,10 @@ class DB
constructor: (@database) ->
throw new Error("default database must be passed to new") unless @database
- _initRpcArgs: (options) ->
+ _initOptions: (options) ->
args = {}
args.DB = options.database or @database
+ args.xt = options.expires if options.expires?
args
open: (@host = 'localhost', @port = 1978) ->
@@ -73,7 +74,7 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to status");
- rpc_args = this._initRpcArgs options
+ rpc_args = this._initOptions options
@rpcClient.call 'status', rpc_args, (error, status, output) ->
if error?
callback error, output
@@ -118,7 +119,8 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to set");
- @restClient.put key, value, options.database or @database, (error) ->
+ params = this._initOptions options
+ @restClient.put key, value, params, (error) ->
callback error
# Add a record if it doesn't already exist
@@ -133,7 +135,7 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to add");
- rpc_args = this._initRpcArgs options
+ rpc_args = this._initOptions options
rpc_args.key = key
rpc_args.value = value
@rpcClient.call 'add', rpc_args, (error, status, output) ->
@@ -158,7 +160,7 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to replace");
- rpc_args = this._initRpcArgs options
+ rpc_args = this._initOptions options
rpc_args.key = key
rpc_args.value = value
@rpcClient.call 'replace', rpc_args, (error, status, output) ->
@@ -182,7 +184,7 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to append");
- rpc_args = this._initRpcArgs options
+ rpc_args = this._initOptions options
rpc_args.key = key
rpc_args.value = value
@rpcClient.call 'append', rpc_args, (error, status, output) ->
@@ -205,7 +207,7 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to increment");
- rpc_args = this._initRpcArgs options
+ rpc_args = this._initOptions options
rpc_args.key = key
rpc_args.num = num
@rpcClient.call 'increment', rpc_args, (error, status, output) ->
@@ -229,7 +231,7 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to incrementDouble");
- rpc_args = this._initRpcArgs options
+ rpc_args = this._initOptions options
rpc_args.key = key
rpc_args.num = num
@rpcClient.call 'increment_double', rpc_args, (error, status, output) ->
@@ -253,7 +255,7 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to cas");
- rpc_args = this._initRpcArgs options
+ rpc_args = this._initOptions options
rpc_args.key = key
rpc_args.oval = oval if oval?
rpc_args.nval = nval if nval?
@@ -278,7 +280,8 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to remove");
- @restClient.delete key, options.database, (error) ->
+ params = this._initOptions options
+ @restClient.delete key, params, (error) ->
callback error
exists: (key, args...) ->
@@ -292,7 +295,8 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to exists");
- @restClient.head key, options.database, (error, headers) ->
+ params = this._initOptions options
+ @restClient.head key, params, (error, headers) ->
callback error, headers?
# key, [database], callback
@@ -307,8 +311,9 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to get");
- @restClient.get key, options.database, (error, value) ->
- callback error, value
+ params = this._initOptions options
+ @restClient.get key, params, (error, value, expires) ->
+ callback error, value, expires
setBulk: (records, args...) ->
switch args.length
@@ -321,7 +326,7 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to setBulk");
- rpc_args = this._initRpcArgs options
+ rpc_args = this._initOptions options
rpc_args["_#{key}"] = value for key, value of records
@rpcClient.call 'set_bulk', rpc_args, (error, status, output) ->
@@ -343,7 +348,7 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to removeBulk");
- rpc_args = this._initRpcArgs options
+ rpc_args = this._initOptions options
rpc_args["_#{key}"] = '' for key in keys
@rpcClient.call 'remove_bulk', rpc_args, (error, status, output) ->
@@ -365,7 +370,7 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to getBulk");
- rpc_args = this._initRpcArgs options
+ rpc_args = this._initOptions options
rpc_args["_#{key}"] = '' for key in keys
@rpcClient.call 'get_bulk', rpc_args, (error, status, output) ->
@@ -395,7 +400,7 @@ class DB
else
throw new Error("Invalid number of arguments (#{args.length}) to #{procedure}");
- rpc_args = this._initRpcArgs options
+ rpc_args = this._initOptions options
if procedure == 'match_prefix'
rpc_args.prefix = pattern
else
View
@@ -7,21 +7,31 @@ class RestClient
_extractArgs: (args) ->
switch args.length
- when 1 then return [null, args[0]]
+ when 1 then return [{}, args[0]]
when 2 then return args
else
throw new Error("Invalid number of arguments (#{args.length})");
- _buildPath: (database, key) ->
- if database then "/#{encodeURIComponent(database)}/#{encodeURIComponent(key)}" else "/#{escape(key)}"
+ _expirationTime: (date_or_number) ->
+ if typeof date_or_number == 'number'
+ Math.round((Date.now() + (1000 * date_or_number)) / 1000)
+ else if date_or_number instanceof Date
+ date_or_number.toUTCString()
+ else
+ throw new Error("Invalid expires value, must be Date or Number")
+
+ _buildPath: (params, key) ->
+ path = "/#{encodeURIComponent(key)}"
+ path = "/#{encodeURIComponent(params.DB)}" + path if params.DB
+ path
get: (key, args...) ->
- [database, callback] = this._extractArgs(args)
+ [params, callback] = this._extractArgs(args)
request =
host: @host
port: @port
- path: this._buildPath database, key
+ path: this._buildPath params, key
headers:
'Content-Length': 0
'Connection': 'keep-alive'
@@ -31,6 +41,8 @@ class RestClient
# if large responses are expected it would be
# better to write to a file if over some size threshold.
content_length = parseInt(response.headers['content-length'], 10)
+ expires = if response.headers.hasOwnProperty 'x-kt-xt' then new Date(response.headers['x-kt-xt']) else null
+
value = new Buffer(content_length)
offset = 0
@@ -40,20 +52,20 @@ class RestClient
response.on 'end', ->
switch response.statusCode
- when 200 then callback undefined, value
- when 404 then callback undefined, null
+ when 200 then callback undefined, value, expires
+ when 404 then callback undefined, null, expires
else callback new Error("Unexpected response from server: #{response.statusCode}")
.on 'error', (error) ->
callback error
head: (key, args...) ->
- [database, callback] = this._extractArgs(args)
+ [params, callback] = this._extractArgs(args)
options =
host: @host
port: @port
method: 'HEAD'
- path: this._buildPath database, key
+ path: this._buildPath params, key
headers:
'Content-Length': 0
'Connection': 'keep-alive'
@@ -69,16 +81,17 @@ class RestClient
.end()
put: (key, value, args...) ->
- [database, callback] = this._extractArgs(args)
+ [params, callback] = this._extractArgs(args)
options =
host: @host
port: @port
method: 'PUT'
- path: this._buildPath database, key
+ path: this._buildPath params, key
headers:
'Content-Length': if typeof value == 'string' then Buffer.byteLength(value) else value.length
'Connection': 'keep-alive'
+ options.headers['X-Kt-Xt'] = this._expirationTime(params.xt) if params.xt?
http.request options, (response) ->
response.on 'end', ->
@@ -90,13 +103,13 @@ class RestClient
.end(value)
delete: (key, args...) ->
- [database, callback] = this._extractArgs(args)
+ [params, callback] = this._extractArgs(args)
options =
host: @host
port: @port
method: 'DELETE'
- path: this._buildPath database, key
+ path: this._buildPath params, key
headers:
'Content-Length': 0
'Connection': 'keep-alive'
View
@@ -106,6 +106,15 @@ module.exports =
test.ok value == null
test.done()
+ 'allows the expiry time to be specified': (test) ->
+ test.expect 2
+ db.set 'test', 'Value', {expires: 60}, (error, output) ->
+ test.ifError error
+
+ db.get 'test', (error, value, expires) ->
+ test.ok expires instanceof Date
+ test.done()
+
add: testCase
setUp: dbClear

0 comments on commit 1165953

Please sign in to comment.