Cerebral
A state controller with its own debugger
What is Cerebral?
To get a more complete introduction, watch this video on Cerebral. But to give you a quick overview, imagine your application in three parts. Your VIEW layer, your MODEL layer and smack in the middle, the CONTROLLER layer. The VIEW layer has historically had very few changes to its concept, though technically they have become a lot more effective.
If you are familiar with a Backbone View with a template or an Angular Controller/Directive with a template, that is pretty much how a VIEW works. The more recent React js VIEW (component) library is much the same concept in regards of being responsible for rendering HTML based on STATE inside the VIEW, but it does it in a radically different way that is a lot faster.
The traditional MODEL layer of your app, like Backbone Model or Angular Resource, are wrappers for your database entities. To make it easier to communicate changes back and forth to the server. This has changed radically the last year. Instead of thinking the MODEL layer as wrapped objects that allows for easier communication, it is now just one big plain object containing any data/state your application needs, it being a database entity or just some state indicating that your application is loading, show a modal etc.
The CONTROLLER layer is the most loosely defined concept on the frontend. On the backend the CONTROLLER in MVC starts with your router. It is what receives a request from the client and moves it through middleware which often communicates with the MODEL layer to create a response. Since we also have a concept of a router on the frontend I believe it has been a bit confusing how all of this should fit together.
With Cerebral the CONTROLLER layer of your application has nothing to do with the router. Routing is just state changes, like anything else. Instead the CONTROLLER is responsible to move a request from the UI, called a signal, through middlewares, called actions, to make changes to the state of the application. When a signal is done running it can respond to the UI that it needs to update, most commonly with an even, passing the new state of the application.
What makes Cerebral so special is the way it tracks signals and state mutations. It does not matter what you VIEW layer or MODEL layer is, you hook them on to the CONTROLLER on each side and off you go. The Chrome Debugger will help you analyze and control the state flow as you develop the app. This is a very powerful concept that makes it very easy to scale, reuse code and reduce development time.
Grab the Chrome debugger
How to get started
1. Install debugger
Install the Chrome Cerebral Debugger
2. Choose a package
The Cerebral Core API is "low level", but extremely flexible. If you do not have any specific needs in regards of VIEW or MODEL layer, you can choose one of the preset packages that will get you quickly up and running:
3. Signals and actions
Depending on the package you choose you instantiate and create signals differently. Please continue with the README of the specific package you chose
How to create a custom Cerebral package
If the current packages does not meet your needs you are free to create your own package with its own VIEW and MODEL layer. To define a Controller you need somewhere to store the state. You can use whatever you want in this regard, but to gain the full power of the developer tools the state store should be immutable. This specifically allows you to move back and forth in time in the debugger and you will gain benefits in rendering optimization.
In this example we will use the immutable-store project as a state store, but freezer, baobab, immutable-js are also good alternatives.
index.js
var Cerebral = require('cerebral');
var Store = require('immutable-store');
var EventEmitter = require('events').EventEmitter;
var Controller = Cerebral.Controller;
var Value = Cerebral.Value;
module.exports = function (state, defaultArgs) {
var initialState = Store(state);
var events = new EventEmitter();
state = initialState;
var controller = Controller({
onReset: function () {
state = initialState;
},
onUpdate: function () {
events.emit('change', state);
},
onRemember: function () {
events.emit('change', state);
},
onSeek: function (seek, isPlaying, recording) {
state = state.import(recording.initialState);
events.emit('change', state);
},
onGet: function (path) {
return Value(path, state);
},
onSet: function (path, value) {
var key = path.pop();
state = Value(path, state).set(key, value);
},
onUnset: function (path, key) {
state = Value(path, state).unset(key);
},
onPush: function (path, value) {
state = Value(path, state).push(value);
},
onSplice: function () {
var args = [].slice.call(arguments);
var value = Value(args.shift(), state);
state = value.splice.apply(value, args);
},
onMerge: function (path, value) {
state = Value(path, state).merge(value);
}
});
controller.events = events;
return controller;
};
Demos
TodoMVC: www.christianalfoni.com/todomvc
Cerebral - The beginning
Read this article introducing Cerebral: Cerebral developer preview
Contributors
- Discussions and code contributions - Marc
- Logo and illustrations - Petter Stenberg Hansen
- Article review - Jesse Wood
Thanks guys!