UNPKG

brobbot

Version:

A simple helpful robot for your Company

398 lines (331 loc) 10.2 kB
{EventEmitter} = require 'events' User = require './user' BrainSegment = require './brain-segment' Q = require 'q' _ = require 'lodash' class Brain extends EventEmitter # Represents somewhat persistent storage for the robot. Extend this. # # Returns a new Brain with no external storage. constructor: (@robot) -> @data = users: {} _private: {} @autoSave = true @ready = Q @ # Take a dump # # Returns promise for object dump: -> Q(@data) # Public: get the length of the list stored at `key` # # Returns int llen: (key) -> Q(@data._private[@key key].length) # Public: set the list value at the specified index # # Returns promise lset: (key, index, value) -> @data._private[@key key][index] = @serialize value Q @ # Public: insert a value into the list before or after the pivot element. # # Returns promise linsert: (key, placement, pivot, value) -> key = @key key if @data._private[key] isnt undefined index = _.find @data._private[key], pivot if index @data._private[key].splice 0, placement is 'AFTER' ? index + 1 : index, @serialize(value) Q @ # Public: push a new value onto the left-side of the list # # Returns promise lpush: (key, value) -> key = @key key if @data._private[key] is undefined @data._private[key] = [] @data._private[key].unshift(@serialize value) Q @ # Public: push a new value onto the right-side of the list # # Returns promise rpush: (key, value) -> key = @key key if @data._private[key] is undefined @data._private[key] = [] @data._private[key].push(@serialize value) Q @ # Public: pop a value off of the left-side of the list # # Returns promise for list item lpop: (key) -> Q(@deserialize(@data._private[@key key].shift())) # Public: pop a value off of the right-side of the list # # Returns promise for list item rpop: (key) -> Q(@deserialize(@data._private[@key key].pop())) # Public: get a list item by index # # Returns promise for list item lindex: (key, index) -> Q(@deserialize(@data._private[@key key][index])) # Public: get an entire list # # Returns promise for array lgetall: (key) -> @lrange(key, 0, -1) # Public: get a slice of the list # # Returns promise for array lrange: (key, start, end) -> key = @key key if end < 0 end = @data._private[key].length + end Q(_.map(@data._private[key].slice(start, end + 1), @deserialize.bind(@))) # Public: remove values from a list # # Returns promise lrem: (key, value) -> key = @key key @data._private[key] = _.without @data._private[key], value Q() # Public: Add a member to the set specified by `key` # # Returns promise sadd: (key, value) -> key = @key key if @data._private[key] is undefined @data._private[key] = [] #TODO behavior with objects may not be what's expected. maybe make JSON.stringify the default serializer? if not _.contains @data._private[key], value @data._private[key].push(@serialize value) Q @ # Public: Test whether the member is in the set # # Returns promise for boolean sismember: (key, value) -> Q(_.contains @data._private[key], value) # Public: Remove a member from the set # # Returns promise srem: (key, value) -> key = @key key index = _.findIndex @data._private[key], value if index @data._private[key].splice(1, index) Q @ # Public: Get the size of the set # # Returns promise for int scard: (key) -> Q @data._private[@key key].length # Public: Get and remove a random member from the set # # Returns promise for a set member spop: (key) -> key = @key key index = _.random 0, @data._private[key].length - 1 item = @data._private[key][index] @data._private[key].splice(1, index) Q item # Public: Get a random member from the set # # Returns promise for a set member srandmember: (key) -> key = @key key Q @data._private[key][_.random 0, @data._private[key].length - 1] # Public: Get all the members of the set # # Returns promise for array smembers: (key) -> Q @data._private[@key key] # Public: get all the keys, optionally restricted to keys prefixed with `searchKey` # # Returns promise for array keys: (searchKey = '') -> searchKey = @key searchKey Q(_.map(_.filter(_.keys(@data._private), (key) -> key.indexOf searchKey is 0), @unkey.bind(@))) # Public: transform a key from internal brain key, to user-facing key # # Returns string unkey: (key) -> key # Public: transform the key for internal use # overridden by brain-segment # # Returns string. key: (key) -> key # Public: get the key for the users # # Returns string. usersKey: () -> 'users' # Public: Store key-value pair under the private namespace and extend # existing. # # Returns promise set: (key, value) -> if value is undefined _.each key, (v, k) => @set k, v else @data._private[@key key] = @serialize value Q @ # Public: Get value by key from the private namespace in @data # or return null if not found. # # Returns promise get: (key) -> Q(@deserialize(@data._private[@key key] ? null)) # Public: Check whether the given key has been set # # Return promise for boolean exists: (key) -> Q(@data._private[@key key] isnt undefined) # Public: increment the value by num atomically # # Returns promise incrby: (key, num) -> key = @key key @data._private[key] = (@data._private[key] or 0) + num Q @data._private[key] # Public: Get all the keys for the given hash table name # # Returns promise for array. hkeys: (table) -> Q(_.keys(@data._private[@key table] or {})) # Public: Get all the values for the given hash table name # # Returns promise for array. hvals: (table) -> Q(_.mapValues(@data._private[@key table] or {}, @deserialize.bind(@))) # Public: get the size of the hash table. # # Returns promise for int hlen: (table) -> Q(_.size(@data._private[@key table])) # Public: Set a value in the specified hash table # # Returns promise for the value. hset: (table, key, value) -> table = @key table @data._private[table] = @data._private[table] or {} @data._private[table][key] = @serialize value Q @ # Public: Get a value from the specified hash table. # # Returns: promise for the value. hget: (table, key) -> Q(@deserialize @data._private[@key table][key]) # Public: Delete a field from a hash table # # Returns promise hdel: (table, key) -> delete @data._private[@key table][key] Q @ # Public: Get the whole hash table as an object. # # Returns: promise for object. hgetall: (table) -> Q(_.clone(_.mapValues(@data._private[@key table], @deserialize.bind @))) # Public: increment the hash value by num atomically # # Returns promise hincrby: (table, key, num) -> table = @key table @data._private[table] = @data._private[table] or {} @data._private[table][key] = (@data._private[table][key] or 0) + num Q @data._private[table][key] # Public: Remove value by key from the private namespace in @data # if it exists # # Returns promise remove: (key) -> delete @data._private[@key key] Q @ # alias for remove del: (key) -> @remove key # Public: nothin to close # # Returns promise close: -> Q @ # Public: Merge keys against the in memory representation. # # Returns promise # # Caveats: Deeply nested structures don't merge well. mergeData: (data) -> @set data # Public: Perform any necessary pre-set serialization on a value # # Returns serialized value serialize: (value) -> value # Public: Perform any necessary post-get deserialization on a value # # Returns deserialized value deserialize: (value) -> value # Public: Get an Array of User objects stored in the brain. # # Returns promise for an Array of User objects. users: -> Q(@data.users) # Public: Add a user to the data-store # # Returns promise for user addUser: (user) -> @data.users[user.id] = user Q(user) # Public: Get or create a User object given a unique identifier. # # Returns promise for a User instance of the specified user. userForId: (id, options) -> user = @data.users[id] if !user or (options and options.room and (user.room isnt options.room)) return @addUser(new User(id, options)) Q(user) # Public: Get a User object given a name. # # Returns promise for a User instance for the user with the specified name. userForName: (name) -> result = null lowerName = name.toLowerCase() for k of (@data.users or { }) userName = @data.users[k]['name'] if userName? and userName.toString().toLowerCase() is lowerName result = @data.users[k] Q(result) # Public: Get all users whose names match fuzzyName. Currently, match # means 'starts with', but this could be extended to match initials, # nicknames, etc. # # Returns promise an Array of User instances matching the fuzzy name. usersForRawFuzzyName: (fuzzyName) -> lowerFuzzyName = fuzzyName.toLowerCase() Q(user) for key, user of (@data.users or {}) when ( user.name.toLowerCase().lastIndexOf(lowerFuzzyName, 0) is 0 ) # Public: If fuzzyName is an exact match for a user, returns an array with # just that user. Otherwise, returns an array of all users for which # fuzzyName is a raw fuzzy match (see usersForRawFuzzyName). # # Returns promise an Array of User instances matching the fuzzy name. usersForFuzzyName: (fuzzyName) -> matchedUsers = @usersForRawFuzzyName(fuzzyName) lowerFuzzyName = fuzzyName.toLowerCase() for user in matchedUsers return [user] if user.name.toLowerCase() is lowerFuzzyName Q(matchedUsers) # Public: Return a brain segment bound to the given key-prefix. # # Returns BrainSegment segment: (segment) -> new BrainSegment @, segment module.exports = Brain