Skip to content

Commit

Permalink
Prepare v2.0.0 final for release
Browse files Browse the repository at this point in the history
  • Loading branch information
kirsle committed Feb 26, 2019
1 parent 3f04cdb commit a8e9356
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 147 deletions.
121 changes: 12 additions & 109 deletions Changes.md
Original file line number Diff line number Diff line change
@@ -1,119 +1,22 @@
# Changes

## 2.0.0 Beta: Async/Await
## 2.0.0: The Async/Await Update

This is a major new version of RiveScript. It adds async/await support and
makes the API asynchronous everywhere. It also decaffeinates the code,
migrating it from CoffeeScript back to native ES2015+ with modern syntax.

### Backwards Incompatible Changes

* The `reply()` method now returns a Promise instead of a string. In that
regard, it works just like the old `replyAsync()` method did.
* The `loadFile()` and `loadDirectory()` functions return Promises now
instead of using callbacks.
* The `replyAsync()` method is now deprecated in favor of `reply()`.

The user variable session manager is now pluggable and replaceable, with
the default continuing to be in-memory storage of user variables.

For session managers to support async database drivers, all of the user
variable functions now return their result as a Promise:

* `setUservar(user, name, value)`
* `setUservars(user, data)`
* `getUservar(user, name)`
* `getUservars([user])`
* `clearUservars([user])`
* `freezeUservars(user)`
* `thawUservars(user, action="thaw")`
* `lastMatch(user)`
* `initialMatch(user)`
* `lastTriggers(user)`
* `getUserTopicTriggers(user)`

### User Variable Session Managers

RiveScript-JS finally joins its Python, Go, and Java cousins in supporting
pluggable user variable session managers.

This feature will allow you to replace the default in-memory user variable
store with one backed by a database, like MongoDB or Redis.

```javascript
// MemorySessionManager is the default session store, but you
// could implement Redis or anything by making a SessionManager
// compatible class.
const { MemorySessionManager } = require("./src/sessions");

// Use your session manager.
var bot = new RiveScript({
sessionManager: new MemorySessionManager()
});
```

See the [sessions](./sessions.md) documentation for more details.

### Async Objects in Conditions

Async/Await enables asynchronous object macros that return Promises to
be used *anywhere* throughout RiveScript. This means they can finally be
used inside of `*Condition`s! Example:

```rivescript
// <call>wait-limited $timeout $maxTimeout</call>
// If the $timeout > $maxTimeout, it resolves "too long" immediately.
// Otherwise it waits $timeout seconds and resolves "done"
> object wait-limited javascript
var timeout = parseInt(args[0]);
var max = parseInt(args[1]);
return new Promise(function(resolve, reject) {
if (timeout > max) {
resolve("too long");
} else {
setTimeout(function() {
resolve("done");
}, timeout*1000);
}
});
< object
+ can you wait # seconds
* <call>wait-limited <star> 6</call> == done => I can!
- No the longest I'll wait is 6 seconds.
```

### Upgrading

To support the async features, the RiveScript API had to break backward
compatibility.

The `reply()` function now returns a Promise instead of the string
result. It works like `replyAsync()` did before; and so now `replyAsync()`
is a deprecated function.

Here is an example of how to migrate your code if you were using `reply()`
before to get the string reply:

```diff
- var reply = bot.reply(username, message, this);
- console.log("Bot>", reply);
+ rs.reply(username, message, this).then(function(reply) {
console.log("Bot>", reply);
+ })
```

Likewise, the `loadDirectory()` and `loadFile()` functions were made
to be Promise-based instead of callback-based. Switching over is simple:

