1 | # Modules
|
2 |
|
3 | An agent can be extended with modules, offering additional functionality.
|
4 | evejs comes with a number of built in modules. Usage:
|
5 |
|
6 | agent.extend(moduleName);
|
7 | agent.moduleName = agent.loadModule(moduleName);
|
8 |
|
9 | Evejs contains the following built-in modules:
|
10 |
|
11 | - [Babble](#babble)
|
12 | - [Pattern](#pattern)
|
13 | - [Request](#request)
|
14 | - [RPC](#rpc)
|
15 |
|
16 |
|
17 | ## Babble
|
18 |
|
19 | Babble enables dynamic communication flows between agents by means of
|
20 | conversations. A conversation is modeled as a control flow diagram containing
|
21 | blocks `ask`, `tell`, `listen`, `iif`, `decide`, and `then`. Each block can
|
22 | link to a next block in the control flow. Conversations are dynamic:
|
23 | a scenario is build programmatically, and the blocks can dynamically determine
|
24 | the next block in the scenario. During a conversation, a context is available
|
25 | to store the state of the conversation.
|
26 |
|
27 | Evejs can be used together with [babble](https://github.com/enmasseio/babble),
|
28 | extending the agents with support for dynamic communication flows.
|
29 |
|
30 | Usage:
|
31 |
|
32 | agent.extend('babble');
|
33 |
|
34 | The full API and documentation can be found at the project page of babble:
|
35 |
|
36 | https://github.com/enmasseio/babble
|
37 |
|
38 | Example:
|
39 |
|
40 | Create a file **BabbleAgent.js** with the following contents:
|
41 |
|
42 | ```js
|
43 | var babble = require('babble');
|
44 | var eve = require('evejs');
|
45 |
|
46 | function BabbleAgent(id, props) {
|
47 | // execute super constructor
|
48 | eve.Agent.call(this, id);
|
49 |
|
50 | this.props = props;
|
51 |
|
52 | // babblify the agent
|
53 | this.extend('babble');
|
54 |
|
55 | // add a conversation listener
|
56 | this.listen('hi')
|
57 | .listen(function (message, context) {
|
58 | console.log(context.from + ': ' + message);
|
59 | return message;
|
60 | })
|
61 | .decide(function (message, context) {
|
62 | return (message.indexOf('age') != -1) ? 'age' : 'name';
|
63 | }, {
|
64 | 'name': babble.tell('hi, my name is ' + this.id),
|
65 | 'age': babble.tell('hi, my age is ' + this.props.age)
|
66 | });
|
67 |
|
68 | // connect to all transports provided by the system
|
69 | this.connect(eve.system.transports.getAll());
|
70 | }
|
71 |
|
72 | // extend the eve.Agent prototype
|
73 | BabbleAgent.prototype = Object.create(eve.Agent.prototype);
|
74 | BabbleAgent.prototype.constructor = BabbleAgent;
|
75 |
|
76 | // have a conversation with an other agent
|
77 | BabbleAgent.prototype.talk = function (to) {
|
78 | var name = this.id;
|
79 | var age = this.props.age;
|
80 |
|
81 | this.tell(to, 'hi')
|
82 | .tell(function (message, context) {
|
83 | if (Math.random() > 0.5) {
|
84 | return 'my name is ' + name;
|
85 | } else {
|
86 | return 'my age is ' + age;
|
87 | }
|
88 | })
|
89 | .listen(function (message, context) {
|
90 | console.log(context.from + ': ' + message);
|
91 | });
|
92 | };
|
93 |
|
94 | module.exports = BabbleAgent;
|
95 | ```
|
96 |
|
97 | Usage:
|
98 |
|
99 | ```js
|
100 | var BabbleAgent = require('./BabbleAgent');
|
101 |
|
102 | // create two agents
|
103 | var emma = new BabbleAgent('emma', {age: 27});
|
104 | var jack = new BabbleAgent('jack', {age: 25});
|
105 |
|
106 | // let jack have a conversation with emma
|
107 | jack.talk('emma');
|
108 | ```
|
109 |
|
110 |
|
111 | ## Pattern
|
112 |
|
113 | The `'pattern'` module extends an agent with support for pattern listening.
|
114 | Incoming messages can be matched against patterns.
|
115 | The agent will be extended with functions `listen` and `unlisten`. Cannot be
|
116 | used in conjunction with module `'babble'`.
|
117 |
|
118 | Usage:
|
119 |
|
120 | agent.extend('pattern' [, options]);
|
121 |
|
122 | Available options:
|
123 |
|
124 | - `stopPropagation: boolean`
|
125 | When true, a message will not be propagated to other pattern listeners as
|
126 | soon as there is a match with one of the listeners. Thus, up to one listener
|
127 | is triggered on an incoming message. Default value is false.
|
128 |
|
129 | When false (default), a message will be delivered at all matching pattern
|
130 | listeners. When true, a message will be be delivered at the first matching
|
131 | pattern listener only.
|
132 |
|
133 | Methods:
|
134 |
|
135 | - `Agent.listen(pattern: string | RegExp | Function, callback: Function)`
|
136 | Register an pattern listener, which is triggered when a message comes in which
|
137 | matches given pattern. The pattern can be a string (exact match), a
|
138 | regular expression, or a test function which is invoked as `pattern(message)`.
|
139 | When a message matches the pattern, the `callback` function is invoked as
|
140 | `callback(from, message)`.
|
141 |
|
142 | - `Agent.unlisten(pattern: string | RegExp | Function, callback: Function)`
|
143 | Unregister a registered pattern listener.
|
144 |
|
145 | Example:
|
146 |
|
147 | Save the following code as **PatternAgent.js**:
|
148 |
|
149 | ```js
|
150 | var eve = require('evejs');
|
151 |
|
152 | function PatternAgent(id) {
|
153 | // execute super constructor
|
154 | eve.Agent.call(this, id);
|
155 |
|
156 | // extend the agent with pattern listening functionality
|
157 | this.extend('pattern');
|
158 |
|
159 | // listen for messages containing 'hello' (case insensitive)
|
160 | this.listen(/hello/i, function (from, message) {
|
161 | // reply to the greeting
|
162 | this.send(from, 'Hi ' + from + ', nice to meet you!');
|
163 | });
|
164 |
|
165 | // listen for any message
|
166 | this.listen(/./, function (from, message) {
|
167 | console.log(from + ' said: ' + message);
|
168 | });
|
169 |
|
170 | // connect to all transports provided by the system
|
171 | this.connect(eve.system.transports.getAll());
|
172 | }
|
173 |
|
174 | // extend the eve.Agent prototype
|
175 | PatternAgent.prototype = Object.create(eve.Agent.prototype);
|
176 | PatternAgent.prototype.constructor = PatternAgent;
|
177 |
|
178 | module.exports = PatternAgent;
|
179 | ```
|
180 |
|
181 | Usage:
|
182 |
|
183 | ```js
|
184 | var PatternAgent = require('./PatternAgent');
|
185 |
|
186 | // create two agents
|
187 | var agent1 = new PatternAgent('agent1');
|
188 | var agent2 = new PatternAgent('agent2');
|
189 |
|
190 | // send a message to agent 1
|
191 | agent2.send('agent1', 'Hello agent1!');
|
192 | ```
|
193 |
|
194 |
|
195 | ## Request
|
196 |
|
197 | The `'request'` module adds support for sending requests and awaiting a reply.
|
198 |
|
199 | Usage:
|
200 |
|
201 | agent.extend('request' [, options]);
|
202 |
|
203 | Available options:
|
204 |
|
205 | - `timeout: number`
|
206 | Specify the timeout for a request in milliseconds. When no reply is received
|
207 | before the timeout is exceeded, the requests promise is rejected.
|
208 | Default value is 60000 ms.
|
209 |
|
210 | Methods:
|
211 |
|
212 | - `Agent.request(to: string | Object, message: string, message: *)`
|
213 | Send a request. The function returns a promise which resolves with the reply
|
214 | comes in.
|
215 |
|
216 | Example:
|
217 |
|
218 | Create a file **RequestAgent.js** containing:
|
219 |
|
220 | ```js
|
221 | var eve = require('evejs');
|
222 |
|
223 | function RequestAgent(id) {
|
224 | // execute super constructor
|
225 | eve.Agent.call(this, id);
|
226 |
|
227 | // extend the agent with support for requests
|
228 | this.extend('request');
|
229 |
|
230 | // connect to all transports provided by the system
|
231 | this.connect(eve.system.transports.getAll());
|
232 | }
|
233 |
|
234 | // extend the eve.Agent prototype
|
235 | RequestAgent.prototype = Object.create(eve.Agent.prototype);
|
236 | RequestAgent.prototype.constructor = RequestAgent;
|
237 |
|
238 | // implement the receive method
|
239 | RequestAgent.prototype.receive = function (from, message) {
|
240 | console.log(from + ' said: ' + message);
|
241 |
|
242 | // return value is send back as reply in case of a request
|
243 | return 'Hi ' + from + ', nice to meet you!';
|
244 | };
|
245 |
|
246 | module.exports = RequestAgent;
|
247 | ```
|
248 |
|
249 | Usage:
|
250 |
|
251 | ```js
|
252 | var RequestAgent = require('./RequestAgent');
|
253 |
|
254 | // create two agents
|
255 | var agent1 = new RequestAgent('agent1');
|
256 | var agent2 = new RequestAgent('agent2');
|
257 |
|
258 | // send a request to agent 1, await the response
|
259 | agent2.request('agent1', 'Hello agent1!')
|
260 | .then(function(reply) {
|
261 | console.log('reply: ' + reply);
|
262 | });
|
263 | ```
|
264 |
|
265 |
|
266 | ## RPC
|
267 |
|
268 | The RPC module allows your agents to communicate using JSON RPC 2.0. This can be used over all transport implementations. When using the HTTP transport, the request and reply are performed in the same HTTP session.
|
269 |
|
270 | Usage:
|
271 | ```
|
272 | agent.rpc = agent.loadModule('rpc',options);
|
273 | ```
|
274 | In the options you can define which functions you want to open up the the RPC module. You can supply these as an Object or an Array of function names. The possible ways to define the options are shown here:
|
275 |
|
276 | ```
|
277 | agent.add = function (params, [from]) {return params.a + params.b; }
|
278 | var options = ['add']
|
279 | ```
|
280 | ```
|
281 | agent.add = function (params, [from]) {return params.a + params.b; }
|
282 | var options = {add: agent.add};
|
283 | ```
|
284 | ```
|
285 | agent.rpcFunctions = {};
|
286 | agent.rpcFunctions.add = function (params, [from]) {return params.a + params.b; }
|
287 | var options = agent.rpcFunctions;
|
288 | ```
|
289 |
|
290 | Methods:
|
291 |
|
292 | - `agent.request(to: string | Object, {method: String, params: *, [id: String, jsonrpc: '2.0']})`
|
293 | Send a request. The function returns a promise which resolves with the reply comes in. Only the 'method' and the 'params' fields are required. Evejs will give the message an UUID and add the jsonrpc field as required by the JSON RPC 2.0 spec. The reply is delivered in the JSON RPC response format.
|
294 |
|
295 | Example:
|
296 |
|
297 | Using the `rpc` module, agents can easily send a message and await a response.
|
298 | Create a file **RPCAgent.js** containing:
|
299 |
|
300 | ```js
|
301 | var eve = require('evejs');
|
302 |
|
303 | function RPCAgent(id, props) {
|
304 | // execute super constructor
|
305 | eve.Agent.call(this, id);
|
306 |
|
307 | this.props = props;
|
308 |
|
309 | // load the RPC module
|
310 | this.rpc = this.loadModule('rpc', this.rpcFunctions);
|
311 |
|
312 | // connect to all transports provided by the system
|
313 | this.connect(eve.system.transports.getAll());
|
314 | }
|
315 |
|
316 | // extend the eve.Agent prototype
|
317 | RPCAgent.prototype = Object.create(eve.Agent.prototype);
|
318 | RPCAgent.prototype.constructor = RPCAgent;
|
319 |
|
320 | // create an object containing all RPC functions.
|
321 | RPCAgent.prototype.rpcFunctions = {};
|
322 |
|
323 | // create an RPC function
|
324 | RPCAgent.prototype.rpcFunctions.add = function(params, from) {
|
325 | return params.a + params.b;
|
326 | };
|
327 |
|
328 | module.exports = RPCAgent;
|
329 | ```
|
330 |
|
331 | Usage:
|
332 |
|
333 | ```js
|
334 | var RPCAgent = require('./RPCAgent');
|
335 |
|
336 | // create two agents
|
337 | var agent1 = new RPCAgent('agent1');
|
338 | var agent2 = new RPCAgent('agent2');
|
339 |
|
340 | // send a message to agent1
|
341 | var message = {method:'add', params: {a:1, b:3}};
|
342 | agent2.rpc.request('agent1', message).then(function(reply) {
|
343 | console.log('The agent told me that', params.a, '+', params.b, '=', reply.result);
|
344 | });
|
345 | }
|
346 | ```
|