This framework is the simple JS implementation of CORE design pattern (acronym: Contexts, Objects, Requests and Events)
This design pattern and the framework provide you ability to easily design and implement well structured, high cohesioned, and low coupled modular systems using Events and Requests. The benefit of that approach is a semantic which is highly close to Business Logic, which allows to write less code doing more, and have less bugs and debugging with it.
The ideas in the base of it are similar to following approaches, and it collects the best benefits of them:
- automata-based programming
- actor model
- system of signals and slots
- event-oriented programming
Event is a complex object, that means that something has already happend.
Request is a complex object, that means that something asks to perform its request.
Other objects of the system can subscribe to Events and Requests. Subscription is a static process (which is different to usual obj.fire('event') and obj.on('event) dynamic-style subscriptions).
During initialization CORE framework parses the code of methods and subscribes objects on Events and Requests statically.
Note that 99% of real-world cases don't need a dynamic behaviour, which is a good base to simplify this process
Static approach also offers you an ability to build a map how objects/methods are connected, and analyse it, which can extend your Static Code Analysis Tool benefits of your project.
High-level description: https://medium.com/@i_am_os/core-design-pattern-the-way-out-from-overly-complicated-code-b8804449941
How to help the project: https://medium.com/ux-of-programming-languages/lets-build-a-community-around-core-701eb8ebb02c
There are three steps for using Events: initialization, firing, catching.
You can pass some data with Event.
To initialize Event object call Core.registerEventPoint
with Event name. Event name consists of Object name that has this Event and action name.
Core.registerEventPoint('Player_Started');
To fire Event call FireEvent
function with created Event.
var Player = {
mediaTag: document.getElementById('audio')
, start: function() {
this.mediaTag.play();
FireEvent(new Player_Started({data: 'some-data'}));
}
}
The main twist is that you can catch the fired Event at any spaces of your code.
So this can cut your code several times.
Also you can dinamically subscribe to the event. It is useful in different cases, for example, in angular directives.
var GoogleTrackingObject = {
sendPlayerEvent: function() {
var event = CatchEvent(Player_Started);
/* event.data === 'some-data' //true */
ga('send', 'event', 'player', 'start');
}
}
var GoogleTrackingObject = {
sendPlayerEvents: function() {
var event = CatchEvent(Player_Started, Player_Paused);
ga('send', 'event', 'player', 'player_event', event.type);
}
}
There are three steps for using them: initialization, firing, catching. You can pass some data with the Request.
Just create Request object.
Core.registerRequestPoint('PlayerUI_StartRequest')
Fire it and ask something to perform your request.
var PlayerUi = {
startPlaying: function() {
FireRequest(
new PlayerUI_StartRequest({data: 'some-data'})
, function() {} // success callback
, function() {} // error callback
, {} // context
)
}
}
Catch the Request and perform it.
var PlayerAudio = {
startPlaying: function() {
var request = CatchRequest(PlayerUI_StartRequest);
/* request.data === 'some-data' //true */
return function(cb, eb) {
/* start playing audio player logic */
cb();
}
}
}
There can be several objects that can resolve Requests. When one of them can't process Request it call error callback function and next object start processing.
var PlayerAudio = {
mediaTag: null
, startPlaying: function() {
CatchRequest(PlayerUI_StartRequest);
return function(cb, eb) {
if( !PlayerAudio.mediaTag ) {
return eb();
}
/* start playing audio player logic */
cb();
}
}
}
var Player = {
start: function() {
CatchRequest(PlayerUI_StartRequest);
return function(cb, eb) {
/* start playing audio player logic */
cb();
}
}
}
It's an example how we can implement more complicated object's behaviour and use it in CORE-style.
Core.state(state1, state2, ...)
(String) '' // name of the state
(Object) {
value: (String) // current state value
, go : (Function) // method to change state
}
var Object = {
mainState: Core.state('Idle', 'Running', 'Stopped')
}
When the object has been inited, its state goes to the first value of the set.
Object.mainState.go('Running');
When state has been changed, the Event Object.mainState.GoRunning
fires. And it can be catched at any space of the application.
var MiddleObject = {
getState: function() {
Core.CatchEvent(Object.mainState.GoRunning, Object.mainState.GoStopped);
if( Object.mainState.value === 'Running' ) {
// your code here
}
}
}
#Examples
Examples can be found in /examples directory. There is index.html file for each example. Just open it in any browser to run an example.
##Plane
Location: /examples/requests/plane
Let's suppose we have 2 objects - plane and dispatcher. Plane is going to start and waiting for permission. A pilot sends request to dispatcher and asks him if he can start. So, the pilot is fire a request for launch. Dispatcher can allow or reject it. Plane starts as soon as it gets the request.
##Restaurant
Location: /examples/requests/restaurant
There is a Japanese restaurant with plates that moving in a circle on a table near the visitors. A cook puts the plate on the table when it's ready. There are few types of plates and users can choose and take plate if they like it. If one visitor take a plate, next person can't take it. He have to wait for a new one. If plate make a circle and no one take it the cook give that plate to a cat.
##Traffic Light
Location: /examples/states/trafficLight
There are 2 objects - car and traffic light. Car waiting for green light and start (or do other actions on other lights). Traffic light has 3 states - red, yellow and green. Every time it change color it sends a request.
#Unit tests
We using Mocha with NodeJs for unit tests. Specs can be found in /_tests directory. To run tests:
npm update
npm test