Skip to content

Commit

Permalink
feat(User): add Datastore support
Browse files Browse the repository at this point in the history
  • Loading branch information
mistydemeo committed Jan 11, 2019
1 parent a494d0e commit 40712e1
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 2 deletions.
15 changes: 15 additions & 0 deletions docs/scripting.md
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,21 @@ robot.respond /sleep it off/i, (res) ->
res.reply 'zzzzz'
```

The datastore also allows setting and getting values which are scoped to individual users:

```coffeescript
module.exports = (robot) ->

robot.respond /who is @?([\w .\-]+)\?*$/i, (res) ->
name = res.match[1].trim()

users = robot.brain.usersForFuzzyName(name)
if users.length is 1
user = users[0]
user.get('roles').then (roles) ->
res.send "#{name} is #{roles.join(', ')}"
```

## Script Loading

There are three main sources to load scripts from:
Expand Down
12 changes: 10 additions & 2 deletions src/brain.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const User = require('./user')
// 2. If the original object was a User object, the original object
// 3. If the original object was a plain JavaScript object, return
// a User object with all of the original object's properties.
let reconstructUserIfNecessary = function (user) {
let reconstructUserIfNecessary = function (user, robot) {
if (!user) {
return null
}
Expand All @@ -20,6 +20,9 @@ let reconstructUserIfNecessary = function (user) {
delete user.id
// Use the old user as the "options" object,
// populating the new user with its values.
// Also add the `robot` field so it gets a reference.
user.robot = robot

return new User(id, user)
} else {
return user
Expand All @@ -36,6 +39,7 @@ class Brain extends EventEmitter {
users: {},
_private: {}
}
this.robot = robot

this.autoSave = true

Expand Down Expand Up @@ -142,7 +146,7 @@ class Brain extends EventEmitter {
if (data && data.users) {
for (let k in data.users) {
let user = this.data.users[k]
this.data.users[k] = reconstructUserIfNecessary(user)
this.data.users[k] = reconstructUserIfNecessary(user, this.robot)
}
}

Expand All @@ -161,6 +165,10 @@ class Brain extends EventEmitter {
// Returns a User instance of the specified user.
userForId (id, options) {
let user = this.data.users[id]
if (!options) {
options = {}
}
options.robot = this.robot

if (!user) {
user = new User(id, options)
Expand Down
40 changes: 40 additions & 0 deletions src/user.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict'

const DataStoreUnavailable = require('./datastore').DataStoreUnavailable

class User {
// Represents a participating user in the chat.
//
Expand All @@ -12,6 +14,17 @@ class User {
options = {}
}

// Define a getter method so we don't actually store the
// robot itself on the user object, preventing it from
// being serialized into the brain.
if (options.robot) {
let robot = options.robot
delete options.robot
this._getRobot = function () { return robot }
} else {
this._getRobot = function () { }
}

Object.keys(options).forEach((key) => {
this[key] = options[key]
})
Expand All @@ -20,6 +33,33 @@ class User {
this.name = this.id.toString()
}
}

set (key, value) {
this._checkDatastoreAvailable()
return this._getDatastore()._set(this._constructKey(key), value, 'users')
}

get (key) {
this._checkDatastoreAvailable()
return this._getDatastore()._get(this._constructKey(key), 'users')
}

_constructKey (key) {
return `${this.id}+${key}`
}

_checkDatastoreAvailable () {
if (!this._getDatastore()) {
throw new DataStoreUnavailable('datastore is not initialized')
}
}

_getDatastore () {
let robot = this._getRobot()
if (robot) {
return robot.datastore
}
}
}

module.exports = User
34 changes: 34 additions & 0 deletions test/datastore_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,38 @@ describe('Datastore', function () {
})
})
})

describe('User scope', function () {
it('has access to the robot object', function () {
let user = this.robot.brain.userForId('1')
expect(user._getRobot()).to.equal(this.robot)
})

it('can store user data which is separate from global data', function () {
let user = this.robot.brain.userForId('1')
return user.set('blah', 'blah').then(() => {
return user.get('blah').then((userBlah) => {
return this.robot.datastore.get('blah').then((datastoreBlah) => {
expect(userBlah).to.not.equal(datastoreBlah)
expect(userBlah).to.equal('blah')
expect(datastoreBlah).to.be.an('undefined')
})
})
})
})

it('stores user data separate per-user', function () {
let userOne = this.robot.brain.userForId('1')
let userTwo = this.robot.brain.userForId('2')
return userOne.set('blah', 'blah').then(() => {
return userOne.get('blah').then((valueOne) => {
return userTwo.get('blah').then((valueTwo) => {
expect(valueOne).to.not.equal(valueTwo)
expect(valueOne).to.equal('blah')
expect(valueTwo).to.be.an('undefined')
})
})
})
})
})
})

0 comments on commit 40712e1

Please sign in to comment.