Tuesday, April 8, 2014

High Level Application Routing with StarLogic, StarMap, and StarShine - An idea in the making

I'm usually against using frameworks, but a few days ago I realized I could actually design a framework on top of StarShine. This framework was a fun idea I came up with while discussing top level routing of an application.

I wanted to make a Superset of the MVC framework, that focuses purely on the Business Logic part of your application, hence the name StarLogic.

This pattern models an event listener pattern, but does it in a functionally reactive way. Take the following example:

var StarLogic = require('StarLogic');
//create a router
var app = StarLogic();
function _newId(bo/*Business Object*/) {
bo.id = new Guid();
}
//if id is falsy, give it a new one
app.addRule({ id: false }, _newId);
//returns a finished business object
app.route({});
//{id: [object Guid]}

The idea with this framework, is to define a bunch of simple rules, and have your business logic execute your functions based on the rules. In the above case, any object that passes through the app.route() function will receive an ID if it isn't set already.

High level application routing can occur when you push a route object to a router like this:

var StarLogic = require('StarLogic');
//create a router
var app = StarLogic();
//fake database module you made
var DB = require('./DB');
function _getUser(route) {
//this is a shared object namespace
this.user = DB.getUser(route.id);
}
function _routeApplication() {
//do some application stuff here like change the view
//do some databinding
this.user;
//is the returned user
}
app.addRule({ controller: "user", id: true, action: true }, _getUser, _routeApplication);

Any object pushed through app will now be checked to see if every rule definition provided is correct, then it will execute _getUser and _routeApplication passing the business object in question to the functions.

Hey wait a minute, what about complex routing and nested routers? Easy. The route function is portable and doesn't rely on this being anything. Here are some examples of where the route function is placed in a proper way.

var eventApp = StarLogic();
var route = eventApp.route;
//every event object will be passed to eventApp
document.getElementById('myElement').keydown = route;
function _42KeyDown (evt) {
evt.preventDefault();
}
//this is a bad idea, but you get the idea
eventApp.addRule({which: 42}, _42KeyDown);
var subRouter = StarLogic();
//some random rule
eventApp.addRule({ bubbles: true }, subRouter.route);
//Now any keydown event that bubbles will be passed to subRouter for execution
//add a rule
subRouter.addRule({ 1:true });
//create a list of things
var items = [];
items.push({1:1}, {2:2}, {3:3});
//push them to a router
items.forEach(subRouter.route);
//now each item will be processed by your defered logic
view raw functional.js hosted with ❤ by GitHub

Essentially you get to design data processes, define application level logic, and design a framework for your application on top of StarLogic. Move over angular, StarLogic is coming to a store near you.

Wednesday, April 2, 2014

StarShine - 0.1.0 API Finalized, an App Architecture Tutorial, and Factory Inheritence



Here is the presentation on how to get started on using StarShine.

I'm going to demonstrate now how to actually make an app structured on top of StarShine.

function _eventify() {
var handlers = {};
this.on = function(eventName, handler) {
(handlers[eventName] = handlers[eventName] || [])
// add the handler to the array
.push(handler);
return this;
}
this.once = function(eventName, handler) {
function wrappedHandler() {
handler.apply(this.off(eventName, wrappedHandler), arguments);
}
wrappedHandler.h = handler;
return this.on(eventName, wrappedHandler);
}
this.off = function(eventName, handler) {
for (var list = handlers[eventName], i = 0; handler && list && list[i]; i++) {
list[i] != handler && list[i].h != handler ||
list.splice(i--,1);
}
if (!i) {
delete handlers[eventName];
}
return this;
}
this.emit = function(eventName) {
for(var list = handlers[eventName], i = 0; list && list[i];) {
list[i++].apply(this, list.slice.call(arguments, 1));
}
return this;
}
}
function _exposeThis() {
return this;
}
view raw eventify.js hosted with ❤ by GitHub

This _eventify method will turn the private namespace of your module into an event emitter. This is useful for many things, but mostly it's a building block for an event emitter Factory. In order to expose the event factory, we simply return this; to expose the methods to the object. Finally, we wrap it in a StarShine factory. Here are some rules to remember.
  1. If the parts you create should remain private, create a closure.
  2. If the parts you create should only be exposed and the rest of your closures don't rely on it, create a compound factory
  3. If the parts you create should be BOTH, create a factory and create an instance in your closure
In this case, I want the event emitter to be public AND private. Option three is our target.

