## 1. Project Overview & Quickstart (senecajs/LICENSE)
# LICENSE
Open-source repository senecajs/LICENSE
### Repository Details
- **Repository:** [senecajs/LICENSE](https://github.com/senecajs/LICENSE)
- **Primary Language:** Code
*Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.*
## 2. Official Technical Reference & Guides (senecajs/senecajs.github.io)
## File: README.md
# senecajs.org
[![Build Status][travis-badge]][travis-url]
[![Gitter][gitter-badge]][gitter-url]
This repo contains the documentation website for [Seneca.js][]. These docs are available at
[senecajs.org][] or can be ran locally by cloning this repo and following the steps below.
## Run Locally
After cloning, you will need to get dependencies via npm,
```
npm install
```
Next simply build and serve to port `4000`,
```
npm run build
npm run docs
```
## Contributing
Seneca is an __open__ project and encourage participation. If you feel you can help in
any way, be it with examples, extra testing, tutorials, or new features please be our
guest.
Please make all content changes in the [/src/pages][] folder. All changes are built
just before we redeploy the site so you only need to include changes in your PR. Upon
your PR being accepted your changes will be deployed.
## License
Copyright (c) 2010 - 2017 Richard Rodger and other contributors. Licensed under [MIT][].
[/src/pages]: ./src/pages
[Seneca.js]: https://www.npmjs.com/package/seneca
[senecajs.org]: http://www.senecajs.org/
[Seneca]: http://senecajs.org
[travis-badge]: https://travis-ci.org/senecajs/senecajs.org.svg?branch=master
[travis-url]: https://travis-ci.org/senecajs/senecajs.org.svg?branch=master
[gitter-badge]: https://badges.gitter.im/Join%20Chat.svg
[gitter-url]: https://gitter.im/senecajs/seneca
[MIT]: ./LICENSE
---
## File: src/pages/docs/tutorials/how-to-write-a-plugin.md
---
layout: content.html
title: How to Write a Seneca Plugin
---
# How to Write a Seneca Plugin
When you use the Seneca framework, you write plugins all the
time. They are an easy way to organize your action patterns.
A Seneca plugin is just a function that gets passed an _options_
object, and has a Seneca instance as its _this_ variable. You
then [_add_][] some action patterns in the body of the function,
and you're done. There is no callback.
This article will show some plugin examples, with code, going from
basic to advanced. It will cover the plugin API, and the conventions
to use when writing them. You'll need to log the behaviour of your
plugins, and you'll need to know how to debug them, so that will be
discussed too.
There are many Seneca plugins published on [NPM][]. Most of them
can be extended and modified by overriding their actions. You'll also
need to know how to do this.
Finally, plugins provide you with a way to organize your own code, and
to make use of the [micro-services][] approach to software
architecture, so that will be discussed too.
## Contents
- [A Simple Plugin](#wp-simple)
- [Initializing a Plugin](#wp-init)
- [A Plugin is a Module](#wp-module)
- [Give Your Plugin a Name](#wp-name)
- [Dealing with Options](#wp-options)
## A Simple Plugin
Let's write a plugin that defines one action. The action uses the
plugin _options_ argument to build a result.
``` js
var plugin = function( options ) {
this.add( {foo:'bar'}, function( args, done ) {
done( null, {color: options.color} )
})
}
```
The example above defines a single action
pattern, _foo:bar_. This action provides a result based on the
options provided to the plugin. Plugin options are not required, but
if they are provided, they are passed in as the first argument to the
plugin definition function. The _options_ argument is just a
JavaScript object with some properties. Seneca makes sure it always
exists. Even in the case where you have no options, you'll still get
an empty object.
The context object of the plugin function (that is, the value
of _this_), is a Seneca instance that you can use to define
actions. That means you don't need to
call `require('seneca')` when defining a plugin. This
Seneca instance provides the standard API, but the logging methods are
special - they append information about the plugin. So when you
call `this.log.debug('stuff about my plugin')`, the log
output will contain extra fields identifying the plugin, such as its
name. In this example, you haven't given the plugin a name (you'll see
how to do that in a moment), so Seneca will generate a short random
name for you.
You can use the plugin by calling the [use][] method
of the Seneca object. This loads the plugin into Seneca, after which
the action patterns defined by the plugin are available. You can then
call the [act][] method to trigger them, like so:
``` js
// simple.js
var seneca = require('seneca')()
var plugin = function( options ) { ... } // as above
seneca.use( plugin, {color:'pink'} )
seneca.act( {foo:'bar'}, console.log )
```
This code is available in
the [doc/examples/write-a-plugin][]
example, in the _simple.js_ script. Running the script produces:
``` bash
$ node simple.js
null { color: 'pink' }
```
In the output, the _null_ is the first argument
to _console.log_, and indicates that there was no error. The
output is a JavaScript object with single property _color_, the
value of which is set from the original options given to the plugin.
## Initializing a Plugin
Let's look at our example again.
``` js
// simple.js
var plugin = function( options ) {
this.add( {foo:'bar'}, function( args, done ) {
done( null, {color: options.color} )
})
}
```
As we can see, a plugin is just a function. You can see that there is no callback
passed into this function that defines the plugin. So, how does Seneca
know that the plugin has fully initialized? It's an important
question, because the plugin might depend on establishing a database
connection before it can operate properly.
Many plugins don't even need to initialize, because all they do is define a set of action
patterns. Let's say in this case we would like to initialise our plugin. As with most things in Seneca,
you define an action pattern to handle initialization and make sure it happens in the proper order.
``` js
// init.js
var plugin = function( options ) {
seneca.add( {init:'pluginName'}, function( args, done ) {
// do stuff, e.g.
console.log('connecting to db...')
setTimeout(function(){
console.log('connected!')
done()
}, 1000)
})
this.add( {foo:'bar'}, function( args, done ) {
done( null, {color: options.color} )
})
return 'pluginName'
}
```
For this to work, our plugin needs to have a name. Plugin name and `init` value must be exactly the same. In this case `return 'pluginName'` serves that purpose. See [Give Your Plugin a Name](#wp-name) for alternatives to this approach.
When plugin is fed into `use` method, seneca waits for its init to finish before continuing. That's why we call `done()` even when it does nothing.
## A Plugin is a Module
The Seneca _use_ method can also accept module references. That
is, if you can _require_ it, you can _use_ it! Let's update the
simple example to show this. First, create a file called _foo.js_
containing the plugin code (all the files in this article are available on
the Seneca github
at ([doc/examples/write-a-plugin][]).
``` js
// foo.js
module.exports = function( options ) {
this.add( {foo:'bar'}, function( args, done ) {
done( null, {color: options.color} )
})
}
```
The _foo.js_ file is a normal JavaScript file you can load into Node.js with _require_. It exposes a single function that takes the plugin _options_. To use the plugin, the code is almost the same as before, except that you pass in the _foo.js_ relative file path in the same way you would for _require_.
``` js
// module.js
var seneca = require('seneca')()
seneca.use( './foo.js', {color:'pink'} )
seneca.act( {foo:'bar'}, console.log )
```
The code produces the same output as before:
```bash
$ node module.js
null { color: 'pink' }
```
As well as local files and local modules, you can use public plugin modules
from [npmjs.org][]. Let's use [seneca-echo plugin][] as an example. This plugin echoes back arguments you send to the _role:echo_ pattern.
First, _npm install_ it:
```bash
$ npm install seneca-echo
```
Then use it:
``` js
// echo.js
var seneca = require('seneca')()
seneca.use( 'seneca-echo' )
seneca.act( {role:'echo', foo:'bar'}, console.log )
```
Running _echo.js_ produces:
``` js
$ node echo.js
null { foo: 'bar' }
```
You aren't using any options in this example. The _seneca-echo_
plugin just reproduces the arguments passed in. In this
case _foo:bar_. The _role_ property is not included in
the output.
The Seneca framework comes with [many plugins][] written by the community. Feel free to write one yourself (after reading this article!). By convention, public and generically useful
plugins are prefixed with _seneca-_ as part of their name. This
lets you know the module is a Seneca plugin if you see it on
NPM. However, its a bit tedious to type in "seneca-" all the time, so
you are allowed to abbreviate plugin names by dropping the "seneca-"
prefix. That means you can use the the _seneca-echo_ by just
providing the "echo" part of the name:
``` js
seneca.use( 'echo' )
```
## Give Your Plugin a Name
Your plugin needs a name. You can return a string from the plugin
definition function to give it one. When you look at the Seneca logs,
you can see what your plugin is doing. Let's try it!
``` js
// name0.js
var plugin = function( options ) {
this.add( {foo:'bar'}, function( args, done ) {
done( null, {color: options.color} )
})
return 'name0'
}
var seneca = require('seneca')()
seneca.use( plugin, {color:'pink'} )
seneca.act( {foo:'bar'}, console.log )
```
And then run it like so:
```bash
$ node name0.js --seneca.log=plugin:name0
... DEBUG act name0 - yvgt5y48wqjb IN {foo=bar} ...
... DEBUG act name0 - yvgt5y48wqjb OUT {color=pink} ...
null { color: 'pink' }
```
This uses Seneca's log filtering feature to focus on the log lines
that you care about. For more details on log filtering, read
the [logging tutorial][].
To avoid repetition, the public plugins drop their "seneca-" prefix
when registering their names. Try this:
```bash
$ node echo.js --seneca.log=plugin:echo
... DEBUG plugin echo - add echo - {role=echo} ...
... DEBUG act echo - lkmlk29r6uwt IN {role=echo,foo=bar} ...
... DEBUG act echo - lkmlk29r6uwt OUT {foo=bar} ...
null { foo: 'bar' }
```
You may have noticed something interesting. There were three lines of
logging output that time. Why didn't you see an "add" line for your
"name0" plugin? During the execution of its definition function, it
didn't have a name. You only gave it one when you returned a
name. Sometimes this is useful, because you can set a name
dynamically. Still, is it possible to set the name initially? Yes! Just
give the defining function a name:
``` js
// name1.js
var plugin = function name1( options ) {
this.add( {foo:'bar'}, function( args, done ) {
done( null, {color: options.color} )
})
}
var seneca = require('seneca')()
seneca.use( plugin, {color:'pink'} )
seneca.act( {foo:'bar'}, console.log )
```
Running this gives:
```bash
$ node name1.js --seneca.log=plugin:name1
... DEBUG plugin name1 - add name1 - {foo=bar} ...
... DEBUG act name1 - b3uamicogfnm IN {foo=bar} ...
... DEBUG act name1 - b3uamicogfnm OUT {color=pink} ...
null { color: 'pink' }
```
When you load a plugin as a module then the module reference, as
supplied to the _use_ method, becomes the initial name of the
module (Of course, you can override this by returning your own name)
Here's the _foo.js_ plugin again:
```bash
$ node module.js --seneca.log=plugin:./foo.js
... DEBUG plugin ./foo.js - add ./foo.js - {foo=bar} ...
... DEBUG act ./foo.js - 47ssblskuj59 IN {foo=bar} ...
... DEBUG act ./foo.js - 47ssblskuj59 OUT {color=pink} ...
null { color: 'pink' }
```
There's an obvious risk that you might have a naming conflict. Seneca
allows this because it's more useful to have the ability to
override plugins. If you're defining your own set of plugin names,
it's best to choose a short prefix for your project. This is a good
idea in general for many frameworks!
For example, if you're working on the Manhattan project, choose the
prefix _mh_. Then call your "Trinity" plugin _mh-trinity_.
There are no hard and fast rules for naming your action
patterns. However, there are some conventions that help to organize
the patterns. Your plugin is providing functionality to the
system. This functionality fulfills a role in the system. So it makes
sense to use the form _role:plugin-name_ as part of your action
pattern. This creates a pattern namespace to avoid clashes with other
plugin patterns. The use of the word "role" also indicates that other
plugins may override some aspects of this role (that is, aspects of
this functionality) by providing extensions to some of the action
patterns.
For example,
the [seneca-vcache plugin][] overrides the standard entity patterns, of the
form _role:entity, cmd:*_. It does this to transparently add
caching to the database store operations.
Another common convention is to use the property "cmd" for the main
public commands exposed by the plugin. So, you might have, for
example:
``` js
var plugin = function trinity( options ) {
this.add( {role:'trinity', cmd:'detonate'}, function( args, done ) {
// ... compress plutonium, etc
})
}
```
Many of the public Seneca plugins on NPM follow this pattern. You may
find other patterns more useful in your own projects, so don't feel
obligated to follow this one.
If you load a plugin multiple times, only the last one loaded will be
used. You can however load multiple separate instances of the same
plugin, by using tag strings. NOTE: the action patterns will still be
overridden, unless the plugin handles this for you (like the example
below). The data store plugins, in particular, use this mechanism to
support multiple databases in the same system. For more details, read
the [data entities tutorial.][] data entities
tutorial.
Here's a simple example that uses tags. In this case,
the _bar.js_ plugin defines an action pattern using one of its
option properties. This means that different action patterns are
defined depending on the options provided.
``` js
// bar.js
module.exports = function( options ) {
var tag = this.context.tag
this.add( {foo:'bar', zed:options.zed}, function( args, done ) {
done( null, {color: options.color, tag:tag} )
})
}
```
You can access the tag value from the context property of the plugin
Seneca instance: `this.context.tag`
You still want to debug and track each instance of this plugin, so you
provide a tag each time you register it with the _use_
method. Tags can be supplied in two ways, either by description object
for the plugin, or by suffixing a _$_ character, and then the
tag, to the plugin module reference. Here's the example code:
``` js
// tags.js
var seneca = require('seneca')()
seneca.use( {name:'./bar.js',tag:'AAA'}, {zed:1,color:'red'} )
seneca.use( './bar.js$BBB', {zed:2,color:'green'} )
seneca.act( {foo:'bar',zed:1}, console.log )
seneca.act( {foo:'bar',zed:2}, console.log )
```
Running this code produces the output:
```bash
$ node tags.js
null { color: 'red', tag: 'AAA' }
null { color: 'green', tag: 'BBB' }
```
Using the debug log shows the different instances of the plugin in action:
```bash
$ node tags.js --seneca.log=plugin:./bar.js
... DEBUG plugin ./bar.js AAA add ./bar.js AAA {foo=bar,zed=1} ...
... DEBUG plugin ./bar.js BBB add ./bar.js BBB {foo=bar,zed=2} ...
... DEBUG act ./bar.js AAA pamds7vlteyv IN {foo=bar,zed=1} ...
... DEBUG act ./bar.js BBB 4uxz90gcczn5 IN {foo=bar,zed=2} ...
... DEBUG act ./bar.js AAA pamds7vlteyv OUT {color=red,tag=AAA} ...
null { color: 'red', tag: 'AAA' }
... DEBUG act ./bar.js BBB 4uxz90gcczn5 OUT {color=green,tag=BBB} ...
null { color: 'green', tag: 'BBB' }
```
To isolate a tag, use these log settings:
```bash
$ node tags.js --seneca.log=plugin:./bar.js,tag:AAA
... DEBUG plugin ./bar.js AAA add ./bar.js AAA {foo=bar,zed=1} ...
... DEBUG act ./bar.js AAA 9rp8luozaf92 IN {foo=bar,zed=1} ...
... DEBUG act ./bar.js AAA 9rp8luozaf92 OUT {color=red,tag=AAA} ...
null { color: 'red', tag: 'AAA' }
null { color: 'green', tag: 'BBB' }
```
## Dealing with Options
It's useful to provide default option values for users of your
plugin. Seneca provides a utility function to support
this: `seneca.util.deepextend`. The `deepextend`
function works much the same
as [`_.extend`][],
except that it can handle properties at any level. For example:
``` js
// deepextend.js
var seneca = require('seneca')()
var foo = {
bar: 1,
colors: {
red: 50,
green: 100,
blue: 150,
}
}
var bar = seneca.util.deepextend(foo,{
bar: 2,
colors: {
red: 200
}
})
console.log(bar)
// { bar: 2, colors: { red: 200, green: 100, blue: 150 } }
```
The property `colors.red` is overridden, but the other colors retain
their default values.
You can use this in your own plugins. Let's add default options to
the _foo.js_ module (as above).
``` js
// foo-defopts.js
module.exports = function( options ) {
// Default options
options = this.util.deepextend({
color: 'red',
box: {
width: 100,
height: 200
}
},options)
this.add( {foo:'bar'}, function( args, done ){
done( null, { color: options.color,
box_width: options.box.width,
box_height: options.box.height
})
})
return {name:'foo'}
}
```
(As an aside, note that you can also specify the name of the
plugin by returning an object of the form `{name:...}`. You'll
see some more properties you can add this return object
below).
The default option structure is used as the base for the user supplied
options. Let's supply some user options that will override the defaults:
``` js
// module-defopts.js
var seneca = require('seneca')()
seneca.use( './foo-defopts.js', {
color:'pink',
box:{
width:50
}
})
seneca.act( {foo:'bar'}, console.log )
```
This code runs the _foo:bar_ action, which produces:
```bash
$ node module-defopts.js
null { color: 'pink', box_width: 50, box_height: 200 }
```
The default values for `color` and `box.width` (_red_ and _100_, respectively), have been overridden by the options provided as the second argument to `seneca.use` when the plugin is loaded (_pink_ and _50_).
You can load plugin options from configuration files. Seneca looks for a file named _seneca.options.js_ in the current folder, and _requires_ the file if it exists. This file should be a Node.js module that exports a JSON object. For example:
``` js
// seneca.options.js
module.exports = {
zed: {
red: 50,
green: 100,
blue: 150,
},
'zed$tag0': {
red: 55,
}
}
```
You can specify global Seneca options in this file, and you can
specify options for individual plugins. Top level properties that
match the name of a plugin are used to provide options to plugins when
they are loaded.
Let's see this in action. The _zed.js_ script defines a plugin
that prints out the plugin name and tag
using `this.context` (see above), and also prints out the
options provided to the plugin by Seneca.
``` js
// zed.js
function zed( options ) {
console.log( this.context.name, this.context.tag, options )
}
var seneca = require('seneca')()
seneca.use( zed )
```
As the example _seneca.options.js_ file defines a _zed_ property, this is used to provide options to the _zed_ plugin. Running the _zed.js_ script prints out the options loaded from _seneca.options.js_:
```bash
$ node zed.js
zed undefined { red: 50, green: 100, blue: 150 }
```
If you are using tags to create multiple instances of the same plugin, you can use the _$suffix_ convention to specify options particular to a given tagged plugin instance. The _zed-tag.js_ script is the same as the _zed.js_ script, except that it also creates an additional tagged instance of the _zed_ plugin. Note that the definition of the plugin uses a properties object, with the `init` property specifying the plugin definition function.
``` js
// zed-tag.js
function zed( options ) {
console.log( this.context.name, this.context.tag, options )
}
var seneca = require('seneca')()
seneca.use( zed )
seneca.use( {init:zed, name:'zed', tag:'tag0'} )
```
The _seneca.options.js_ file also defines a _zed$tag0_ property, and the options for the _tag0_ instance of the _zed_ plugin are taken from this. However, if you run the code, you'll notice that it also picks up the options defined for the main _zed_ plugin. These become base defaults, so that the special case option, `red: 55` overrides the main value.
``` js
$ node zed-tag.js
zed undefined { red: 50, green: 100, blue: 150 }
zed tag0 { red: 55, green: 100, blue: 150 }
```
Sometimes you need to access to all the options provided to Seneca. For
example, there is a global _timeout_ value that you might want to
use for timeouts. The _transport_ family of plugins do this, see [redis-transport][] for an example.
Inside your plugin function, you can call `this.options()`
to get back an object containing the entire Seneca options tree:
``` js
// zed-access.js
function zed( options ) {
console.log( this.options() )
}
var seneca = require('../../../lib/seneca.js')()
seneca.use( zed )
```
Running this script produces:
```bash
$ node zed-access.js
{ ...
timeout: 33333,
...
zed: { red: 50, green: 100, blue: 150 },
'zed$tag0': { red: 55 },
...
}
```
You are not required to use the _seneca.options.js_ file. If it exists, it will be loaded and used as the base default for options. You can specify your own configuration file (or an object containing option values), by providing an argument to `seneca.options()`. This is useful for different deployment scenarios. For example, the file _dev.options.js_ defines a custom configuration for the _zed_ plugin:
``` js
// dev.options.js
module.exports = {
zed: {
green: 110,
}
}
```
The _zed-dev.js_ script uses this options file, but also gets the default options from _seneca.options.js_:
``` js
function zed( options ) {
console.log( this.context.name, this.context.tag, options )
}
var seneca = require('seneca')()
seneca.options('./dev.options.js')
seneca.use( zed )
```
And the output has the overridden value for the `green` option.
```bash
$ node zed-dev.js
zed undefined { red: 50, green: 110, blue: 150 }
```
Finally, you can specify options on the command line, either via an argument, or an environment variable. Here are some examples using the _zed-dev.js_ script. Use the `--seneca.options` command line argument to provide option values. You can use "dot notation" to specify nested options, and you can specify multiple options:
```bash
$ node zed-dev.js --seneca.options.zed.red=10 --seneca.options.zed.blue=200
zed undefined { red: 10, green: 110, blue: 200 }
```
Alternatively, you can use the environment variable `SENECA_OPTIONS` to specify options that will be merged into the base defaults (using `seneca.util.deepextend`). The format is [jsonic][] jsonic, a lenient, abbreviated, fully compatible version of JSON for lazy developers.
```bash
$ SENECA_OPTIONS="{zed:{red:10,blue:200}}" node zed-dev.js
zed undefined { red: 10, green: 110, blue: 200 }
```
Command line options always override options from other sources. Here is the order of priority, from highest to lowest:
- Command line
- Environment variable
- Source code
- Custom options file
- Default options file
- Internal defaults
[_add_]: http://senecajs.org/api/#add-pattern-paramspec-action-
[NPM]: http://www.npmjs.org/search?q=seneca%20plugin
[micro-services]: http://martinfowler.com/articles/microservices.html
[use]: http://senecajs.org/api/#use-name-options-
[act]: http://senecajs.org/api/#act-input-callback-
[doc/examples/write-a-plugin]: https://github.com/senecajs/seneca/tree/master/doc/examples/write-a-plugin
[npmjs.org]: https://www.npmjs.org/search?q=seneca
[seneca-echo plugin]: https://www.npmjs.org/package/seneca-echo
[many plugins]: http://senecajs.org/plugins
[logging tutorial]: http://senecajs.org/tutorials/logging-with-seneca.html
[seneca-vcache plugin]: https://github.com/senecajs/seneca-vcache
[data entities tutorial.]: http://senecajs.org/tutorials/understanding-data-entities.html
[`_.extend`]: http://underscorejs.org/#extend
[redis-transport]: https://github.com/senecajs/seneca-redis-transport/blob/master/redis-transport.js
[jsonic]: https://github.com/rjrodger/jsonic
---
## File: src/pages/docs/tutorials/logging-with-seneca.md
---
layout: content.html
---
# Logging with Seneca
This tutorial shows you how to control Seneca's logging output. Clone the [main Seneca repository][] from github, and
open the _doc/examples_folder.
You'll use the Sales Tax example code. This code shows you how to handle sales tax rules using Seneca. Take a look at
the [main README][] for details. For this tutorial, you'll focus on the logging output.
Here's some code to calculate sales tax. It won't work, because you haven't actually told Seneca how to do that yet.
```
var seneca = require('seneca')()
seneca.act({cmd: 'salestax', net: 100}, function (err, result) {
if (err) return console.error(err)
console.log(result.total)
})
```
This invokes a Seneca action that, hopefully, calculates sales tax. Arbitrarily you're using the property `cmd` to
indicate what you want done (calculate sales tax), and `net` is net price before tax. The `callback` function returns
the total price, and uses the _standard Node.js signature_ (error object as first parameter).
Let's try to run this code, even though it will fail. In the examples folder, this code is saved in the file `sales-tax-error.js`. Run this file using _Node.js_, and you'll see the following output:
```
$ node sales-tax-error.js
[-isodate-] INFO init start
[-isodate-] INFO init end
[-isodate-] ERROR fail seneca/act_not_found
Seneca: act(args,cb):
action not found for args = {"cmd":"salestax","net":100}
{ [Error: ...] }
```
Seneca outputs some logging information so you can track what's going
on. The `INIT` entries log the start and end of the initialization phase, when Seneca loads plugins.
The `ERROR` entry tells you what went wrong: no action pattern matched the input args in the JSON document:
`{"cmd":"salestax","net":100}`. The code also prints the JavaScript Error object to the console. That's the line:
`if( err ) return console.error(err);`
You can fix this by defining an action:
```
seneca.add( {cmd:'salestax'}, function(args,callback){
var rate= 0.23
var total = args.net * (1+rate)
callback(null,{total:total})
})
```
The file `sales-tax.js` in the examples folder contains the new code. Run it:
```
$ node sales-tax.js
[-isodate-] INFO init start
[-isodate-] INFO init end
123
```
Well that worked! 23% sales tax on a price of $100 gives a total of $123. Fabulous!
You might find that logging output annoying. Turn it off with:
```
$ node sales-tax.js --seneca.log.quiet
123
```
Or you might be a logging freak, in which case, here's the all-you-can-eat version:.
```
node sales-tax.js --seneca.log.print
[-isodate-] INFO init start
... lots of init stuff ...
[-isodate-] INFO init end
[-isodate-] INFO add {cmd=salestax}
[-isodate-] DEBUG act in 90xkee {cmd=salestax,net=100}
[-isodate-] DEBUG act out 90xkee {total=123}
123
```
So you might be wondering how to get finer-grained logging output. Logging can be filtered on:
- `level`: `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`
- `type`: short string code, examples: `init`, `plugin`, `error`, ...
- `plugin`: the names of the plugin
- `tag`: an identifier tag, used when you have multiple instances of the same plugin
Let do that now:
```
node sales-tax.js --seneca.log=type:act
[-isodate-] DEBUG act in actid0 {cmd=salestax,net=100}
[-isodate-] DEBUG act out actid0 {total=123}
123
```
The command line argument `--seneca.log` accepts a
comma-separated list of filters. The filter `type:act` means only
output the log entries of type `act`. The `type` is the third
field. The `act` entries are very low level debugging logs
showing the operation of individual actions. The logs show the input
arguments, `in`, and the eventual output of the
action `out`. Because these can be separated in time, a random
action identifier (above: `actid0`) is generated for each action, so that you
can match up the input and output.
## Logging from Plugins
Let's turn the sales tax calculator into a plugin. This plugin accepts
two configuration options. You can specify the tax rate, and the
country which applies that rate (using two letter country codes).
Here's the client code, in the file `sales-tax-log.js`:
```
var seneca = require('seneca')()
seneca.use('sales-tax-plugin', {country: 'IE', rate: 0.23})
seneca.use('sales-tax-plugin', {country: 'UK', rate: 0.20})
seneca.ready(function (err) {
if (err) return process.exit(!console.error(err))
seneca.act({role: 'shop', cmd: 'salestax', country: 'IE', net: 100})
seneca.act({role: 'shop', cmd: 'salestax', country: 'UK', net: 200})
seneca.act({role: 'shop', cmd: 'salestax', country: 'UK', net: 300})
})
```
Since you're using log files to trace the commands, you can drop the
callback function from the `act` method call.
Now you need a plugin - that's in the `sales-tax-plugin.js` file:
```
module.exports = function (options) {
var seneca = this
var plugin = 'shop'
var country = options.country || 'IE'
var rate = options.rate || 0.23
var calc = function (net) {
return net * (1 + rate)
}
seneca.add({ role: plugin, cmd: 'salestax', country: country }, function (args, callback) {
var total = calc(parseFloat(args.net, 10))
seneca.log.debug('apply-tax', args.net, total, rate, country)
callback(null, { total: total })
})
seneca.add({ role: plugin, cmd: 'salestax' }, function (args, callback) {
var total = calc(parseFloat(args.net, 10))
seneca.log.debug('apply-tax', args.net, total, rate, country)
callback(null, { total: total })
})
seneca.act({ role: 'web', use: {
prefix: 'shop/',
pin: { role: 'shop', cmd: '*' },
map: {
salestax: { GET: true }
}
}})
return {
name: plugin
}
}
```
The plugin creates a separate instance of the `salestax` object
for each country and one instance that matches a call with no country. This object stores the country rate, country code,
and the number of times that sales tax for that country is calculated
(hit count).
This plugin follows the standard format for Seneca plugins. You provide a function that takes
a set of options and it's called in the Seneca context (eg the `this` object). Using the Seneca instance, you add some actions, and
return a plugin object:
```
module.exports = function (options) {
var seneca = this
seneca.add( { ... }, function (args, callback) {
...
})
return {
name: 'plugin-name'
}
}
```
Run this code, and filter the log to show only debug output from the sales-tax plugin:
```
$ node sales-tax-log.js --seneca.log=plugin:shop
[-isodate-] DEBUG plugin sales-tax IE annv4h
net: 100 total: 123 tax: {hits=1,rate=0.23,country=IE}
[-isodate-] DEBUG plugin sales-tax UK 3rkaa2
net: 200 total: 240 tax: {hits=1,rate=0.2,country=UK}
[-isodate-] DEBUG plugin sales-tax UK cwxcts
net: 300 total: 360 tax: {hits=2,rate=0.2,country=UK}
```
These logs appear because the plugin calls `seneca.log.debug` and provides the information about the sales tax calculation:
```
seneca.add({ role: plugin, cmd: 'salestax', country: country }, function (args, callback) {
var total = calc(parseFloat(args.net, 10))
seneca.log.debug('apply-tax', args.net, total, rate, country)
callback(null, { total: total })
})
```
The object `seneca.log` has convenience functions for the built-in log levels:
- seneca.log.debug
- seneca.log.info
- seneca.log.warn
- seneca.log.error
- seneca.log.fatal
These functions concatenate all their arguments into an array, which is the content of the log message. This array is then
formatted for display or storage by a handler function (which you can provide).
To minimize impact on performance, log data is only evaluated if a
matching log filter is active. The last argument to the logging
function can be a function (which should return an array of values), which again is only executed if a logging
filter matches.
Here's another example. This time, you filter on the `act` log
entry type. This allows you to see the data passing into and out of
actions:
```
$ node sales-tax-log.js --seneca.log=type:act
[-isodate-] DEBUG act in uk74hd {cmd=salestax,country=IE,net=100}
[-isodate-] DEBUG act out uk74hd {total=123}
[-isodate-] DEBUG act in qv5sts {cmd=salestax,country=UK,net=200}
[-isodate-] DEBUG act out qv5sts {total=240}
[-isodate-] DEBUG act in 7j9q4a {cmd=salestax,country=UK,net=300}
[-isodate-] DEBUG act out 7j9q4a {total=360}
```
You can see two entries for each action, `in`
and `out`. Each entry shows the JSON document data being passed
into Seneca, and out of, Seneca. You can also see that each pair has the same action identifier, such as `uk74hd`.
Let's put this all together. You want to see the input and output
data of the actions, and anything the sale tax plugin decides to log:
```
$ node sales-tax-log.js --seneca.log=plugin:sales-tax --seneca.log=type:act
[-isodate-] DEBUG act in cpvycd {cmd=salestax,country=IE,net=100}
[-isodate-] DEBUG plugin sales-tax IE cpvycd
net: 100 total: 123 tax: {hits=1,rate=0.23,country=IE}
[-isodate-] DEBUG act out cpvycd {total=123}
[-isodate-] DEBUG act in tx5zj3 {cmd=salestax,country=UK,net=200}
[-isodate-] DEBUG plugin sales-tax UK tx5zj3
net: 200 total: 240 tax: {hits=1,rate=0.2,country=UK}
[-isodate-] DEBUG act out tx5zj3 {total=240}
[-isodate-] DEBUG act in 8ikumj {cmd=salestax,country=UK,net=300}
[-isodate-] DEBUG plugin sales-tax UK 8ikumj
net: 300 total: 360 tax: {hits=2,rate=0.2,country=UK}
[-isodate-] DEBUG act out 8ikumj {total=360}
```
This shows the detailed processing of the sales tax calculation. The
action identifiers, which you can get using `args.actid$` inside
an action function.You need to specify two `--seneca.log`
filters, as the type is `plugin` for one, and `act` for the
other.
The `tag` filter can be used to focus on a specific, tagged, plugin instance. Here's how you look at UK sales tax
operations only:
```
$ node sales-tax-log.js --seneca.log=plugin:sales-tax,tag:UK
[-isodate-] DEBUG plugin sales-tax UK i2r7wn
net: 200 total: 240 tax: {hits=1,rate=0.2,country=UK}
[-isodate-] DEBUG plugin sales-tax UK 8ir490
net: 300 total: 360 tax: {hits=2,rate=0.2,country=UK}
```
## Live Logs in Your Browser
Console logs are fun, but live logs in your web browser are awesome! Seneca can do this too:
You'll need to create an app that provides a sales-tax calculation HTTP JSON API. Using the
`web` plugin this is easy. This plugin accepts JSON documents from remote clients
over HTTP and submits them to the local Seneca instance.
Here the code, in `sales-tax-app.js`, that sets up the app:
```
var connect = require('connect')
var connect_query = require('connect-query')
var body_parser = require('body-parser')
var seneca = require('seneca')()
seneca.use('sales-tax-plugin', {country: 'IE', rate: 0.23})
seneca.use('sales-tax-plugin', {country: 'UK', rate: 0.20})
seneca.use('sales-tax-plugin', {country: '*', rate: 0.25})
var app = connect()
app.use(connect_query())
app.use(body_parser.json())
app.use(seneca.export('web'))
app.listen(3000)
seneca.use('data-editor')
seneca.use('admin', {server: app, local: true})
```
The script sets up a simple HTTP server, using the Node.js `connect` module. The `web` plugin is preloaded by Seneca and
works locally without any configuration, so all you have to do is hook it as a [connect][] or [express][] middleware, or directly with the standard HTTP API:
```
var app = connect()
app.use(seneca.export('web'))
app.listen(3000)
```
The `admin` and `data-editor` plugins together provide a web administration interface for
Seneca. It uses web sockets, so you need to provide a reference to the
http server object in the plugin options. To expose the
administration web interface locally without requiring a password,
use the `local:true` option:
```
seneca.use('data-editor')
seneca.use('admin', {server: app, local: true})
```
Run this app, and open [localhost:3000/admin][]. You can
still use command line logging - you can have multiple separate logging channels.
```
$ node sales-tax-app.js --seneca.log=plugin:shop
```
The administration interface let's you set filtering options. They work the same way as the command line options.
There's nothing to log yet, so let's generate some sales tax calculations!
The file `sales-tax-app-client.js` contains the client
code. We're using the standard Node HTTP client here:
```
var http = require('http')
http.get({
hostname: 'localhost',
port: 3000,
path: '/shop/salestax?net=100&country=UK'
}, function (res) {
res.on('data', function (chunk) {
console.log(JSON.parse(chunk.toString()))
})
})
```
But you can also test it with cUrl:
```
$ curl -S 'http://localhost:3000/shop/salestax?net=100&country=UK'
$ {"total":120}
```
The sales tax operations code is as before. This is the key idea
behind Seneca - your business logic code stays the same, but you can
move around and reconfigure where it happens without worrying about
refactoring your code.
With the app up and running, run the client: you'll see log entries in the web interface.
## Log Handlers
You can define your own log handlers programmatically when you setup Seneca. The file
`sales-tax-log-handler.js` shows you how to do this:
```
var seneca = require('seneca')
// need this to get a reference to seneca.loghandler
seneca = seneca({
log: {
map: [
{plugin: 'shop', handler: 'print'},
{level: 'all', handler: seneca.loghandler.file('shop.log')}
]
}
})
seneca.use('sales-tax-plugin', {rate: 0.23})
seneca.ready(function (err) {
if (err) return process.exit(!console.error(err))
seneca.act({role: 'shop', cmd: 'salestax', net: 100})
seneca.act({role: 'shop', cmd: 'salestax', net: 200})
seneca.act({role: 'shop', cmd: 'salestax', net: 300})
})
```
Running this script will output log entries both to the console (only where plugin is "shop" ), and
to a log file `shop.log`, which gets everything. In production you mostly just want to output to the console and use
the operating system tools for file redirection. The file handler is mostly for creating special log files.
The logging map allows you to send log entries to multiple locations based on the filters you specify. You can still use the
command line argument `--seneca.log=...` to add further filters.
The built-in handlers are:
- `seneca.loghandler.print`: logs to the console
- `seneca.loghandler.file(filepath)`: logs to a file
- `seneca.loghandler.stream(WriteStream)`: logs to a stream
- `seneca.loghandler.emitter(EventEmitter)`: logs using events
You can write your own handler. It's just a function that takes the
log entry as first argument. The log entry is an array of values.
Here's an example using the [LogEntries.com][] service. This is cloud logging service that
stores your logs and makes them searchable. I wrote their Node.js API module :) - `$ npm install node-logentries`.
This example is in the file `sales-tax-logentries.js`:
```
var logentries = require('node-logentries')
var log = logentries.logger({
token: 'YOUR_TOKEN',
// redefine log levels to match the ones seneca uses
levels: {debug: 0, info: 1, warn: 2, error: 3, fatal: 4}
})
var seneca = require('seneca')({
log: {
map: [
{level: 'all', handler: function () {
log.log(arguments[1], Array.prototype.join.call(arguments, '\t'))
}}
]
}
})
seneca.use('sales-tax-plugin', {rate: 0.23})
seneca.ready(function (err) {
if (err) return process.exit(!console.error(err))
seneca.act({role: 'shop', cmd: 'salestax', net: 100})
seneca.act({role: 'shop', cmd: 'salestax', net: 200})
seneca.act({role: 'shop', cmd: 'salestax', net: 300})
})
```
You'll need to register a [LogEntries.com](https://logentries.com/) account and get a token for this to work.
You can use custom handler functions to send logs anywhere you want, and process them anyway you need.
## One More Thing ...
*Log filters are dynamic*. You can add new ones at runtime using the `seneca.logroute` method:
```
seneca.logroute( {level:'all', handler:seneca.handler.print} )
```
If you omit the handler, any previous filter is removed. If you add
multiple handlers for the same filter, the logs will be sent to all
the handlers.This feature enables the administration web site to
dynamically modify the filters at runtime.
-----
That's all folks!
[main Seneca repository]: http://github.com/senecajs/seneca
[main README]: https://github.com/senecajs/seneca
[connect]: http://www.senchalabs.org/connect/
[express]: http://expressjs.com
[localhost:3000/admin]: localhost:3000/admin
[LogEntries.com]: http://logentries.com
[@senecajs]: https://twitter.com/senecajs
---
## File: src/pages/docs/tutorials/seneca-with-promises.md
---
layout: content.html
title: Seneca with Promises
---
# Seneca with Promises
Even though Seneca does not come with promises built in, it is pretty trivial to use your favorite promise library and use it. In this tutorial we will use one of the most popular libraries out there, [Bluebird][].
### Basic Example
```js
var Promise = require('bluebird');
var seneca = require('seneca')();
// Promisify the .act() method; to learn more about this technique see:
// http://bluebirdjs.com/docs/features.html#promisification-on-steroids
var act = Promise.promisify(seneca.act, seneca);
// Return no error and a success message to illustrate a resolved promise
seneca.add({cmd: 'resolve'}, function (args, done) {
done(null, {message: "Yay, I've been resolved!"});
});
// Return an error to force a rejected promise
seneca.add({cmd: 'reject'}, function (args, done) {
done(new Error("D'oh! I've been rejected."));
});
// Use the new promisified act() with no callback
act({cmd: 'resolve'})
.then(function (result) {
// result will be {message: "Yay, I've been resolved!"} since
// its guaranteed to resolve
})
.catch(function (err) {
// Catch any error as usual if it was rejected
});
act({cmd: 'reject'})
.then(function (result) {
// Never reaches here since we throw an error on purpose
})
.catch(function (err) {
// err will be set with message "D'oh! I've been rejected."
});
```
Note that Bluebird v3 has some [promisification API changes](http://bluebirdjs.com/docs/new-in-bluebird-3.html). Instead of
```js
var act = Promise.promisify(seneca.act, seneca);
```
we have to use
```js
var act = Promise.promisify(seneca.act, {context: seneca});
```
### Handling Gate Executor Timeouts
Luckily the timeouts thrown by the gate executer are errors so the promise ends up being rejected and we can `.catch()` them as any other error.
```js
var Promise = require('bluebird');
var seneca = require('seneca')({ timeout: 500 });
var act = Promise.promisify(seneca.act, seneca);
// Add a command that takes a longer time than the seneca's timeout period
seneca.add({cmd: 'timeout'}, function (args, done) {
setTimeout(function () {
done(null, {message: 'resolve'});
}, 1000);
});
act({cmd: 'timeout'})
.then(function (result) {
// Never reaches here since the gate executer times out
})
.catch(function (err) {
// err will be set with a timeout error thrown by the gate executer
});
```
### Chaining `.act()` Commands
Since we have `.act()` promisified we can now chain them together and get really nice looking code.
```js
act({cmd: 'fetchOrderProducts', id: 'order-12345'})
.then(function (products) {
return act({cmd: 'adjustInventory', products: products});
})
.then(function (inventoryUpdates) {
return act({cmd: 'generateInventoryReport', updates: inventoryUpdates})
})
.catch(function (err) {
console.error(err);
});
```
### Advanced Example
Since we have the power of promises on our side we can do some pretty awesome stuff. Lets say we needed to convert a list of product prices in US dollars into Euros.
```js
var Promise = require('bluebird');
var seneca = require('seneca')();
// Promisify the .act() method
var act = Promise.promisify(seneca.act, seneca);
// Add a conversion command
seneca.add({cmd: 'dollars-to-euros'}, function(args, done) {
var exchangeRate = 0.88;
var euros = args.product.price * exchangeRate;
// Return the product with euros set
done(null, {
name: args.product.name,
price: args.product.price,
euros: euros
});
});
var products = [
{name: 'Product A', price: 9.99},
{name: 'Product B', price: 23.99},
{name: 'Product C', price: 10.00},
{name: 'Product D', price: 100.99},
{name: 'Product E', price: 0.99}
];
// Build an array of promisified commands
var cmds = [];
products.forEach(function (product) {
var command = act({cmd: 'dollars-to-euros', product: product});
cmds.push(command);
});
Promise.all(cmds)
.then(function (results) {
// results is now an array of each of the resolved promises
// {name: 'Product A', price: 9.99, euros: 8.81}
// {name: 'Product B', price: 23.99, euros: 21.15}
// {name: 'Product C', price: 10.00, euros: 8.82}
// {name: 'Product D', price: 100.99, euros: 89.05}
// {name: 'Product E', price: 0.99, euros: 0.87}
results.forEach(function (result) {
console.log(result);
});
})
.catch(function (err) {
console.error(err);
});
```
### Working with entities API
[Entities in Seneca](http://senecajs.org/tutorials/understanding-data-entities.html) provide a way to model your data in an [Active Record](http://www.martinfowler.com/eaaCatalog/activeRecord.html) fashion, where each entity exposes methods to interact with it such as `save$`, `load$`, `remove$` and `list$`.
Once an entity is created, via `seneca.make`, those methods can be promisified using the same technique as before, with `Promise.promisify`.
```js
'use strict';
const seneca = require('seneca')();
const Promise = require('bluebird');
const Entity = requre('seneca-entity');
seneca.use(Entity)
var entity = seneca.make('base', 'name', { some: 'data' });
// Promisify the .save$() method
var save$ = Promise.promisify(entity.save$, { context: entity });
save$()
.then(console.log)
.catch(console.error);
```
You may also promisify any of the other API CRUD methods this way.
[Bluebird]: https://www.npmjs.com/package/bluebird
---
## File: src/pages/docs/tutorials/understanding-data-entities.md
---
layout: content.html
---
# Entity
The Seneca framework provides a data entity API based loosely on the [ActiveRecord style][]. Here's how it works.
## The Seneca Philosophy
The Seneca framework is defined by a philosophy that [actions are better than objects][].
The only first-class citizens in the Seneca framework are _actions_. You register actions
in Seneca by defining a set of key-value pairs that the action matches. When a JSON
object is submitted to Seneca, it triggers an action if a matching set of key-value pairs
is found. The action returns another JSON object.
Actions can call other actions, and wrap existing actions. Groups of actions can work
together to provide specific functionality, such as user management. Such groups are
called __plugins__. To keep things organized, a few conventions are used. A `role`
property identifies a specific area of functionality. A `cmd` property identifies a
specific action.
For example:
``` js
seneca.act('role:entity,cmd:save', {ent:{...}}, (err,reply) => {...})
```
This action will save data entities, as part of the group of actions that perform the
`role` of data persistence. The `ent` property is an object containing the data of the
data entity to save.
In Seneca, data persistence is provided by a set of actions. These are:
`save`, `load`, `list`, `remove`. This provides a consistent interface
for all other actions that need to persist data.
As convenience, these data entity actions are also available in the form of data entity
objects, that expose the `cmd`'s as methods - just like the ActiveRecord pattern. However,
you cannot add business logic to these objects.
__Business logic belongs inside actions!__
## The Data Entity API
First you need a Seneca instance:
``` js
var seneca = require('seneca')()
var entities = require('seneca-entity')
seneca.use(entities)
```
Then you can create data entity objects:
``` js
var foo = seneca.make('foo')
```
The entity name is `foo`. If your underlying data store is
MongoDB, this data entity corresponds to the `foo`
collection. As a convenience, so you don't have to hook up a database, Seneca provides a transient in-memory store out of the
box (so you can just start coding!).
Next, add some data fields:
``` js
foo.name = 'Apple'
foo.price = 1.99
```
The data fields are just ordinary JavaScript object properties.
Now, you need to save the data:
``` js
foo.save$(function(err,foo){
console.log(foo)
})
```
The `save$` method invokes the `role:entity,cmd:save`
action, passing in the foo object as the value of `ent` argument.
The reason for the `$` suffix is to namespace the `cmd`
methods. You can always be 100% certain that vanilla property names
"just work". Stick to alphanumeric characters and underscore and you'll be fine.
The `save$` method takes a _callback_, using the standard
Node.js idiom: The first parameter is an error object (if there was an
error), the second the result of the action. The `save$` method provides
a new copy of the foo entity. This copy has been saved to persistent
storage, and includes a unique `id` property.
Once you've saved the data entity, you'll want to load it again at
some point. Use the `load$` method to do this, passing in
the `id` property.
``` js
var id = '...'
var foo_entity = seneca.make('foo')
foo_entity.load$( id, function(err,foo){
console.log(foo)
})
```
You can call the `load$` method on any data entity object
to load another entity of the same type. The original entity does
not change - you get the loaded entity back via the callback.
To delete entities, you also use the `id` property, with the
`remove$` method:
``` js
var id = '...'
var foo_entity = seneca.make('foo')
foo_entity.remove$( id, function(err){ ... })
```
To get a list of entities that match a query, use
the `list$` method:
``` js
var foo_entity = seneca.make('foo')
foo_entity.list$( {price:1.99}, function(err,list){
list.forEach(function( foo ){
console.log(foo)
})
})
```
The matching entities are returned as an array. The query is a set of
property values, all of which must match. This is equivalent to a SQL
query of the form: ` col1 = 'val1' AND col2 = 'val2' AND ... `.
Seneca provides a common query format that works
across all data stores. The trade-off is that these queries have
limited expressiveness (more on this later, including the *get-out-of-jail* options).
One thing you can do is sort the results:
``` js
foo_entity.list$( {price:1.99, sort$:{price:-1}}, function(err,list){
...
})
```
The `sort$` meta argument takes a sub-object containing a single key, the field to sort. The value `+1` means sort ascending,
and the value `-1` means sort descending. The common query format only accepts a sort by one field.
You can also use queries with the `load$` and `remove$` methods. The first matching entity is selected.
## Zone, Base and Name: The Entity Namespace
Your data can live in many different places. It can be persistent or transient. It may have
business rules that apply to it.It may be owned by different people.
Seneca lets you work with your data, without worrying about where it lives, or what rules should
apply to it. This makes it easy to handle different types of data in different ways. To make this
easier, Seneca provides a three layer namespace for data entities:
- `name`: the primary name of the entity. For example: `product`
- `base`: group name for entities that "belong together". For example: `shop`
- `zone`: name for a data set belonging to a business entity, geography, or customer. For example: `tenant001`
The zone and base are optional. You can just use the name element in the same way you use ordinary
database tables, and you'll be just fine. Here's an example of creating a `foo` entity (as seen
above):
``` js
var foo_entity = seneca.make('foo')
```
Often, a set of plugins that provide the related functions, will use
the same `base`. This ensures that the entities used by these
plugins won't interfere with your own entities.
For example, the [user][]
and [auth][] plugins,
which handle user accounts, and login/logout, use the `sys` base,
and work with the following entities:
``` js
var sys_user = seneca.make('sys','user')
var sys_login = seneca.make('sys','login')
```
The underlying database needs to have a name for the table or
collection associated with an entity. The convention is to join the
base and name with an underscore, as `'_'` is accepted by most database
systems as a valid name character. This means that `name`, `base` and
`zone` values should only be alphanumeric, and to be completely safe,
should never start with a number.
For the above plugins, the table or collection names would be:
`sys_user` and `sys_login`.
The `zone` element provides a higher level namespace that Seneca itself does not
use. It is merely a placeholder for your own needs. For example, you
may need to isolate customer data into separate physical databases.
The zone is never part of the database table name. You use it by
registering multiple instances of the same database plugin, pointing
at different physical databases. Seneca's pattern matching makes this
automatic for you (see the entity type mapping examples below).
You can also use the zone for custom business rules. The zone, base and name appear as action arguments - just pattern match the underlying actions! (and there are examples below).
### Creating an Entity with a Specific Zone, Base and Name
The `make` method is available on both the main Seneca object, and on each entity object (where it always has a $ suffix):
``` js
// the alias make$ will also work
var foo = seneca.make('foo')
// make() does not exist to avoid property clashes
var bar = foo.make$('bar')
```
It optionally accepts up to three string arguments, specifying the zone, base and name, always in that order:
``` js
var foo = seneca.make('foo')
var bar_foo = seneca.make('bar','foo')
var zen_bar_foo = seneca.make('zen','bar','foo')
```
When no arguments are given, calling `make$` on an entity will create a new instance of the same kind (same zone, base and name):
``` js
var foo = seneca.make('foo')
var morefoo = foo.make$()
```
No data is copied, you get a completely new, empty, data entity (use `clone$` instead to copy the data).
If you pass in an object as the last argument to `make$`, it will be used to initialize the entity data fields:
``` js
var foo = seneca.make('foo', {price:1.99,color:'red'})
console.log('price is '+foo.price+' and color is '+foo.color)
```
If you call the `toString` method on an entity, it will indicate the zone, base and name using the syntax `zone/base/name` as a prefix to the entity data:
```
$zone/base/name:{id=...;prop=val,...}
```
If any of the namespace elements are not defined, a minus `'-'` is used as placeholder:
```
$-/-/name:{id=...;prop=val,...}
```
The syntax `zone/base/name` is also used a shorthand for an
entity type pattern. For example, `-/bar/-` means any entities
that have base `bar`.
#### `entity.canon$([options])`
Each entity has a `canon$` method to extract or test equality of the `zone/base/name` properties.
```js
var apple = seneca.make('market','fruit');
// Get the properties
apple.canon$(); // -> '-/market/fruit'
apple.canon$({object: true}); // -> {zone: undefined, base: 'market', name: 'fruit'}
apple.canon$({array: true}); // -> [undefined, 'market', 'fruit']
// Test the properties by 'is a'
apple.canon$({isa: '-/market/fruit'}); // -> true
apple.canon$({isa: {base: 'market', name: 'fruit'}}); // -> true
apple.canon$({isa: '-/market/vegetable'}); // -> false
```
#### `entity.data$([options])`
Each entity also has a `data$` method to read and write to the entity.
```js
var apple = seneca.make('market','fruit');
apple.name = 'MacIntosh';
// Includes all $-properties
apple.data$(); // -> {'entity$': {zone: undefined, base: 'market', name: 'fruit'}, name: 'MacIntosh'}
// Exclude all $-properties
apple.data$(false); // -> {name: 'MacIntosh'}
// Update and add data
apple.data$({name: 'Golden Delicious', color: 'Yellow'});
```
## Using Databases to Store Entity Data
To store persistent data, you'll need to use an external
database. Each database needs a plugin that understands how to talk to
that database. The plugins normally use a specific driver module to do the actual talking.
For example, the [seneca-mongo-driver][] plugin
uses the [mongoDB][] module.
Using a data store plugin is easy. Register with Seneca and supply the database connection details as options to the plugin:
``` js
var seneca = require('seneca')()
seneca.use('mongo-store',{
name:'dbname',
host:'127.0.0.1',
port:27017
})
```
The database connection will need to be established before you can
save data. Use the `seneca.ready` function to supply a
callback that will be called once the database is good to go:
``` js
seneca.ready(function(err){
var apple = seneca.make$('fruit')
apple.name = 'Pink Lady'
apple.price = 1.99
apple.save$(function(err,apple){
if( err ) return console.log(err);
console.log( "apple = "+apple )
})
})
```
The `seneca.ready` function works for any plugin that has a callback dependency
like this - it will only be triggered once all the plugins are ready.
To close any open database connections, use the `seneca.close` method:
``` js
seneca.close(function(err){
console.log('database closed!')
})
```
### Data Store Plugins
To use a data store plugin, you'll normally need to install the module via npm:
```
npm install seneca-mongo-store
```
The data store plugins use a naming convention of the form `seneca--store`. The suffix `db` is dropped. Here are some of the existing data store plugins:
- JSON files (on disk) - [seneca-jsonfile-store][]
- MongoDB - [seneca-mongo-store][]
- MySQL - [seneca-mysql-store][]
- PostgreSQL - [seneca-postgres-store][]
- levelDB - [seneca-level-store][]
Refer to their project pages for details on behaviour and configuration options. As a convenience, Seneca allows you to drop the `seneca-` prefix when registering the plugin:
``` js
seneca.use('mongo-store',{ ... })
```
The default, built-in data store is `mem-store`, which provides a
transient in-memory store. This is very useful for quick prototyping
and allows you to get started quickly. By sticking to the common
entity feature set (see below), you can easily swap over to a real database at a
later point.
If you'd like to add support for a database to Seneca,
we are working on a _guide to writing data store plugins_, stay tuned!
### Mapping Entities to Data Stores
One of the most useful features of the Seneca data entity model is the
ability to transparently use different databases. This is enabled by
the use of Seneca actions for all the underlying operations. This
makes it easy to pattern match against specific entity zones, bases
and names and send them to different data stores.
You can use the `map` option when registering a data store plugin
to specify the data entity types that it should support. All others will be ignored.
The map is a set of key-value pairs, where the key is an entity type
pattern, and the value a list of entity `cmd`s
(such as `save`,`load`,`list`,`remove`,...),
or `'*'`, which means the mapping applies to all `cmd`s.
The example mapping below means that all entities with the name `tmp`,
regardless of zone or base, will use the transient `mem-store`:
``` js
seneca.use('mem-store',{ map:{
'-/-/tmp':'*'
}})
```
To use different databases for different groups of data, use the `base` element:
``` js
seneca.use('jsonfile-store',{
folder:'json-data', map:{'-/json/-':'*'}
})
seneca.use('level-store',{
folder:'level-data', map:{'-/level/-':'*'}
})
```
This mapping sends -/json/- entities to
the [jsonfile][] data store, and -/level/- entities to
the [leveldb][] data store.
Here it is in action:
``` js
seneca.ready(function(err,seneca){
;seneca
.make$('json','foo',{propA:'val1',propB:'val2'})
.save$(function(err,json_foo){
console.log(''+json_foo)
;seneca
.make$('level','bar',{propA:'val3',propB:'val4'})
.save$(function(err,level_bar){
console.log(''+level_bar)
}) })
})
```
The full source code is available in the data-entities folder of the [seneca examples repository][].
(The ; prefix is just a marker to avoid excessive indentation)
## Data Store Logging
You can track and debug the activity of data entities by reviewing the action log, and the plugin log for the datastore.
For example, run the example above, that uses both the jsonfile store and the leveldb store, using the `--seneca.log=type:act` log filter, and you get the output:
```
$ node main.js --seneca.log=type:act
...
2013-04-18T10:05:45.818Z DEBUG act jsonfile-store BCL wa8xc5 In {cmd=save,role=entity,ent=$-/json/foo:{id=;propA=val1;propB=val2},name=foo,base=json} gx38qi
2013-04-18T10:05:45.821Z DEBUG act jsonfile-store BCL wa8xc5 OUT [$-/json/foo:{id=ulw8ew;propA=val1;propB=val2}] gx38qi
...
2013-04-18T10:05:45.822Z DEBUG act level-store GPN 8dnjyt IN {cmd=save,role=entity,ent=$-/level/bar:{id=;propA=val3;propB=val4},name=bar,base=level} 8ml1p7
2013-04-18T10:05:45.826Z DEBUG act level-store GPN 8dnjyt OUT [$-/level/bar:{id=7de92fc0-f402-411d-80ea-59e435a8c398;propA=val3;propB=val4}] 8ml1p7
...
```
This shows the `role:entity, cmd:save` action of both data
stores. Seneca actions use a JSON-in/JSON-out model. You can trace
this using the `IN` and `OUT` markers in the log
entries. The `IN` and `OUT` entries are connected by an action identifier, such as `wa8xc5`.
This lets you trace actions when they interleave asynchronously.
The `IN` log entries show the action arguments, including the entity data, and the entity zone, base and name (if defined).
Once the action completes, the `OUT` log entries show the returned data. In particular, notice that the entities now have generated `id`s.
The data stores themselves also generate logging output. Try `--seneca.log=type:plugin` to see this:
```
$ node main.js --seneca.log=type:plugin
2013-04-18T10:39:54.961Z DEBUG plugin jsonfile-store QSG cop6lx save/insert $-/json/foo:{id=nt7usm;propA=val1;propB=val2} jsonfile-store~QSG~-/json/-
2013-04-18T10:40:19.802Z DEBUG plugin level-store JNG save/insert $-/level/bar:{id=7166037e-112d-448c-9afa-84e69d84aa25;propA=val3;propB=val4} level-store~JNG~-/level/-
```
In this case, the data stores creates a log entry for each save operation that inserts data. The entity data is also shown.
Each plugin instance gets a three letter tag, such as `QSG`, or `JNG`. This helps you distinguish between multiple mappings that use the same data store.
Each data store plugin instance can be ths be described by the name of the data store plugin, the tag, and the associated mapping. This is the last element of the log entry. For example:
`level-store~JNG~-/level/-`
[ActiveRecord style]: http://www.martinfowler.com/eaaCatalog/activeRecord.html
[actions are better than objects]: http://richardrodger.com
[user]: https://github.com/rjrodger/seneca-user
[auth]: https://github.com/rjrodger/seneca-auth
[seneca-mongo-driver]: https://github.com/senecajs/seneca-mongo-store
[mongoDB]: http://mongodb.github.io/node-mongodb-native/
[seneca-jsonfile-store]: http://github.com/rjrodger/seneca-jsonfile-store
[seneca-mongo-store]: http://github.com/senecajs/seneca-mongo-store
[seneca-mysql-store]: https://github.com/mirceaalexandru/seneca-mysql-store
[seneca-postgres-store]: https://github.com/marianr/seneca-postgres-store
[seneca-level-store]: https://github.com/senecajs/seneca-level-store
[guide to writing data store plugins]: /data-store-guide.html
[jsonfile]: https://github.com/rjrodger/seneca-jsonfile-store
[leveldb]: https://github.com/senecajs/seneca-level-store
[seneca examples repository]: https://github.com/senecajs/seneca-examples
---
## File: src/pages/docs/tutorials/understanding-prior-actions.md
---
layout: content.html
---
# Understanding Prior Actions
## The Seneca software component model
Software *components* are supposed to make your life easier. They are supposed to let you write production-ready code faster. They do this in four ways.
Software components are:
- **self-contained**, so they don't step on each other's toes;
- **reusable**, so you don't have to write so much code;
- **extensible**, so they're actually useful in the real world;
- **composable**, so you can build bigger things.
[plugins][] are designed to deliver on these four features.
## Pattern-based APIs make this easy
Seneca plugins are fundamentally just a list of action patterns.
- This makes them _self-contained_ because you must use messages (that match the patterns) to interact with the plugin.
- They are _reusable_, because you can load them into any Seneca microservice.
- They are _extensible_ because you can override patterns with your own functionality.
- And they are _composable_ because you can build up pattern behaviour with a function callback chain.
The ease of extensibility and composability are the primary benefits of the _pattern-based approach_.
Let's look at a simple example. Here's a plugin that converts color values between representations.
``` js
var seneca = require('seneca')()
seneca.use( function color() {
var map_name_hex = {
black: '000000',
red: 'FF0000',
green: '00FF00',
blue: '0000FF',
white: 'FFFFFF'
}
this
.add('role:color,cmd:convert', function (msg, respond) {
var out = { hex: map_name_hex[msg.name] }
respond( null, out )
})
})
// prints { hex: 'FF0000' }
seneca.act('role:color,cmd:convert,name:red', console.log)
// prints { hex: undefined } as yellow not recognized
seneca.act('role:color,cmd:convert,name:yellow', console.log)
```
This plugin only supports a limited range of colors. One way you can extend the set of supported colors is by adding special cases (in this case, we're ignoring the fact that the color name to hex feature is just a simple mapping data structure).
``` js
seneca.add('role:color,cmd:convert,name:yellow', function( msg, respond ) {
respond( null, { hex: 'FFFF00' })
})
// prints { hex: 'FFFF00' }
seneca.act('role:color,cmd:convert,name:yellow', console.log )
```
**This is standard Seneca best practice**. You are allowed to _define your own special cases_.
But what if you have many new colors you want to add? Another way to extend the `color` plugin is to **_override_** the existing pattern:
``` js
var more_name_hex = {
cyan: '00FFFF',
fuchsia: 'FF00FF'
}
seneca.add('role:color,cmd:convert', function (msg, respond) {
this.prior(msg, function (err, out) {
if (err) return respond(err)
if (!out.hex) {
out.hex = more_name_hex[msg.name]
}
respond(null, out)
})
})
// prints { hex: 'FFFF00' }, from override
seneca.act('role:color,cmd:convert,name:cyan', console.log)
// prints { hex: '00FFFF' }, from more specific custom pattern
seneca.act('role:color,cmd:convert,name:yellow', console.log)
// prints { hex: 'FF0000' }, from color plugin
seneca.act('role:color,cmd:convert,name:red', console.log)
```
The function `this.prior` is a reference to the _original_ action function for the `role:color,cmd:convert` pattern. This original action function knows how to handle the colors black, red, green, blue and white.
The new action function for `role:color,cmd:convert` first passes on the input message to the original action function, and if the original action function does recognize a color and produce a hex value, then the new action function does nothing.
This _original action function_ is known as the **_prior_**. If the prior does not provide a hex value, then the new action function checks for the colors it knows about, cyan and fuchsia, and handles those. The color yellow is still handled by the special case action function that specifically matches `name:yellow`.
Priors can be used in this way to *customize the behavior of any action pattern*.
## Understanding priors
**_Priors_** can be stacked. Each time you override an action pattern, you get a prior.
This prior may have its own prior from a previous definition of the action pattern. Thus you can compose layers of additional functionality.
For example, you can add *validation*, *auditing*, *throttling*, custom logging, input and output data manipulation, or trigger other messages. The calling code has no visibility of these customizations, and no need to know about them, so the plugin remains self-contained.
If you call `this.prior`, and there is no previous definition, then you get an empty response (null) back. In production code you should always handle this case.
Priors can be _strict_. This means that a prior only exists if there is an **exact** matching action pattern. Normally, priors are not strict, so that *sub-patterns will be priors*. Here's an example.
For the following patterns, added in this order:
- **a:1**
- **a:1,b:2**
- **a:1,b:2,c:3**
The prior chain is:
a:1,b:2,c:3 → a:1,b:2 → a:1.
In the example above, for `role:color,cmd:convert,name:yellow`, the prior is `role:color,cmd:convert`.
If you use the strict setting, then the priors will only be for exact matches. For the following patterns, added in this order:
- **a:1**
- **a:1,b:2,strict$:{add:false}**
- **a:1,b:2,c:3**
The prior chain is `a:1,b:2,c:3` → `a:1,b:2` only.
You can make every prior strict by setting the top level option:
`seneca({ strict: { add: true } })`
## Add order is significant!
In the same way that the order of plugin definition is significant,
the order of pattern overrides is also significant.
Seneca *checks only at definition time* for matching priors.
This is deliberate, so that you have well-defined behaviour
you can determine simply from reading the code.
Using the example above, if you add patterns in the order:
- **a:1,b:2,c:3**
- **a:1,b:2**
- **a:1**
Then there are _no_ priors.
**For this reason, take care when adding plugins whose purpose is mainly to extend existing patterns. They should be added after the main plugin that adds functionality.**
## Best practices for data entities
The Seneca [data entity patterns][] can be extended to handle special cases. You use calls to `this.prior` to perform the underlying data operations.
For example, Let's say you want to add a `last_updated` field to every data entity. Override the `role:entity,cmd:save` pattern to do this:
``` js
var seneca = require('seneca')()
seneca.add('role:entity,cmd:save', function( msg, respond ) {
msg.ent.last_updated = Date.now()
this.prior( msg, respond )
})
// prints $-/-/foo;id=9wl7sn;{bar:1,last_updated:1441383791347}
seneca.make$('foo').data$({bar:1}).save$( console.log )
```
The `role:entity,cmd:save` message contains an `ent` property with the Seneca entity data, which you can modify as desired.
In production systems, you'll tend to want to do a number of things to entities:
- **define general custom behaviors for all entities,**
- **define custom behaviors for certain types of entity,**
- **define custom behaviors for single entities.**
Use the standard `role:entity,cmd:save|load|remove|list` action patterns to define general customizations, as per the example above.
To define custom behaviour for a specific entity, make sure to add the pattern using the non-strict (default!) option:
``` js
seneca.add('role:entity,cmd:save,name:bar', function (msg, respond) {
msg.ent.zed = 1
this.prior(msg, respond)
})
// prints $-/-/foo;id=m3l3zp;{a:1,last_updated:1441384489162}
seneca.make$('foo').data$({ a: 1 }).save$(console.log)
// prints $-/-/bar;id=air0bm;{b:1,zed:1,last_updated:1441384489162}
seneca.make$('bar').data$({ b: 1 }).save$(console.log)
```
The `bar` _entity_ still gets the `last_updated` _field_. If you had used `strict$:{add:true}`, then it would not have.
Take care when using the `base` and `zone` fields for *name-spacing entities*.
If you define a custom behavior for all entities of the same base, this will work as intended.
But if you also define custom behaviors only using a name, then an entity that matches both the name and base will not trigger the base behavior, as it will not have the correct prior.
Here's a simple example of how the patterns work:
- **a:1**
- **a:1,b:2**
- **a:1,c:3**
has the prior chains:
- **a:1,b:2** → **a:1**
- **a:1,c:3** → **a:1**
Thus, the input message `{a:1, b:2, c:3}` will match \_`a:1,b:2` as `b` precedes `c` alphabetically, and patterns are disambiguated alphabetically. It will _not_ also match \_`a:1,c:3\`.
Similarly, if you have:
- **role:entity,cmd:save,name:foo**
- **role:entity,cmd:save,base:bar**
Then `{role:entity,cmd:save,name:foo,base:bar}` will not trigger any custom priors for _base:bar_.
The rule to follow is: if you are defining `base` behaviors, only define `name,base` behaviors for specific entities. If you are defining `zone` behaviors, only define `base,zone` and `name,base,zone behaviors`.
## Debugging priors
You can trace the structure of action patterns priors using the `--seneca.print.tree` command line option. Run the following code:
``` js
// filename: prior-debug.js
var seneca = require('seneca')()
seneca
.add('a:1', function( msg, respond ) {
respond( null, { a:1 })
})
.add('a:1,b:2', function( msg, respond ) {
this.prior( msg, function( err, out ) {
out.b = 2
respond( err, out )
})
})
.add('a:1,b:2,c:3', function( msg, respond ) {
this.prior( msg, function( err, out ) {
out.c = 3
respond( err, out )
})
})
.act( 'a:1,b:2,c:3', console.log )
```
And you'll see the output:
``` js
$ node prior-debug.js --seneca.print.tree
2015-09-04T17:00:55.445Z 5ftqv0kxp9zn/1441386055436/36136/- INFO hello Seneca/0.6.4/5ftqv0kxp9zn/1441386055436/36136/-
Seneca action patterns for instance: 5ftqv0kxp9zn/1441386055436/36136/-
└─┬ a:1
├── # root$, (e7roo),
└─┬ b:2
├── # root$, (d77dx),
│ # root$, (e7roo),
└─┬ c:3
└── # root$, (nzgbq),
# root$, (d77dx),
# root$, (e7roo),
null { a: 1, b: 2, c: 3 }
```
The last line `{ a: 1, b: 2, c: 3 }` is the expected output from the prior chain for the action pattern `a:1,b:2,c:3`. Above that is an textual tree diagram of the defined patterns in the Seneca instance.
Each pattern is represented by a leaf of the tree. On each leaf is a stack of prior function identifiers, showing the order in which the priors will be called.
For `a:1,b:2,c:3`, you can see that the priors for `a:1,b:2` (_d77dx_), and `a:1` (_e7roo_) will form the prior chain.
You can also use the option `--seneca.print.tree.all` to see all the system action patterns, not just your own.
Running the above program using `--seneca.log.all` also shows you the log output that allows you to trace the execution of the action patterns. Here's a sample run, showing only the lines of interest:
``` js
$ node prior-debug.js --seneca.log.all --seneca.log.short
...
159 3h/- DEBUG plugin root$ ADD (kjnse) a:1
159 3h/- DEBUG plugin root$ ADD (xtpzn) a:1,b:2
160 3h/- DEBUG plugin root$ ADD (460jf) a:1,b:2,c:3
...
160 3h/- DEBUG act root$ IN 0j/12 a:1,b:2,c:3 {a:1,b:2,c:3} ENTRY (460jf) - - -
164 3h/- DEBUG act root$ IN 7e/12 a:1,b:2 {a:1,b:2,c:3} PRIOR;(460jf) (xtpzn) - - -
165 3h/- DEBUG act root$ IN me/12 a:1 {a:1,b:2,c:3} PRIOR;(460jf),(xtpzn) (kjnse) - - -
165 3h/- DEBUG act root$ OUT me/12 a:1 {a:1} PRIOR;(460jf),(xtpzn) (kjnse) - - 0 -
167 3h/- DEBUG act root$ OUT 7e/12 a:1,b:2 {a:1,b:2} PRIOR;(460jf) (xtpzn) - - 3 -
167 3h/- DEBUG act root$ OUT 0j/12 a:1,b:2,c:3 {a:1,b:2,c:3} EXIT (460jf) - - 7 -
```
Here you can see the definitions of the action patterns, which gives the action identifiers:
- `a:1` → (*kjnse*)
- `a:1,b:2` → (*xtpzn*)
- `a:1,b:2,c:3` → (*460jf*)
These identifiers are included in the log lines of the IN/OUT actions calls so that you can follow the prior calls.
In addition, the message and transaction identifiers, starting with 0j/12, allow you to trace all the messages generated by the initial message.
For more details on Seneca logging, read the [logging tutorial][].
## Help
If you have questions on **priors**, you can:
* Tweet to [@senecajs][],
* [github issue][],
* Start a [conversation on gitter][conversation].
That's all folks! Corrections and comments: please tweet [@senecajs][].
[plugins]: http://senecajs.org/tutorials/how-to-write-a-plugin.html
[data entity patterns]: http://senecajs.org/tutorials/understanding-data-entities.html
[logging tutorial]: http://senecajs.org/tutorials/logging-with-seneca.html
[@senecajs]: https://twitter.com/senecajs
[github issue]: https://github.com/senecajs/seneca/issues
[conversation]: https://gitter.im/senecajs/seneca
---
## File: src/pages/docs/tutorials/understanding-query-syntax.md
---
layout: content.html
---
# Understanding Query Syntax
Each seneca store has a set of utility functions. One of them is `list$`, which is used to query for a very specific selection of data.
**Note:** An awesome npm module [lodash](https://www.npmjs.com/package/lodash) is used in examples here for neat display of results (`_` variable).
## Contents
- [Sample Entities](#wp-sample-entities)
- [List All](#wp-list-all)
- [Filter](#wp-filter)
- [AND Filter](#wp-and-filter)
- [Sorting](#wp-sorting)
- [Limit](#wp-limit)
- [Combine](#wp-combine)
## Sample Entities
For the purpose of this example, a small set of manual entries needs to be created(so that we have something to select from). This can be easily achieved using chaining. Note only one callback is needed - at the very end. Each `make$()` is needed to make sure instances are unique.
As with people, there may be many with the same name living in different locations.
``` js
var person = seneca.make$('person')
person
.make$().save$({name:'John', surname:'Smith', city:'Dublin', address:'Street 4'})
.make$().save$({name:'John', surname:'Smith', city:'Dublin', address:'Street 5'})
.make$().save$({name:'John', surname:'Smith', city:'Dublin', address:'Street 6'})
.make$().save$({name:'John', surname:'Smith', city:'Dublin', address:'Street 1'})
.make$().save$({name:'John', surname:'Smith', city:'Dublin', address:'Street 2'})
.make$().save$({name:'John', surname:'Smith', city:'Dublin', address:'Street 3'})
.make$().save$({name:'William', surname:'Smith', city:'Dublin', address:'Street 7'})
.make$().save$({name:'William', surname:'Smith', city:'Dublin', address:'Street 8'})
.make$().save$({name:'William', surname:'McDonald', city:'Dublin', address:'Street 9'},
function (err, res) {
if (err) console.error(err)
// all entities saved
})
```
Once we have our entities in our store, we can proceed to retrieving the data.
## List All
In order to get all entries from the store, we make query object empty.
``` js
person.list$({}, function (err, res) {
if (err) console.error(err)
_.each(res, function (entry) {
console.log(entry)
})
})
```
We can also remove the empty object altogether.
``` js
person.list$(function (err, res) {
if (err) console.error(err)
_.each(res, function (entry) {
console.log(entry)
})
})
```
Result:
```
$-/-/person;id=udl5h4;{name:John,surname:Smith,city:Dublin,address:Street 4}
$-/-/person;id=h0wxn8;{name:John,surname:Smith,city:Dublin,address:Street 5}
$-/-/person;id=szu6kd;{name:John,surname:Smith,city:Dublin,address:Street 6}
$-/-/person;id=wq0rz0;{name:John,surname:Smith,city:Dublin,address:Street 1}
$-/-/person;id=zl88s0;{name:John,surname:Smith,city:Dublin,address:Street 2}
$-/-/person;id=avzobw;{name:John,surname:Smith,city:Dublin,address:Street 3}
$-/-/person;id=ltdgwy;{name:William,surname:Smith,city:Dublin,address:Street 7}
$-/-/person;id=t4dmll;{name:William,surname:Smith,city:Dublin,address:Street 8}
$-/-/person;id=b2rodt;{name:William,surname:McDonald,city:Dublin,address:Street 9}
```
## Filter
In order to select a subset of entries, we add matching field to the query object.
``` js
list$({name:'William'}, function (err, res) {
if (err) console.error(err)
_.each(res, function (entry) {
console.log(entry)
})
})
```
Result:
```
$-/-/person;id=fvs3p6;{name:William,surname:Smith,city:Dublin,address:Street 7}
$-/-/person;id=lpdl0e;{name:William,surname:Smith,city:Dublin,address:Street 8}
$-/-/person;id=s8mh28;{name:William,surname:McDonald,city:Dublin,address:Street 9}
```
## AND Filter
In order to select a subset of entries, which comply with many constraints, we just simply add more matching fields to the query object.
``` js
list$({name:'William', surname:'McDonald'}, function (err, res) {
if (err) console.error(err)
_.each(res, function (entry) {
console.log(entry)
})
})
```
Result:
```
$-/-/person;id=r5g9fx;{name:William,surname:McDonald,city:Dublin,address:Street 9}
```
## Sorting
In order to sort in ascending or descending order, we add a `sort$` field containing an object containing a field name and sort direction.
**Ascending**
``` js
list$({sort$:{address:1}}, function (err, res) {
if (err) console.error(err)
_.each(res, function (entry) {
console.log(entry)
})
})
```
Result:
```
$-/-/person;id=wq0rz0;{name:John,surname:Smith,city:Dublin,address:Street 1}
$-/-/person;id=zl88s0;{name:John,surname:Smith,city:Dublin,address:Street 2}
$-/-/person;id=avzobw;{name:John,surname:Smith,city:Dublin,address:Street 3}
$-/-/person;id=udl5h4;{name:John,surname:Smith,city:Dublin,address:Street 4}
$-/-/person;id=h0wxn8;{name:John,surname:Smith,city:Dublin,address:Street 5}
$-/-/person;id=szu6kd;{name:John,surname:Smith,city:Dublin,address:Street 6}
$-/-/person;id=ltdgwy;{name:William,surname:Smith,city:Dublin,address:Street 7}
$-/-/person;id=t4dmll;{name:William,surname:Smith,city:Dublin,address:Street 8}
$-/-/person;id=b2rodt;{name:William,surname:McDonald,city:Dublin,address:Street 9}
```
**Descending**
``` js
list$({sort$:{address:-1}}, function (err, res) {
if (err) console.error(err)
_.each(res, function (entry) {
console.log(entry)
})
})
```
Result:
```
$-/-/person;id=sfbvsw;{name:William,surname:McDonald,city:Dublin,address:Street 9}
$-/-/person;id=0611oy;{name:William,surname:Smith,city:Dublin,address:Street 8}
$-/-/person;id=l843pa;{name:William,surname:Smith,city:Dublin,address:Street 7}
$-/-/person;id=fmsp67;{name:John,surname:Smith,city:Dublin,address:Street 6}
$-/-/person;id=8xfr93;{name:John,surname:Smith,city:Dublin,address:Street 5}
$-/-/person;id=ynnvj8;{name:John,surname:Smith,city:Dublin,address:Street 4}
$-/-/person;id=iakb54;{name:John,surname:Smith,city:Dublin,address:Street 3}
$-/-/person;id=avlabl;{name:John,surname:Smith,city:Dublin,address:Street 2}
$-/-/person;id=as6k3n;{name:John,surname:Smith,city:Dublin,address:Street 1}
```
## Limit
We can limit the amount of results by adding the `limit$` field to the query. It has a numerical value.
``` js
list$({limit$:4}, function (err, res) {
if (err) console.error(err)
_.each(res, function (entry) {
console.log(entry)
})
})
```
Result:
```
$-/-/person;id=u0ekqm;{name:John,surname:Smith,city:Dublin,address:Street 4}
$-/-/person;id=j3cm0w;{name:John,surname:Smith,city:Dublin,address:Street 5}
$-/-/person;id=2f9e24;{name:John,surname:Smith,city:Dublin,address:Street 6}
$-/-/person;id=4opp4t;{name:John,surname:Smith,city:Dublin,address:Street 1}
```
## Combine
A number of $ fields can be inserted into the query object to achieve desired output. For example, we could decide to sort in ascending order and limit results to first three.
``` js
list$({sort$:{address:1}, limit$:3}, function (err, res) {
if (err) console.error(err)
_.each(res, function (entry) {
console.log(entry)
})
})
```
Result:
```
$-/-/person;id=big0da;{name:John,surname:Smith,city:Dublin,address:Street 1}
$-/-/person;id=khg1i1;{name:John,surname:Smith,city:Dublin,address:Street 2}
$-/-/person;id=979zgb;{name:John,surname:Smith,city:Dublin,address:Street 3}
```
---
## File: src/pages/docs/index.md
---
layout: content.html
---
# Documentation
## Frequently Asked Questions
This is a good place to start when you hit something odd: [FAQ List](/faq).
## Tutorials
Seneca tutorials offer a deep dive into a particular concept or feature. We also have a number
of sample projects, small and large that demonstrate how to use Seneca in a number of different
ways and with different complimentary technologies.
- [Logging with Seneca][Tutorial01]
- [How to write a plugin][Tutorial02]
- [How to write unit tests][unit-testing]
- [Understanding data entities][Tutorial03]
- [Understanding query syntax][Tutorial04]
- [Understanding prior actions][Tutorial05]
- [Seneca with promises][Tutorial06]
## Sample projects
For convenience, we have a number of sample apps in a single repository. This repository
covers single concepts as well as popular plugins. See the repo at [Seneca examples][Sample01].
### NodeZoo
NodeZoo is a search engine for Node.js modules. The complete system is an example of a real-
world service built using Node.js microservices. Each microservice is published in its
own GitHub repository. The code base is intended to be used as a larger-scale example and as a
starting point for your own projects. See the system at [NodeZoo][Sample02].
## Articles
- [Seneca 1.0.0: A microservices framework for Node.js][Article01]
- [Monolithic Node.js][Article02]
## Talks
Here are some talks about Seneca and Microservices:
- [We are not Object-Oriented anymore][Talk01] - _Matteo Collina (FullStack London 2015)_
- [Measuring Microservices][Talk02] - _Richard Rodger (microxchg.io Berlin 2015)_
- [Microservices][Talk03] - _Richard Rodger (NodeConf EU 2014)_
## Books
These books help you understand how to design, build and deploy microservice systems. Some
of them cover Seneca directly, and others provide more general guidance. The ideas in the
books listed here can be applied using Seneca without restriction. They all contain useful
insights, and more importantly, real-world lessons.
### The Tao of Microservices
The Tao of Microservices teaches you the path to understanding how to apply microservices
architecture with your own real-world projects. This high-level book offers you a conceptual
view of microservice architectures, along with core concepts and their application. You'll
also find a detailed case study for the nodezoo.com system, including all source code and
documentation. By the end of the book, you'll have explored in depth the key ideas of the
microservice architecture and will be able to design, analyze and implement systems based
on this architecture.
[The Tao of Microservices][Book01]
### Building Microservices
Distributed systems have become more fine-grained in the past 10 years, shifting from code-
heavy monolithic applications to smaller, self-contained microservices. But developing these
systems brings its own set of headaches. With lots of examples and practical advice, this book
takes a holistic view of the topics that system architects and administrators must consider
when building, managing, and evolving microservice architectures.
[Building Microservices][Book02]
### Antifragile Software
We've spent over a decade now becoming more and more agile and adaptable in our ways of
working. Unfortunately our software is now struggling to keep up with the pace of innovation
that is increasingly being demanded by modern businesses. It's time to sort that out. It's
time for Antifragile Software with Microservices.
[Antifragile Software][Book03]
### Microservices: Flexible Software Architectures
A Microservice-based architecture divides software systems into many small services which can
be deployed independently. Every team works on its own Microservices and is thus decoupled
from other teams. This allows to easily scale agile processes. The modularization into
Microservices protects the system against architecture decay. Consequently, systems based on
Microservices stay maintainable in the long term. In addition, legacy systems can be migrated
to Microservices without having to change the legacy code. Moreover, Continuous Delivery is
easier to implement in Microservice-based systems.
[Microservices: Flexible Software Architectures][Book04]
### Microservices in .NET
Microservices in .NET shows you how to build and deploy secure and operations-friendly
microservices using Nancy. The book takes you through an introduction to the microservices
architectural style. Next, you'll learn important practical aspects of developing microservices
from simple core concepts to more sophisticated. Throughout the book, you'll see many code
examples implementing it with lightweight .NET technologies - most prominently Nancy. By the
end, you'll be able to quickly and easily build reliable and operations-friendly microservices
using Nancy, OWIN and other open technologies.
[Microservices in .NET][Book05]
### Microservices in GO
GO is a great language for building microservices. However there are a lot of challenges to
navigate. How do you do caching, manage databases. Monitor and analyze performance. Integrate
with Docker. Do continuous deployments. Run on private or public clouds.
[Microservices in GO][Book06]
### SOA Patterns
SOA Patterns provides architectural guidance through patterns and anti-patterns. It shows you
how to build real SOA services that feature flexibility, availability, and scalability.
Through an extensive set of patterns, this book identifies the major SOA pressure points and
provides reusable techniques to address them. Each pattern pairs the classic problem/solution
format with a unique technology map, showing where specific solutions fit into the general
pattern.
[SOA Patterns](https://www.manning.com/books/soa-patterns)
[Tutorial01]: /docs/tutorials/logging-with-seneca.html
[Tutorial02]: /docs/tutorials/how-to-write-a-plugin.html
[Tutorial03]: /docs/tutorials/understanding-data-entities.html
[Tutorial04]: /docs/tutorials/understanding-query-syntax.html
[Tutorial05]: /docs/tutorials/understanding-prior-actions.html
[Tutorial06]: /docs/tutorials/seneca-with-promises.html
[unit-testing]: /docs/tutorials/unit-testing.html
[Sample01]: https://github.com/rjrodger/seneca-examples
[Sample02]: https://github.com/nodezoo/nodezoo-system
[Article01]: http://www.richardrodger.com/seneca-microservices-nodejs#.VqjAZRiLT-k
[Article02]: http://www.richardrodger.com/monolithic-nodejs#.VqjAixiLT-k
[Talk01]: https://skillsmatter.com/skillscasts/6819-we-are-not-object-oriented-anymore-or-why-the-node-callback-style-is-awesome
[Talk02]: http://www.infoq.com/presentations/measuring-microservices
[Talk03]: https://www.youtube.com/watch?v=fVfWuked2qE
[Book01]: https://manning.com/books/the-tao-of-microservices?a_aid=tms&a_bid=3b7806c8
[Book02]: http://shop.oreilly.com/product/0636920033158.do
[Book03]: https://leanpub.com/antifragilesoftware
[Book04]: http://microservices-book.com/
[Book05]: https://www.manning.com/books/microservices-in-net
[Book06]: http://microservicesingo.com