famous-mediator
Advanced tools
Comparing version 0.1.1 to 0.2.0
149
index.js
define(function(require, exports, module) { | ||
/* | ||
The Famo.us Engine is used as a global eventbus to announce creation of modules. | ||
var Engine = require('famous/core/Engine'); | ||
If you want to register a module, simple call in your constructor function: | ||
// Create Mediator object | ||
var Mediator = {}; | ||
// Callback list for Mediator.ready([a,b,c],function(a,b,c){ ... }) | ||
var CallbackList = []; | ||
```javascript | ||
Engine.emit('created',this) | ||
```` | ||
// cache for all modules | ||
var Modules = window.Modules = {}; | ||
A module MUST have a name to be registered. This name can be found on: | ||
/* | ||
For each module you want to mediate, emit a 'created' event on the Engine. | ||
- this.options.id = 'xxxx' | ||
- this.options.name = 'xxxx' | ||
- this.id = 'xxxx' | ||
- this.name = 'xxxx' | ||
Example: Router.js | ||
The module with the name `xxxx` is accessible as follows: | ||
``` | ||
var Mediator = require('famous-mediator'); | ||
Mediator.xxxx | ||
Mediator.on('created:xxxx',function(xxxx){ .... }); | ||
``` | ||
The idea is to write Mediator singletons that couple modules together by: | ||
- listening to the events they emit | ||
- calling the public API | ||
- triggering events on a module | ||
For example, imagine you have a `Router` module and a `pages` module: | ||
``` | ||
var Mediator = require('famous-mediator'); | ||
function Router(){ | ||
.... | ||
Engine.emit('created',this); | ||
} | ||
Mediator.on('created:Router',function(router){ | ||
router.on('change',function(name){ | ||
Mediator.pages.set(name); | ||
}); | ||
}); | ||
``` | ||
*/ | ||
var Engine = require('famous/core/Engine'); | ||
// cache for all modules that are created | ||
var Mediator = window.Mediator = {}; | ||
Mediator.on = Engine.on; | ||
// when a module is created... | ||
Mediator.on('created',function(module){ | ||
*/ | ||
Engine.on('created',function(module){ | ||
// try to find a name | ||
// Try to find a name | ||
var name; | ||
@@ -63,10 +34,88 @@ if(module.options) { | ||
// when name is found | ||
// ready name is found | ||
if(name) { | ||
Mediator[name] = module; // store module for later reference | ||
Engine.emit('created:'+name,module); // broadcast module creation | ||
// store module | ||
Modules[name] = module; | ||
// Check callbacks that are waiting | ||
CallbackList.forEach(function(item,callbackIndex){ | ||
// it this callback waiting for current module? | ||
var moduleIndex = item.waitFor.indexOf(name); | ||
// if so... | ||
if(moduleIndex > -1){ | ||
// ...remove module from 'wait' list | ||
item.waitFor.splice(moduleIndex,1); | ||
// Are we finished loading all modules? | ||
if(item.waitFor.length === 0){ | ||
// map names to actual modules | ||
var modules = item.modules.map(function(name){ return Modules[name]; }); | ||
// invoke callback | ||
item.callback.apply(null,modules); | ||
// remove callback from list | ||
CallbackList.splice(callbackIndex,1); | ||
} | ||
} | ||
}); | ||
} | ||
}); | ||
/* | ||
Mediator.ready(modules,callback) | ||
Waits for until `modules` to exist before executing callback. | ||
Use this to couple modules together in your custom Mediators. | ||
Example: RouterMediator.js | ||
Mediator.ready(['Router','PageController'],function(router,page) { | ||
router.on('change',page.update) | ||
}) | ||
*/ | ||
Mediator.ready = function ready(modules,callback){ | ||
var waitFor = modules.filter(function(name){ return !Modules[name]; }); | ||
// All modules are already loaded! Invoke immediatly | ||
if(waitFor.length === 0){ | ||
modules = modules.map(function(name){ return Modules[name]; }); | ||
callback.apply(null,modules); | ||
// Waiting for modules. Invoke later. | ||
} else { | ||
CallbackList.push({ | ||
waitFor:waitFor, | ||
modules:modules, | ||
callback:callback | ||
}); | ||
} | ||
}; | ||
/* | ||
Mediator.on | ||
Mediator.on('navigate',['Router'],function(event,router){ .... }) | ||
Use for global events, i.e. events whose information is insensitive to context. | ||
Example: A 'navigate' or 'tweet' event doesn't care about its sender or context | ||
Counter example: A 'click' event might mean different things depending on its context (surface). | ||
*/ | ||
Mediator.on = function(eventName,modules,callback) { | ||
if(typeof modules === 'function') { | ||
callback = modules; | ||
modules = []; | ||
} | ||
Engine.on(eventName,function(data){ | ||
Mediator.ready(modules,function(){ | ||
var args = Array.prototype.slice.call(arguments,0); | ||
args.unshift(data); | ||
callback.apply(null,args); | ||
}); | ||
}); | ||
}; | ||
// Alias! | ||
Mediator.emit = Engine.emit; | ||
module.exports = Mediator; | ||
}); |
{ | ||
"name": "famous-mediator", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "Utilities for a mediator design pattern in Famo.us", | ||
@@ -18,3 +18,5 @@ "main": "index.js", | ||
], | ||
"author": "Mark Marijnissen", | ||
"author": { | ||
"name": "Mark Marijnissen" | ||
}, | ||
"license": "MIT", | ||
@@ -24,3 +26,9 @@ "bugs": { | ||
}, | ||
"homepage": "https://github.com/markmarijnissen/famous-mediator" | ||
"homepage": "https://github.com/markmarijnissen/famous-mediator", | ||
"readme": "famous-mediator\n===============\n\nA design pattern that decouples modules using the mediator pattern.\n\n## Getting started\n\nInstall using bower or npm\n\n```bash\n bower install famous-mediator\n npm install famous-mediator\n```\n## Example\n\nImagine you have a `Router` module and a `pages` module:\n\n*1. Router.js - does it's thing and doesn't know about other modules*\n```javascript\n function Router(){\n // blablabla\n this.name = 'router';\n Engine.emit('created',this) // broadcast created event\n // blablabla\n }\n```\n\n*2. PageController.js - displays pages but doesn't know about any routes!*\n```javascript\n function PageController(){\n // blablabla\n this.name = 'pages'; // name your module\n Engine.emit('created',this) // broadcast created event\n // blablabla\n }\n```\n\n*3. RouteMediator.js - couples the router and pages together!*\n```javascript\n var Mediator = require('famous-mediator');;\n\n Mediator.on('created:Router',function(router){\n router.on('change',function(name){\n Mediator.pages.setPage(name);\n });\n });\n```\n\n## Benefits\n\nUsing the mediator pattern to decouple modules\n\n* Promotes clean, reusable modules\n* Clear seperation of concerns - no more sneaky business logic in your templates!\n* Is the perfect place for hacks, experiments, monkey-patches and workarounds. And because all of your \"bad\" code is in the Mediators, your actual code base stays clean! (True story!)\n\n## Usage\n\nThe Famo.us Engine is used as a global eventbus to announce creation of modules. If you want to register a module, simple call in your constructor function:\n\n```javascript\n Engine.emit('created',this)\n````\n\nA module MUST have a name to be registered. This name can be found on:\n\n- this.options.id = 'xxxx'\n- this.options.name = 'xxxx'\n- this.id = 'xxxx'\n- this.name = 'xxxx'\n\nThe module with the name `xxxx` is accessible as follows:\n\n```\n var Mediator = require('famous-mediator');\n Mediator.xxxx\n Mediator.on('created:xxxx',function(xxxx){ .... });\n```\n\nThe idea is to write Mediator singletons that couple modules together by:\n\n- listening to the events they emit\n- calling the public API\n- triggering events on a module\n\n## Changelog\n\n### 0.1.1 (31/10/2014)\n\n* Aliased `Mediator.on` to `Engine.on`.\n\n## Contribute\n\nFeel free to contribute to this project in any way. The easiest way to support this project is by giving it a star.\n\n## Contact\n- @markmarijnissen\n- http://www.madebymark.nl\n- info@madebymark.nl\n\n© 2014 - Mark Marijnissen", | ||
"readmeFilename": "README.md", | ||
"gitHead": "9d83c4f9c64cea45cd600904cf644cdcb8b7543e", | ||
"_id": "famous-mediator@0.1.1", | ||
"_shasum": "d90327b410d015baf08fa9f2d33648bad330a0d3", | ||
"_from": "famous-mediator@" | ||
} |
111
README.md
@@ -14,7 +14,16 @@ famous-mediator | ||
``` | ||
## Benefits | ||
Use the mediator pattern to decouple modules | ||
* Promotes clean, reusable modules | ||
* Clear seperation of concerns - no more sneaky business logic in your templates! | ||
* Is the perfect place for hacks, experiments, monkey-patches and workarounds. And because all of your "bad" code is in the Mediators, your actual code base stays clean! (True story!) | ||
## Example | ||
Imagine you have a `Router` module and a `pages` module: | ||
Imagine you have a `router` module and a `pages` module: | ||
*1. Router.js - does it's thing and doesn't know about other modules* | ||
*1. Router.js - just tracks navigation and doesn't know about other modules* | ||
```javascript | ||
@@ -29,3 +38,3 @@ function Router(){ | ||
*2. PageController.js - displays pages but doesn't know about any routes!* | ||
*2. PageController.js - just displays pages but doesn't know how to route & navigate!* | ||
```javascript | ||
@@ -38,2 +47,8 @@ function PageController(){ | ||
} | ||
// It emits an application wide event if a link is clicked. | ||
PageController.prototype.onLinkClick = function(event) { | ||
Engine.emit('link-clicked',event.target.getAttribute('href')); | ||
} | ||
} | ||
``` | ||
@@ -43,22 +58,30 @@ | ||
```javascript | ||
var Mediator = require('famous-mediator');; | ||
var Mediator = require('famous-mediator'); | ||
Mediator.on('created:Router',function(router){ | ||
// Router -> PageController | ||
Mediator.ready(['router','pages'],function(router,pages){ | ||
router.on('change',function(name){ | ||
Mediator.pages.setPage(name); | ||
pages.setPage(name); | ||
}); | ||
}); | ||
// PageController -> Router | ||
// | ||
// mediator translates 'link-clicked' from page to an actual | ||
// navigation in the router. | ||
Mediator.on('link-clicked',['router'],function(href,router){ | ||
router.set(href); | ||
}); | ||
``` | ||
## Benefits | ||
## Usage | ||
Using the mediator pattern to decouple modules | ||
The idea is to write Mediator singletons that couple modules together by: | ||
* Promotes clean, reusable modules | ||
* Clear seperation of concerns - no more sneaky business logic in your templates! | ||
* Is the perfect place for hacks, experiments, monkey-patches and workarounds. And because all of your "bad" code is in the Mediators, your actual code base stays clean! (True story!) | ||
1. Listen to events from one module, or listen to global events. | ||
2. Call public API of another module | ||
## Usage | ||
### Step 1: Create modules | ||
The Famo.us Engine is used as a global eventbus to announce creation of modules. If you want to register a module, simple call in your constructor function: | ||
The Famo.us Engine is used as a global eventbus to announce creation of modules. If you want to register a module, simple add to your constructor function: | ||
@@ -71,26 +94,62 @@ ```javascript | ||
- this.options.id = 'xxxx' | ||
- this.options.name = 'xxxx' | ||
- this.id = 'xxxx' | ||
- this.name = 'xxxx' | ||
- this.options.id = 'myCoolModule' | ||
- this.options.name = 'myCoolModule' | ||
- this.id = 'myCoolModule' | ||
- this.name = 'myCoolModule' | ||
The module with the name `xxxx` is accessible as follows: | ||
**Note:** | ||
I am using the `Engine` so that this `Mediator` module becomes an *optional* dependency. This enables component authors to facilitate a meditator pattern without enforcing this pattern on all users. | ||
### Step 2: Couple modules | ||
The module with the name `myCoolModule` is accessible using `Mediator.ready(moduleArray,readyCallback)`: | ||
``` | ||
var Mediator = require('famous-mediator'); | ||
Mediator.xxxx | ||
Mediator.on('created:xxxx',function(xxxx){ .... }); | ||
Mediator.ready(['myCoolModule'],function(myCoolModule){ .... }); | ||
``` | ||
The idea is to write Mediator singletons that couple modules together by: | ||
You can also mediate using global events and `Mediator.on(eventName,moduleArray,eventcallback)` | ||
- listening to the events they emit | ||
- calling the public API | ||
- triggering events on a module | ||
``` | ||
// in PageController.js | ||
Engine.emit('link-clicked','/homepage'); | ||
// in RouterMediator.js | ||
Engine.on('link-clicked',['router'],function(href,router){ | ||
router.set(href); | ||
}) | ||
``` | ||
As you can see, you can specify a module array to ensure modules are ready. | ||
## Tips | ||
### Use global events where you can | ||
* If there is only a single instance of the module | ||
* If the context of the module is not important | ||
Example: A 'navigate' or 'tweet' event doesn't care about its sender or context | ||
Counter example: A 'click' event might mean different things depending on its context (surface). | ||
### Write multiple mediators | ||
Write mediators for routing, error handling, data-flow, etc. This makes it easier to maintain your software. (i.e. when you decide to switch from an REST API to Firebase, you can simply rewrite the data mediator) | ||
### If you hack, hack in the mediators | ||
Keep your codebase clean and only add hacks in your mediators. | ||
* Mediators are application-specific and have little code. They are by definition not re-usable (because they couple). This makes it the perfect place to write hacks (which is usually highly app-specific code). | ||
* Mediators are high-level which make them safer to hack. When you tweak/modify/hack low-level code, you risk regression bugs and breaking everything that depends on it. | ||
## Changelog | ||
### 0.1.1 (31/10/2014) | ||
### 0.2.0 (1/10/2014) | ||
* Aliased `Mediator.on` to `Engine.on`. | ||
* Added `Mediator.ready` and `Mediator.on` | ||
* Updated README. | ||
@@ -97,0 +156,0 @@ ## Contribute |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14473
193
160