var EventEmitter = StarShine(_eventify, _exposeThis);
function _setupEventEmitter(StarShine, window, document) {
//this turns the private namespace into an event emitter...
return StarShine.mixIn(this, EventEmitter());
//and exposes it to the API we are building
}
//Setup the app
var myApp = StarShine(_setupEventEmitter);
//Execute your code
var API = myApp(StarShine, window, document);
//now API has an event emitter
view raw app.js hosted with ❤ by GitHub

Of course, if you wanted events to be private (and therefore not exposed,) simply include the original function that created the event emitter in the first place. You don't even need the _exposeThis function.

var APP = StarShine(_eventify);
APP();

Finally, if I want the methods to simply be exposed without referenced internally, I can do factory re-use.

var APP = StarShine(EventEmitter, _anotherClosure);
var API = APP();
view raw Option2.js hosted with ❤ by GitHub

Moving on, we should create a couple of closures that define different portions of our application. I'm going to look for anything with a [data-user-control-1] attribute on my dom and put a link inside of it.

function _renderAllUserControl1(document) {
//app is an event emitter
var map = [].map, app = this;
function _createFragment (documentElement) {
this.container = document.createDocumentFragment();
documentElement.appendChild(this.container);
}
function _doSomethingWithContainer() {
//create a bunch of elements
var a = this.a = document.createElement("a");
a.href="#goSomeWhere";
a.innerHtml = "this is a test";
this.container.appendChild(a);
}
//remember, you're going to be passed the index from the .map function
function _addAnEventListener(documentElement, index) {
this.a.onclick = function(){
//UserControls will be defined in this context, see below
app.trigger("UserControlClick", UserControls[index]);
};
}
function _buildTheApiForMyControl() {
var private = this;
return {
//these methods will be attached to the UserControls list items
"remove": function() {
private.container.removeChild(private.a);
}
};
}
var controlContainers = document.querySelectorAll("[data-user-control-1]");
var ControlFactory = StarShine(_createUserControl1, _doSomethingWithContainer, _addEventListener, _buildTheApiForMyControl);
//index and create a user control for each controlContainer
var UserControls = map.call(controlContainers, ControlFactory);
APP.on("UserControlClick", function(UserControl) {
//this method was returned via _buildTheApiForMyControl
UserControl.remove();
});
return {
//this will be exposed to your global API
};
}
var APPFactory = StarShine(_eventEmitter, _exposeThis, _renderAllUserControl1);
var myApp = APPFactory(document);

This is a lot of code to read, so take your time and re-read it.

On the highest level, we define a process. Inside this process, we define a factory for creating a dom fragment and inject it into the container AND the private namespace for later use inside of the _createFragment function.

Then we create an anchor tag using the terrible dom api (encapsulated and transparent to the API consumer!) and append it to the container defined already.

Then we add a click listener to the anchor in a separate process that triggers a global APP event. The this in the parent closure is a reference to the event emitter exposed by our app.

We define an API for the control that can be consumed by the app and anyone who has access to see the UserControl. I put a remove function into the definition of a "UserControl1" as an example.

Finally, I add an event listener to the app to listen for UserControlClick events and remove the anchor from the DOM.

If you define your Application in terms of functions, suddenly everything is modularized and is much easier to maintain. Embrace the "encapsulate and forget about it" principle because you are making building blocks and tools.

The following example is a good place to start for your app.
StarShine(function(window, document, StarShine){
'use strict';
//Immediately Invoked Inline Function
//Define your application processes
function _ApplicationProcess(window, document, StarShine){
var private = this;//share internal variables between other application processes
//do some code here
//APP will be defined here after the whole factory executes
}
//execute everything once
var APP = StarShine(_ApplicationProcess)(window, document, StarShine);
})(window, document, StarShine)
view raw APP.js hosted with ❤ by GitHub


Experiment and make the code work for you.

Now, onto factory inheritence (as promised!)
//if your factories return an api
//and they don't need to share their private state between them
var myCompoundFactory
= StarShine(Factory1, Factory2, Factory3)
.proto(Factory1.proto(), Factory2.proto(), Factory3.proto());
//Just remember that the parameters need to match up for both factories or you may have a problem


If you must accept different parameters, or some kind of state needs to be shared between the factories, maybe it would be best to make the combined factories actual private children of the parent factory. Each factory should be a unit or an encapsulated building block designed to operate all by itself.