Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #459 from caridy/yaf-develop

support for route events in controllers, support global models, and spec->models mapping.
  • Loading branch information...
commit bb539ea84a11486192953a544955791b677e4bb5 2 parents a87298c + b6c3992
@caridy caridy authored
View
16 lib/app/autoload/action-context.common.js
@@ -303,9 +303,12 @@ YUI.add('mojito-action-context', function(Y, NAME) {
// If the action is not found try the '__call' function
if (Y.Lang.isFunction(controller.__call)) {
actionFunction = '__call';
+ Y.log('__call is deprecated, please use a router event instead.', 'warn', NAME);
+ } else if (controller.fire && controller.getEvent && controller.getEvent(actionFunction)) {
+ Y.log('Using a custom event to trigger the action into the controller', 'info', NAME);
} else {
- // If there is still no joy then die
- error = new Error("No method '" + command.action +
+ // If there is still no joy then die
+ error = new Error("No method or event '" + command.action +
"' on controller type '" + instance.type + "'");
error.code = 404;
throw error;
@@ -320,7 +323,14 @@ YUI.add('mojito-action-context', function(Y, NAME) {
Y.log('action context created, executing action "' + actionFunction +
'"', 'mojito', 'qeperf');
- controller[actionFunction](this);
+ if (controller[actionFunction]) {
+ Y.log('Action as a method is deprecated, please use a route event instead for action: ' + actionFunction, 'warn', NAME);
+ controller[actionFunction](this);
+ } else {
+ controller.fire(actionFunction, {
+ ac: this
+ });
+ }
}
Y.namespace('mojito').ActionContext = ActionContext;
View
236 lib/app/autoload/controller-context.common.js
@@ -29,6 +29,9 @@ YUI.add('mojito-controller-context', function(Y, NAME) {
// controller.init() call below.
instance = this.instance,
controller,
+ models,
+ actions = this.Y.mojito.actions || [],
+ ControllerClass,
shareYUIInstance = this.shareYUIInstance,
// do a shallow merge of app-level and mojit-level configs
@@ -36,68 +39,148 @@ YUI.add('mojito-controller-context', function(Y, NAME) {
configApp = this.store.getAppConfig({}).config,
configCombo = Y.merge(configApp, instance.config),
+ controllerName = instance['controller-module'],
// Y.mojito.controller for legacy, multi-instance.
// Y.mojito.controllers for shared instance
- c = this.Y.mojito.controller ||
- this.Y.mojito.controllers[instance['controller-module']];
+ originalController = this.Y.mojito.controller ||
+ this.Y.mojito.controllers[controllerName];
// If sharing YUI and controller clobbers, log an error.
if (shareYUIInstance && this.Y.mojito.controller) {
- this.Y.log(instance['controller-module'] + ' mojit' +
+ this.Y.log(controllerName + ' mojit' +
' clobbers Y.mojito.controller namespace. Please use' +
' `Y.namespace(\'mojito.controllers\')[NAME]` when ' +
' declaring controllers.', 'error', NAME);
}
- if (!Y.Lang.isObject(c)) {
+ // expanding models so we can pass the references into the
+ // initialization process.
+ models = this._expandModels(configCombo);
+
+ // using instance->models as a mapping to pass some models
+ // instances as part of the configuration into the
+ // controller. This will facilitate the access to global models in
+ // a form of config.modelName or this.get('modelName') if using
+ // attributes in the controller.
+ Y.Object.each((instance.models || {}), function(modelName, attrName) {
+ if (models[modelName]) {
+ configCombo[attrName] = models[modelName];
+ }
+ });
+
+ if (Y.Lang.isFunction(originalController)) {
+
+ // Using the original function as a synthetic controller class
+ // to mix in few other stuff
+ if (originalController._yuibuild) {
+
+ // TODO: what if the class extends Y.Base but was not built
+ // using Y.Base.create?
+
+ this.Y.log('Mix in actions and Y.EventTarget into a controller' +
+ ' created with Y.Base.create', 'debug', NAME);
+
+ // the original controller was build with Y.Base.create
+ // we just need to mix few more things, including actions
+ // in which case they need to be extensions.
+ ControllerClass = Y.Base.create(controllerName, originalController,
+ Y.Object.values(actions), {}, {});
+
+ // Make controller an EventTarget, we want to keep this very
+ // light-weight and obscure for now.
+ // TODO: how can we know if EventTarget is really needed
+ // it might be part of the original class already.
+ Y.mix(ControllerClass, Y.EventTarget, false, null, 1);
+
+ } else {
+
+ // the original controller seems to be a regular class
+ // let's just augment it to ger EventTarget support
+
+ this.Y.log('Mix in actions and Y.EventTarget into a class' +
+ ' controller', 'debug', NAME);
+
+ // TODO: how can I preserve the original class definition?
+ ControllerClass = originalController;
+
+ // TODO: should we use augment?
+
+ // Make controller an EventTarget, we want to keep this very
+ // light-weight and obscure for now.
+ Y.augment(ControllerClass, Y.EventTarget, true, null, {
+ emitFacade: true
+ });
+
+ // mix in any (new) actions (the actions namespace here would be
+ // populated by the resource store...but currently unused? Could
+ // this be replaced by light inheritance to the controllers here).
+ // TODO: should we keep supporting this? or should we just mix
+ // common + server or common + client controllers now that we
+ // have support for events?
+ Y.Object.each(actions, function(action, actionName) {
+ this.Y.log('mixing action \'' + actionName +
+ '\' into controller...', 'debug', NAME);
+ ControllerClass[actionName] = action;
+ });
+ }
+
+ } else if (Y.Lang.isObject(originalController)) {
+
+ // Creating a synthetic controller class to mix in few other stuff
+ ControllerClass = function() {};
+ ControllerClass.prototype = originalController;
+
+ // TODO: should we use augment?
+
+ this.Y.log('Mix in actions and Y.EventTarget into an object' +
+ ' controller', 'debug', NAME);
+
+ // Make controller an EventTarget, we want to keep this very
+ // light-weight and obscure for now.
+ Y.augment(ControllerClass, Y.EventTarget, true, null, {
+ emitFacade: true
+ });
+
+ // mix in any (new) actions (the actions namespace here would be
+ // populated by the resource store...but currently unused? Could
+ // this be replaced by light inheritance to the controllers here).
+ // TODO: should we keep supporting this? or should we just mix
+ // common + server or common + client controllers now that we
+ // have support for events?
+ Y.Object.each(this.Y.mojito.actions, function(action, actionName) {
+ this.Y.log('mixing action \'' + actionName +
+ '\' into controller...', 'debug', NAME);
+ ControllerClass[actionName] = action;
+ });
+
+ } else {
error = new Error('Mojit controller prototype is not an' +
- ' object! (mojit id: \'' + instance.id + '\')');
+ ' object or a function! (mojit id: \'' + instance.id + '\')');
error.code = 500;
throw error;
}
- // we make a controller instance by using the heir() function, this
- // gives us proper function scope within the controller actions
- controller = this.controller = Y.mojito.util.heir(c);
+ // we make a controller instance by using the controller class, this
+ // gives us proper function scope within the controller actions and
+ // add EventTarget capabilities into the controller instance.
+ // TODO: expand configCombo->models to plug app level models
+ controller = this.controller = new ControllerClass(configCombo);
- if (Y.Lang.isFunction(controller.init)) {
+ // explicitly checking the original controller, otherwise
+ // we should not try to deal with "init" since the Y.BaseCore
+ // life-cycle will handle that.
+ if (Y.Lang.isFunction(originalController.init)) {
// Use the instance data which isn't really an instance to
// provide construction parameters for the controller init().
controller.init(configCombo);
}
- // mix in any (new) actions (the actions namespace here would be
- // populated by the resource store...but currently unused? Could
- // this be replaced by light inheritance to the controllers here).
- Y.Object.each(this.Y.mojito.actions, function(action, actionName) {
- this.Y.log('mixing action \'' + actionName +
- '\' into controller...', 'debug', NAME);
- controller[actionName] = function() {
- action.apply(controller, arguments);
- };
- });
-
// stash the models this controller has available to be later
// attached to the ActionContext
- this.models = {};
-
- Y.Object.each(this.Y.mojito.models, function(model, modelName) {
-
- if (!shareYUIInstance || (instance.models &&
- instance.models[modelName])) {
-
- // TODO: Why? There's no particular reason to inherit here.
- var modelInstance = Y.mojito.util.heir(model);
-
- if (Y.Lang.isFunction(modelInstance.init)) {
- // NOTE that we use the same config here that we use to
- // config the controller
- modelInstance.init(configCombo);
- }
- this.models[modelName] = modelInstance;
- }
- }, this);
+ // TODO: this is for BC for those folks who are using
+ // ac.models.foo to get a model reference
+ this.models = models;
},
@@ -160,12 +243,87 @@ YUI.add('mojito-controller-context', function(Y, NAME) {
this.Y.mojito.perf.mark('mojito', 'core_dispatch_end[' +
(instance.id || '@' + instance.type) + ':' + action + ']');
+ },
+
+
+ _expandModels: function (mojitConfig) {
+ var error,
+ instance = this.instance,
+ models = {},
+ shareYUIInstance = this.shareYUIInstance,
+
+ // global models that inherit from Y.Model can
+ // be defined on application.json
+ globalModels = this.store.getAppConfig({}).models || {};
+
+
+ // TODO: for now, we have a hack to instantiate global models
+ // per mojit instance, but in the future, these models
+ // should be sandboxed as part of the "frame" mojit, and
+ // a reference should be hand over to the mojit that is
+ // trying to use it, so, part of the complexity of this
+ // method will be removed.
+
+ // BC for mojito models, so devs can get a reference to it
+ // from ac.models.<modelName>
+ Y.Object.each(this.Y.mojito.models, function(originalModel, modelName) {
+ var modelInstance,
+ ModelClass,
+ // in case a global model definition is passing some custom
+ // configurations, in which case they have precedence. Also,
+ // models might change it, better to preserve the mojitConfig.
+ modelConfig = Y.merge(mojitConfig, (globalModels[modelName] || {}));
+
+ // TODO: why? why do we need to worry about instance.models
+ if (!shareYUIInstance || (instance.models &&
+ instance.models[modelName])) {
+
+
+ if (Y.Lang.isFunction(originalModel)) {
+
+ // TODO: should we do something with the function? or should
+ // we just assume it extends Y.Model?
+ ModelClass = originalModel;
+
+ } else if (Y.Lang.isObject(originalModel)) {
+
+ // Creating a synthetic model class
+ ModelClass = function() {};
+ ModelClass.prototype = originalModel;
+
+ }
+
+
+ // we make a controller instance by using the controller class, this
+ // gives us proper function scope within the controller actions and
+ // add EventTarget capabilities into the controller instance.
+ // TODO: expand configCombo->models to plug app level models
+ modelInstance = new ModelClass(modelConfig);
+
+
+ // explicitly checking the original model, otherwise
+ // we should not try to deal with "init" since the Y.BaseCore
+ // life-cycle will handle that.
+ if (Y.Lang.isFunction(originalModel.init)) {
+ // NOTE that we use the same config here that we use to
+ // config the controller
+ modelInstance.init(modelConfig);
+ }
+ models[modelName] = modelInstance;
+ }
+
+ }, this);
+
+ return models;
}
+
};
Y.namespace('mojito').ControllerContext = ControllerContext;
}, '0.1.0', {requires: [
'mojito-action-context',
- 'mojito-util'
+ 'mojito-util',
+ 'event-custom',
+ 'base-build'
]});
View
2  travis/before.sh
@@ -14,7 +14,7 @@ fi
# YUI bleeding
cd ../
echo "Cloning YUI Repository"
-git clone git://github.com/yui/yui3.git yui-src
+git clone -b 3.x git://github.com/yui/yui3.git yui-src
wait
cd ./yui-src/src/yui
echo "Making YUI NPM Module"
Please sign in to comment.
Something went wrong with that request. Please try again.