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