Skip to content

Instantly share code, notes, and snippets.

@Satyam
Created January 1, 2012 16:28
Show Gist options
  • Save Satyam/1547708 to your computer and use it in GitHub Desktop.
Save Satyam/1547708 to your computer and use it in GitHub Desktop.
App plugin
/*
App plugins can be loaded over an instance of App by setting a route with a wildcard path
such as:
{
path:'/:app/*',
callback: 'dispatchApp'
}
where the app param is the module name of the plugin,
which should have its entry in the YUI config file (unless loaded by some other means)
*/
dispatchApp: function (req) {
var appName = req.params.app,
self = this;
if (!Y[appName] || !this[appName]) {
Y.use(appName, function() {
if (Y[appName]) {
self.plug(Y[appName]);
self.dispatch();
} else {
Y.error('Plugin not found');
}
});
} else {
req.next();
}
}
}
/*
Once loaded and plugged in, dispatch is called to process the new routes just added.
Thus, it doesn't really matter what extra arguments the path contained besides the
plugin name, they will be caught by the routes added by the plugin.
Any further calls to this route will find the app plugin already loaded and plugged
so it will call the next route to deal with its routes. In fact, this happens as soon as
it got loaded sin the call to dispatch() would have made router enter dispatchApp again
and jump directly to the req.next() call.
The App Plugin should inherit from the following class:
*/
Y.Plugin.App = Y.Base.create('appPlugin', Y.Plugin.Base, [], {
_boundCallbacks:null,
initializer: function () {
var self = this,
host = self.get('host'),
constr = self.constructor,
binds = [],
b;
Y.each(constr.routes, function(item) {
b = Y.bind(self[item.callback],self);
binds.push({p:item.path, b:b})
host.route(item.path, b );
});
this._boundCallbacks = binds;
Y.mix(host.views,constr.views);
},
destructor: function () {
var self = this,
host = self.get('host'),
constr = self.constructor,
binds = this._boundCallbacks,
routes = host.get('routes'),
i,r,b = binds.pop();
Y.each(constr.views, function (value, key) {
if (host.views[key].type === value.type) {
delete host.views[key];
}
});
for (i = routes.length -1;i>=0 && b ;i--) {
r = routes[i];
if (b.p === r.path && b.b === r.callback) {
routes.splice(i,1);
b = binds.pop();
}
}
host.set('routes',routes);
}
});
/*
The App plugin can have its own set of views and routes
defined not as instance properties and attributes but as static members
which then get merged into the base views and routes.
The code is more complex than it should since it is somewhat hard to
identify the routes properly, specially since in this case they are bound to the
instance of the plugin so they can have access to its own properties and attributes
and not those of the host.
It would be great if routes could have a 'context' or 'scope' property.
The App plugin can be defined like this:
*/
Y.MyAppPlugin = Y.Base.create('my-app-plugin', Y.Plugin.App, [], {
/* ... */
}, {
ATTRS: { /* ... */ },
NS:'MyAppPlugin',
routes: [ /* ... */ ],
views: { /* ... */ }
});
/*
Unlike proper App instances, views and routes are defined as static properties,
their contents are the same as they would otherwise be.
The dispatchApp method at the top relies on the module name to be the same
as the name of the class for the plugin and the same as the namespace (NS) of the app Plugin.
In doing so, I'm bending a little bit the rules for naming modules, classes and plugin
namespaces since they should not all use the same capitalization.
Changing the code for the plugin dispatcher, this might not be so.
Other changes are possible:
The classes for the plugins might well reside all under a namespace under Y
instead of directly under Y, i.e.: Y.Apps.MyAppPlugin instead of Y.MyAppPlugin.
Furthermore, all plugins might use the same namespace (NS) so that only one of them
would be plugged in at any one time. The app plugin should make sure it destroys
any views and models it might have generated and the destructors for all these
views and models should free any resources they might have taken,
which should be done anyway, and is even more important in this case.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment