leopold
Version:
Event-sourced state support
225 lines (212 loc) • 7.86 kB
HTML
<html>
<head>
<meta name="viewport" content="width=device-width" charset="utf-8">
<title>leopold</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/cayman.min.css">
<link rel="stylesheet" href="css/prism.min.css">
<link rel="stylesheet" href="css/index.min.css">
<link rel="stylesheet" href="css/docs.min.css">
<link rel="stylesheet" href="css/bootstrap-responsive.min.css">
</head>
<body data-spy="scroll" data-target=".scrollspy">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container"><a class="brand">Mr. Doc</a>
<div class="nav-collapse collapse">
<ul class="nav pull-right sponsored"></ul>
</div>
</div>
</div>
</div>
<header id="overview" class="jumbotron subhead">
<div class="container">
<h1>leopold</h1>
<p class="lead"></p>
</div>
</header>
<div class="container">
<div class="row">
<div class="span3 bs-docs-sidebar">
<ul class="nav nav-list bs-docs-sidenav affix-top">
<li class="active"><a href="index.html">Main</a></li>
<li><a href="leopold.js.html">leopold.js</a></li>
</ul>
<div class="scrollspy">
<ul class="nav nav-list bs-docs-sidenav affix-top">
</ul>
</div>
</div>
<div class="span9">
<section id="Main" class="readme"><h1>leopold</h1>
<pre><code> __^___
[(0¿0)]
~-
</code></pre>
<h2>Event-sourced models for nodejs or browser</h2>
<p><a href="https://github.com/mnichols/leopold/stargazers"><img src="https://img.shields.io/github/stars/mnichols/leopold.svg" alt="GitHub stars"></a>
<a href="https://travis-ci.org/mnichols/leopold"><img src="https://travis-ci.org/mnichols/leopold.svg?branch=master" alt="Build Status"></a></p>
<h3>Install</h3>
<p><code>npm install --save leopold</code></p>
<h3>Quick Start</h3>
<h4>Eventable models</h4>
<pre><code class="language-js">
import leopold from 'leopold'
import stampit from 'stampit'
// create a leo that includes a unit of work and factory for event providers
const leo = leopold()
const accountSpec = stampit()
.methods({
// by convention, event handlers are named '$' + '${event.name}'
// async handlers are supported
$initialized: function(e) {
this.id(e.id)
this.name = e.name
this.balance = e.balance
}
, $deposited: function(e) {
this.balance = e.balance
}
, initialize: function(name) {
//returns a promise
return this.raise({
event: 'initialized'
, id: 'checking'
, balance: 0
, name: name
})
}
, deposit: function(amount) {
return this.raise({
event: 'deposited'
, balance: (this.balance + amount)
})
}
})
.compose(leo.eventable())
const customerSpec = stampit()
.methods({
$initialized: function(e) {
this.id(e.id)
this.name = e.name
this.accounts = {}
}
, $accountApproved: function(e) {
//use special _id attribute to initialize new account
return account()
.initialize(e.accountId, e.accountName)
.bind(this)
.then(function(newAccount) {
this.accounts[e.accountName] = newAccount
})
}
, initialize: function(name) {
return this.raise({
event: 'initialized'
, name: name
, id: 42
})
}
, approveAccount: function(accountName) {
let acctId = cuid()
return this.raise({
event: 'accountApproved'
, accountName: accountName
, accountId: acctId
})
.bind(this)
.then(function(){
return this.accounts[acctId].initialize()
})
}
, makeDeposit: function(account, amount) {
return this.accounts[account].deposit(amount)
}
})
//use
let customer = customerSpec.create()
customer.approveAccount('checking') // -> customer.accounts['checking']
customer.makeDeposit('checking',300.42) // -> account.balance === 300.42
</code></pre>
<p>Notice we have a object graph that is two deep.</p>
<h4>Now let's consume a set of events to restore state to where this was</h4>
<pre><code class="language-js">
let instance = customerSpec.create({_id: 1})
let events = [
{ event: 'initialized', id: 1, name: 'mike' }
, { event: 'accountApproved', id: 1, name: 'checking' }
, { event: 'initialized', id: 'checking', name: 'checking', balance: 0 }
, { event: 'deposited', id: 'checking', balance: 300.42}
]
let envelope = {
revision: 1
, events: events
}
// events are stored as envelopes
return leo.mount(envelope)
.then(function(){
return leo.restore(instance, 0 , Number.MAX_VALUE)
})
// instance.accounts[{cuid}].balance === 300.42
</code></pre>
<h3>Taking advantage of events for testing side effects</h3>
<pre><code class="language-js">
import leopold from 'leopold'
import stampit from 'stampit'
// create a leo that includes a unit of work and factory for event providers
const envelopes = []
const storage = {
store(env) { envelopes.push(env )}
}
const leo = leopold({
//this commits events right away
atomic: false
, storage
})
const myModel = stampit()
.methods({
// by convention, event handlers are named '$' + '${event.name}'
// async handlers are supported
$initialized: function(e) {
//do stuff
}
, initialize: function() {
return this.raise({ event: ‘initialized’})
}
})
.compose(leo.eventable())
myModel.initialize()
envelopes.length === 1 // true
envelopes[0].events[0].event === ‘initialized’ // true
</code></pre>
<h3>Dependencies</h3>
<p><code>leopold</code> uses <a href="https://github.com/stampit-org/stampit">stampit</a> under the hood
and the <code>eventable</code> call presumes you are composing event source behavior
into an prototype ('spec').</p>
<p><code>leopold</code> is also using some ES6 features that require <a href="http://babeljs.io/">babel</a>.</p>
<h3>Running tests</h3>
<p><code>make test</code> (nodejs)
<code>make browser</code> (browser) then visit on any browser at <code>http://localhost:2222</code></p>
</section>
</div>
</div>
</div>
<footer class="footer">
<div class="container">
<p>Documentation generated with <a href="https://github.com/mr-doc/mr-doc">Mr. Doc </a> created by <a href="https://twitter.com/FGRibreau" data-show-count="false" class="twitter-follow-button">Francois-Guillaume Ribreau </a></p>
<p>Mr. Doc is sponsored by <a href="http://bringr.net/?btt" title="Outil d'analyse des réseaux sociaux" class="bringr">Bringr </a> and <a href="https://redsmin.com/?btt" title="Full Redis GUI" class="redsmin">Redsmin</a></p>
<p>Theme borrowed from Twitter Bootstrap</p>
</div>
</footer>
<script src="js/twitter-widget.min.js"></script>
<script src="js/jquery.min.js"></script>
<script src="js/bootstrap-transition.min.js"></script>
<script src="js/bootstrap-scrollspy.min.js"></script>
<script src="js/bootstrap-dropdown.min.js"></script>
<script src="js/bootstrap-collapse.min.js"></script>
<script src="js/bootstrap-affix.min.js"></script>
<script src="js/prism.min.js"></script>
<script src="js/index.min.js"></script>
</body>
</html>