PageRenderTime 2418ms CodeModel.GetById 35ms RepoModel.GetById 93ms app.codeStats 0ms

/lodis.coffee

http://github.com/elcuervo/lodis
CoffeeScript | 429 lines | 332 code | 97 blank | 0 comment | 74 complexity | 78fc17670d34c817df96e8ddeb5e2caa MD5 | raw file
  1. class @Lodis
  2. constructor: ->
  3. this.storage = window.localStorage
  4. _pack: (value) -> JSON.stringify value
  5. _unpack: (value) -> JSON.parse value
  6. _expire_key: (key) =>
  7. this.del(key)
  8. delete this._expiration_hash[key]
  9. _get_set_or_default: (key, default_value) ->
  10. this._unpack(this.get(key)) or default_value
  11. _get_set: (key) ->
  12. this._get_set_or_default(key, [])
  13. _extract_from_set: (source, members...) ->
  14. result = []
  15. for member in members
  16. if member in this.smembers(source)
  17. result.push item for item in this._get_set(source) when item != member
  18. result
  19. _get_unpacked: (key) ->
  20. try
  21. this._unpack(this.get(key))
  22. catch error
  23. this.get(key)
  24. _get_hash: (key) ->
  25. this._get_set_or_default(key, {})
  26. _set_packed: (key, value) ->
  27. value = this._pack(value)
  28. this.set(key, value)
  29. _get_difference_or_intersection_for_sets: (action = 'DIFF', keys...) ->
  30. [head, tail...] = keys
  31. set = this._get_set(head)
  32. other_set = result = []
  33. other_set = other_set.concat(this._get_set(key)) for key in tail
  34. for value in set
  35. condition = switch action.toUpperCase()
  36. when 'DIFF' then value not in other_set
  37. when 'INTER' then value in other_set
  38. result.push value if condition
  39. result.reverse()
  40. _get_from_hash: (key, options = with_keys: true, with_values: true, only: []) ->
  41. hash = this._get_hash(key)
  42. result = []
  43. for key, value of hash
  44. result.push key if options["with_keys"]
  45. result.push value if options["with_values"] or ( options["only"] and key in options["only"])
  46. result
  47. _alter_int_value: (key, quantity) ->
  48. if this.exists(key)
  49. value = parseInt this.get(key)
  50. if typeof value is "number"
  51. value = value + quantity
  52. this.set(key, value)
  53. value
  54. else
  55. throw new Error
  56. _expiration_hash: {}
  57. del: (keys...) ->
  58. this.storage.removeItem(key) for key in keys
  59. true
  60. set: (key, value) ->
  61. this.storage.setItem(key, value)
  62. true
  63. get: (key) ->
  64. this.storage.getItem(key)
  65. exists: (key) ->
  66. this.get(key)?
  67. dbsize: -> this.storage.length
  68. keys: (regexp) ->
  69. found_keys = []
  70. for i in [0..this.dbsize() - 1]
  71. key = this.storage.key(i)
  72. found_keys.push key if key.match(regexp)
  73. found_keys
  74. expire: (key, seconds) ->
  75. miliseconds = seconds*1000
  76. timeout_id = setTimeout this._expire_key, miliseconds, key
  77. this._expiration_hash[key] = id: timeout_id, timeout: new Date().getTime() + miliseconds
  78. true
  79. expireat: (key, miliseconds) ->
  80. return false if (miliseconds < new Date().getTime()) or !this.exists(key)
  81. seconds = (miliseconds - new Date().getTime()) / 1000
  82. this.expire key, seconds
  83. true
  84. ttl: (key) ->
  85. if this.exists(key)
  86. if this._expiration_hash[key]
  87. Math.floor((this._expiration_hash[key].timeout - new Date().getTime()) / 1000)
  88. else
  89. -1
  90. append: (key, value) ->
  91. this.set(key, "#{this.get(key)}#{value}") if this.exists(key)
  92. auth: (password) ->
  93. true # this should do someting?
  94. bgrewriteaof: -> true
  95. bgsave: -> true
  96. blpop: ->
  97. lrange: (key, start, end) ->
  98. end += 1
  99. set = this._get_set(key)
  100. end = set.length + end if end < 1
  101. result = set.slice(start, end)
  102. result = [result] if result.constructor is String
  103. result
  104. lpush: (key, item) ->
  105. set = this._get_set(key)
  106. set.unshift(item)
  107. this._set_packed(key, set)
  108. rpush: (key, item) ->
  109. set = this._get_set(key)
  110. set.push(item)
  111. this._set_packed(key, set)
  112. rpushx: (key, item) ->
  113. this.rpush(key, item) if this.exists(key)
  114. decr: (key) ->
  115. this.decrby(key, 1)
  116. incr: (key) ->
  117. this.incrby(key, 1)
  118. decrby: (key, quantity = 1) ->
  119. this._alter_int_value(key, -quantity)
  120. incrby: (key, quantity = 1) ->
  121. this._alter_int_value(key, quantity)
  122. echo: (message) -> message
  123. flushall: ->
  124. this.storage.clear()
  125. flushdb: ->
  126. this.flushall()
  127. getrange: (key, start, end) ->
  128. if this.exists(key)
  129. string = this.get(key)
  130. start = string.length + start if start < 0
  131. end = string.length + end if end < 0
  132. string.substr start, end + 1
  133. getset: (key, value) ->
  134. if this.exists(key)
  135. old_value = this.get(key)
  136. this.set(key, value)
  137. old_value
  138. hset: (hash_key, key, value) ->
  139. hash = this._get_hash(hash_key)
  140. hash[key] = value
  141. this._set_packed(hash_key, hash)
  142. true
  143. hget: (hash_key, key) ->
  144. if this.exists(hash_key)
  145. hash = this._get_hash(hash_key)
  146. hash[key]
  147. hgetall: (hash_key) ->
  148. this._get_from_hash(hash_key) if this.exists(hash_key)
  149. hexists: (hash_key, key) ->
  150. this.hget(hash_key, key)?
  151. hkeys: (hash_key) ->
  152. this._get_from_hash(hash_key, with_keys: true, with_values: false) if this.exists(hash_key)
  153. hlen: (hash_key) ->
  154. this.hkeys(hash_key).length if this.exists(hash_key)
  155. hincrby: (hash_key, key, quantity) ->
  156. if this.hexists(hash_key, key)
  157. old_value = parseInt this.hget(hash_key, key)
  158. if typeof old_value is "number"
  159. new_value = old_value + quantity
  160. this.hset(hash_key, key, new_value)
  161. new_value
  162. else
  163. throw new Error("Invalid type")
  164. hmget: (hash_key, keys...) ->
  165. this._get_from_hash(hash_key, with_values: true, with_keys: false, only: keys) if this.exists(hash_key)
  166. hmset: (hash_key, keys_and_values...) ->
  167. result = {}
  168. result[keys_and_values[i-1]] = value for i, value of keys_and_values when i % 2
  169. this._set_packed(hash_key, result)
  170. hsetnx: (hash_key, key, value) ->
  171. if !this.exists(hash_key)
  172. this.hset(hash_key, key, value)
  173. true
  174. else
  175. false
  176. hvals: (hash_key) ->
  177. this._get_from_hash(hash_key, with_keys: false, with_values: true) if this.exists(hash_key)
  178. lindex: (key, index) ->
  179. if this.exists(key)
  180. hash = this._get_set(key)
  181. index = hash.length + index if index < 0
  182. hash[index] or false
  183. linsert: (key, direction, reference_value, value) ->
  184. if this.exists(key)
  185. direction = switch direction.toUpperCase()
  186. when "BEFORE" then -1
  187. when "AFTER" then 1
  188. set = this._get_set(key)
  189. reference_value = set.indexOf(reference_value) + direction
  190. [left_side, right_side] = [set[0...reference_value], set[reference_value..-1]]
  191. result = left_side.concat([value])
  192. result = result.concat(right_side)
  193. this._set_packed(key, result)
  194. llen: (key) ->
  195. if this.exists(key)
  196. set = this._get_set(key)
  197. set.length
  198. lpop: (key) ->
  199. if this.exists(key)
  200. set = this._get_set(key)
  201. value = set[1..-1]
  202. this._set_packed(key, value)
  203. value
  204. lpushx: (key, value) ->
  205. if this.exists(key)
  206. this.lpush(key, value)
  207. else
  208. false
  209. lrem: (key, count, item) ->
  210. if this.exists(key)
  211. quantity = Math.abs(count)
  212. set = this._get_set(key)
  213. set = set.reverse() if count < 0
  214. result = []
  215. for value in set
  216. if value == item and quantity > 0
  217. quantity -= 1
  218. else
  219. result.push value
  220. result = result.reverse() if count < 0
  221. this._set_packed(key, result)
  222. lset: (key, index, value) ->
  223. if this.exists(key)
  224. set = this._get_set(key)
  225. index = set.length + index if index < 0
  226. set[index] = value
  227. this._set_packed(key, set)
  228. ltrim: (key, start, end) ->
  229. if this.exists(key)
  230. set = this._get_set(key)
  231. end = set.length + end if end < 0
  232. result = set[start..end]
  233. this._set_packed(key, result)
  234. mget: (keys...) ->
  235. result = []
  236. result.push this.get(key) for key in keys
  237. result
  238. mset: (keys_and_values...) ->
  239. this.set(keys_and_values[i-1], value) for i, value of keys_and_values when i % 2
  240. msetnx: (keys_and_values...) ->
  241. return for i, key_or_value of keys_and_values when (!(i % 2) and this.exists(key_or_value))
  242. this.mset(keys_and_values...)
  243. persist: (key) ->
  244. if this.exists(key)
  245. if this._expiration_hash[key]
  246. clearTimeout this._expiration_hash[key].id
  247. delete this._expiration_hash[key]
  248. ping: -> "PONG"
  249. randomkey: ->
  250. keys = this.keys()
  251. keys[Math.floor(Math.random() * keys.length)]
  252. rename: (key, new_key) ->
  253. value = this.get(key)
  254. this.del(key)
  255. this.set(new_key, value)
  256. renamenx: (key, new_key) ->
  257. this.rename(key, new_key) if !this.exists(new_key)
  258. rpop: (key) ->
  259. if this.exists(key)
  260. set = this._get_set(key)
  261. value = set.pop()
  262. this._set_packed(key, set)
  263. value
  264. rpoplpush: (hash_key, other_hash_key) ->
  265. if this.exists(hash_key)
  266. value = this.rpop(hash_key)
  267. this.lpush(other_hash_key, value)
  268. value
  269. sadd: (key, members...) ->
  270. set = this._get_set(key)
  271. this.lpush(key, member) for member in members when member not in set
  272. smembers: (key) ->
  273. this._get_set(key) if this.exists(key)
  274. save: -> true # ?
  275. scard: (key) ->
  276. this._get_set(key).length
  277. sdiff: (keys...) ->
  278. this._get_difference_or_intersection_for_sets('DIFF', keys...)
  279. sdiffstore: (destination, keys...) ->
  280. this._set_packed(destination, this.sdiff(keys...))
  281. select: (db) -> db is 0
  282. setex: (key, expire, value) ->
  283. this.set(key, value)
  284. this.expire(key, expire)
  285. setnx: (key, value) -> this.set(key, value) if !this.exists(key)
  286. setrange: (key, offset, value) ->
  287. old_value = if this.exists(key)
  288. this.get(key).substr(0, offset)
  289. else
  290. result = new String
  291. result += " " for i in [0...offset]
  292. result
  293. this.set(key, "#{old_value}#{value}")
  294. shutdown: -> true # ?
  295. sinter: (keys...) ->
  296. this._get_difference_or_intersection_for_sets('INTER', keys...)
  297. sinterstore: (destination, keys...) ->
  298. this._set_packed(destination, this.sinter(keys...))
  299. sismember: (key, value) ->
  300. if this.exists(key)
  301. set = this._get_set(key)
  302. set.indexOf(value) > -1
  303. smove: (source, destination, member) ->
  304. if this.exists(source)
  305. if member in this.smembers(source)
  306. result = this._extract_from_set(source, member)
  307. this._set_packed(source, result)
  308. this.rpush(destination, member)
  309. true
  310. else
  311. false
  312. spop: (key) ->
  313. if this.exists(key)
  314. set = this._get_set(key)
  315. set.pop()
  316. this._set_packed(key, set)
  317. srandmember: (key) ->
  318. if this.exists(key)
  319. set = this._get_set(key)
  320. set[Math.floor(Math.random() * set.length)]
  321. srem: (key, members...) ->
  322. if this.exists(key)
  323. set = this._get_set(key)
  324. result = this._extract_from_set(key, members...)
  325. this._set_packed(key, result)
  326. strlen: (key) ->
  327. if this.exists(key)
  328. this.get(key).length
  329. else
  330. 0
  331. type: (key) ->
  332. this._get_unpacked(key).constructor.name.toLowerCase() if this.exists(key)