```diff
- bot.loadDirectory("./brain", onSuccess, onError);
+ bot.loadDirectory("./brain").then(onSuccess).catch(onError);

- bot.loadFile("./brain/admin.rive", onSuccess, onError);
+ bot.loadFile("./brain/admin.rive").then(onSuccess).catch(onError);
```
See the [Upgrading-v2](https://github.com/aichaos/rivescript-js/blob/master/Upgrading-v2.md) document for information about what's
new and how to upgrade your code.

v2.0.0 last minute changes:

- Fix a bug where `<reply>` in `+Trigger` wasn't being formatted properly
(lowercased, etc.) so matching was difficult.
- Add a Redis driver for an example User Variable Session Manager that
stores variables directly to the cache, and an `eg/redis` example bot
that demonstrates it.
- Write documentation for final v2.0.0 launch.

### 2.0.0-beta.1 - Jan 16 2019

Expand Down
45 changes: 14 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ pairs for building up a bot's intelligence.
This library can be used both in a web browser or as a Node module.
See the `eg/` folder for examples.

## NOTICE: CHANGES IN v2.0.0

RiveScript v2.0.0 comes with a **massive** refactor of the codebase to
implement modern Async/Await features all throughout. The refactor now
allows features like "storing user variables directly in Redis" or
"using asynchronous macros in conditionals"

But it necessarily had to break some backwards compatibility -- slightly!
-- by turning previously synchronous functions like `reply()` into
async ones that return Promises like `replyAsync()` did.

See the [Upgrading-v2](https://github.com/aichaos/rivescript-js/blob/master/Upgrading-v2.md) document for information on the changes
and how to fix your code for the new version.

## USAGE

```javascript
Expand Down Expand Up @@ -80,37 +94,6 @@ Both shell scripts accept command line parameters:

When using RiveScript.js as a library, the synopsis is as follows:

## NOTICE: CHANGES IN v2.0.0

RiveScript v2.0.0 comes with a **massive** refactor of the codebase to
implement modern Async/Await features all throughout. This refactor
enables the following **new features**:

* You can now `<call>` asynchronous object macros inside of `*Condition`
checks.
* You can **actively** store users' variables into a database or
Redis cache. The module `rivescript-redis` provides a Redis cache
driver for this feature and there's an example bot at
[eg/redis](https://github.com/aichaos/rivescript-js/tree/master/eg/redis).
Other drivers (MongoDB, etc.) are up to you to write (send a pull
request! Use the Redis driver as an example)

Because everything had to upgrade to async functions for this to work,
it necessarily had to break backwards compatibility slightly:

* **reply()** now returns a Promise instead of a string, like replyAsync()
has always done.
* All user variable functions (getUservars, setUservars, etc.) now return
a Promise rather than their values directly; this change is what makes
it possible to swap out async database drivers instead of the default
in-memory store!
* **loadFile()** and **loadDirectory()** now return Promises. However, the
old callback-based syntax still works but is now deprecated.
* **replyAsync()** is now deprecated in favor of just **reply()** since
they both do the same thing.

See the [Change Log](https://github.com/aichaos/rivescript-js/blob/master/Changes.md) for more details.

## DOCUMENTATION

There is generated Markdown and HTML documentation of the modules in the
Expand Down
83 changes: 79 additions & 4 deletions Upgrading-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,85 @@ The major highlights of the RiveScript v2.0.0 release are:
syntax. CoffeeScript was holding us back from the `await` keyword
and JavaScript is actually nice to program in nowadays.

# New Features

## User Variables Session Managers

Previously, RiveScript only stored user variables in its own process
memory (keyed by username), and if you wanted the bot to remember user
information across reboots, you needed to use functions like getUservars
and setUservars to get data in and out of the bot's memory.

With the new Async/Await features, now, it is possible to replace the
User Variable Session Manager with a more active one, such as a Redis
cache or a MongoDB database. So every time a user variable gets `<set>`,
it's put directly into a database rather than just kept in memory.

The default is still an in-memory store, but you can implement a custom
async driver following the SessionManager interface. There is a Redis
driver in the rivescript-js git repository called `rivescript-redis`

```javascript
// Import rivescript and rivescript-redis
const RiveScript = require("rivescript"),
RedisSessionManager = require("rivescript-redis");

// Construct your RiveScript bot as normal...
let bot = new RiveScript({
utf8: true,

// Give it a new Redis session manager.
sessionManager: new RedisSessionManager({
// The constructor takes an `opts` object, and mostly passes it
// directly along to the underlying `redis` module. So all these
// parameters come from `redis`
host: "localhost", // default
port: 6369,

// NOTE: this option is used by `redis` and is also noticed by
// rivescript-redis: it's optional but recommended to set a
// prefix. The Redis keys otherwise are simply the username
// given to RiveScript.
prefix: "rivescript/"
})
});

// And carry on as normal. All user variables will be actively persisted
// in Redis (no need to call `getUservars()` and `setUservars()` to manage
// them yourself -- though these functions DO work and will get you current
// data from your Redis cache!)
```

# Async Objects in Conditions

Async/Await enables asynchronous object macros that return Promises to
be used *anywhere* throughout RiveScript. This means they can finally be
used inside of `*Condition`s! Example:

```rivescript
// <call>wait-limited $timeout $maxTimeout</call>
// If the $timeout > $maxTimeout, it resolves "too long" immediately.
// Otherwise it waits $timeout seconds and resolves "done"
> object wait-limited javascript
var timeout = parseInt(args[0]);
var max = parseInt(args[1]);
return new Promise(function(resolve, reject) {
if (timeout > max) {
resolve("too long");
} else {
setTimeout(function() {
resolve("done");
}, timeout*1000);
}
});
< object
+ can you wait # seconds
* <call>wait-limited <star> 6</call> == done => I can!
- No the longest I'll wait is 6 seconds.
```

# JavaScript API Changes

## Changed: reply() now returns a Promise
Expand Down Expand Up @@ -136,10 +215,6 @@ bot.setUservar(user, "name", "Alice").then(() => {
});
```

# Background

TBD.

[1]: https://github.com/aichaos/rivescript-js
[2]: https://www.twilio.com/blog/2015/10/asyncawait-the-hero-javascript-deserved.html
[3]: https://github.com/aichaos/rivescript-js/commit/2e6aae30bbecd4cba946ef95b085f023954dcb97?w=1
54 changes: 54 additions & 0 deletions contrib/redis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Redis for RiveScript

```
npm install rivescript-redis
```

This module implements a Redis cache driver for the RiveScript User Variable
Session Manager.

It lets you **actively** persist your user variables to Redis instead of
just in the bot's memory, so that your bot can easily recall user
information across reboots and even across separate machines by storing
them in Redis.

This module is part of the `rivescript-js` project which can be found at
https://github.com/aichaos/rivescript-js and is released under the same
license (MIT).

## Usage

```javascript
// Import rivescript and rivescript-redis
const RiveScript = require("rivescript"),
RedisSessionManager = require("rivescript-redis");

// Construct your RiveScript bot as normal...
let bot = new RiveScript({
utf8: true,

// Give it a new Redis session manager.
sessionManager: new RedisSessionManager({
// The constructor takes an `opts` object, and mostly passes it
// directly along to the underlying `redis` module. So all these
// parameters come from `redis`
host: "localhost", // default
port: 6369,

// NOTE: this option is used by `redis` and is also noticed by
// rivescript-redis: it's optional but recommended to set a
// prefix. The Redis keys otherwise are simply the username
// given to RiveScript.
prefix: "rivescript/"
})
});

// And carry on as normal. All user variables will be actively persisted
// in Redis (no need to call `getUservars()` and `setUservars()` to manage
// them yourself -- though these functions DO work and will get you current
// data from your Redis cache!)
```

## License

MIT.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rivescript",
"version": "2.0.0-beta.1",
"version": "2.0.0",
"description": "RiveScript is a scripting language for chatterbots, making it easy to write trigger/response pairs for building up a bot's intelligence.",
"keywords": [
"bot",
Expand Down
2 changes: 1 addition & 1 deletion src/rivescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ near future.
*/

// Constants
const VERSION = "2.0.0-beta.1";
const VERSION = "2.0.0";

// Helper modules
const Parser = require("./parser");
Expand Down
3 changes: 2 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ module.exports = {
node: {
fs: "empty"
},
target: "web"
target: "web",
mode: "development",
}

0 comments on commit a8e9356

Please sign in to comment.