Skip to content
This repository

Sweetandsour2 #874

Merged
merged 10 commits into from over 1 year ago

2 participants

bthaeler Caridy Patino
bthaeler

This is a new hook system that replaces the embedded perf code. This will be the foundation for a new debugger interface.

Caridy Patino
Owner

@bthaeler a couple of questions:

  • did you validate the unit tests? I remember when I put in place the perf there were few issues with the tests due to the fact that every script is using Y.mojito.hooks, which is undefined if it is not part of the testsuite mocks.

  • is the current output format of the perf still preserved? if not, we need to verify with @drewfish about this impact of that since we use it in the current profiler and potentially in our CI to monitor performance, but I'm not sure about that last part.

Caridy Patino
Owner

According to @drewfish we are not using perf and profiler at all in our CI, so, feel free to break it, and even remove it in favor of the new profiler.

So, the only thing pending is the tests to pass.

bthaeler

The existing perf markers are re-implemented using the hook system. So they should continue to work just fine.

It wasn't very clear to me how to run the unit tests. So I didn't. Looking at the Travis build output on github, it looks like some of the tests do complain. I can try and fix those. Is there a good current document explaining how to setup a test system for running the unit tests?

Caridy Patino
Owner

Few pointers:

bthaeler

I added some missing mojito-hooks dependency to places, and I added a stuff for the hook system to the test base. I think the unit tests are fine now.

Caridy Patino caridy commented on the diff January 09, 2013
tests/base/mojito-test.js
@@ -26,6 +26,12 @@ YUI.add('mojito', function(Y, NAME) {
26 26
     Y.namespace('mojito.addons');
27 27
     Y.namespace('mojito.addons.ac');
28 28
     Y.namespace('mojito.addons.viewEngines');
  29
+    // HookSystem::StartBlock
  30
+    Y.mojito.hooks = {
  31
+        hook: function () {},
3
Caridy Patino Owner
caridy added a note January 09, 2013

I think enableHookGroup is also missing from this. Even though only perf is calling it conditionally, I think we will have more scripts calling it, in which case we need a mock for testing.

Caridy Patino Owner
caridy added a note January 11, 2013

Any update on this one?

bthaeler
bthaeler added a note January 11, 2013

yes, I added a stub function.

    enableHookGroup: function () {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Caridy Patino caridy commented on the diff January 09, 2013
lib/mojito.js
@@ -281,6 +281,13 @@ MojitoServer.prototype._configureAppInstance = function(app, options) {
281 281
         outputHandler.setLogger({
282 282
             log: Y.log
283 283
         });
  284
+        // HookSystem::StartBlock
  285
+        if (!req.hook) {
  286
+            req.hook = null;
3
Caridy Patino Owner
caridy added a note January 09, 2013

What is the purpose of setting it to null when it is already a falsy value?

bthaeler
bthaeler added a note January 09, 2013

From my, and your performance tests, it is more efficient to test if a field is null vs undefined. IE 'if (x.y)' is more efficient if x.y = null vs x.y is just undefined.

I am just trying to make the hook system as low impact as possible.

Caridy Patino Owner
caridy added a note January 11, 2013

ok, we can revisit this later on after the first merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/mojito.js
@@ -281,6 +281,13 @@ MojitoServer.prototype._configureAppInstance = function(app, options) {
281 281
         outputHandler.setLogger({
282 282
             log: Y.log
283 283
         });
  284
+        // HookSystem::StartBlock
  285
+        if (!req.hook) {
  286
+            req.hook = null;
  287
+        }
  288
+        outputHandler.hook = req.hook;
  289
+        command.instance.hook = req.hook;
1
Caridy Patino Owner
caridy added a note January 09, 2013

Any particular reason for adding hook into the instance instead of command. In theory, an instance's action can only be executed if there is a command to be dispatched. In my mind, hook is more tied to command than it is to instance. In any case, you should have access to command from everywhere during the dispatch process, just like instance. In the server, this is just a technicallity, but in the client, that instance is persistent, and command is created every time an action is invoked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/app/autoload/mojito-client.client.js
@@ -629,6 +642,12 @@ YUI.add('mojito-client', function(Y, NAME) {
629 642
 
630 643
             outputHandler = new Y.mojito.OutputHandler(viewId, cb, this);
631 644
 
  645
+            // HookSystem::StartBlock
  646
+            if (Y.mojito.hooks) {
  647
+                outputHandler.hook = Y.mojito.hooks.client_ctx;
2
Caridy Patino Owner
caridy added a note January 09, 2013

client_ctx sounds weird here, specially when you assign it to outputHandler.hook. Is client_ctx the persistent hook in the client side? If yes, can we rename it? We don't want to keep overloading the context term in mojito, we already use it for many things.

bthaeler
bthaeler added a note January 09, 2013

Originally this was for the clients. But this also works for people who want to enable a hook global, without associating this with a request. So how about 'global_hooks'.

Each callback will have its own unique this state per tool. This is stored in this object. So do we want 'global_hooks' or 'global_hooks_ctx'?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Caridy Patino caridy commented on the diff January 09, 2013
lib/app/autoload/hooks.common.js
((101 lines not shown))
  101
+     * @param {String} tool tool name for this hook
  102
+     * @param {String} hook name of hook
  103
+     * @param {Function} cb call back function
  104
+     *
  105
+     * Example:
  106
+     * <pre>
  107
+     * Y.mojito.hooks.registerHook('test_tool, 'test_hook', function (reg, data) {});
  108
+     * </pre>
  109
+     */
  110
+    Y.mojito.hooks.registerHook = function (tool, hook, cb) {
  111
+        if (!map_hook_tool[hook]) {
  112
+            map_hook_tool[hook] = {};
  113
+        }
  114
+        map_hook_tool[hook][tool] = cb;
  115
+
  116
+        Y.mojito.hooks.hook = Y.mojito.hooks.real_hook;
5
Caridy Patino Owner
caridy added a note January 09, 2013

Why is this happening here? This sounds like a leak-prompt structure, because any request can potentially change this. If the hook is something created per request, then it should be an inmutable object.

bthaeler
bthaeler added a note January 09, 2013

Not sure what your concern is here. Hooks are statically defined. Groups of hooks are enabled on a per request basis. What hooks people are interested in is static.

Caridy Patino Owner
caridy added a note January 11, 2013

My question is why hook gets real_hook whenever a new hook is register? is this something to have "hook" defined only if there is at least one hook registered? I'm just curious about this statement.

bthaeler
bthaeler added a note January 11, 2013

Yes, the idea is that if you are not using any code that needs a hook, the hook system defaults to an empty function.

Caridy Patino Owner
caridy added a note January 11, 2013

ok

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/app/autoload/hooks.common.js
((123 lines not shown))
  123
+     *
  124
+     * @method enableHookGroup
  125
+     * @param {Object} req Request object to enable hook on
  126
+     * @param {String} tool Name of tool to enable
  127
+     * @return {Object} this tools context used for its hook callbacks
  128
+     *
  129
+     * Example:
  130
+     * <pre>
  131
+     * Y.mojito.hooks.enableHookGroup(req, 'test_tool');
  132
+     * </pre>
  133
+     */
  134
+    Y.mojito.hooks.enableHookGroup = function (req, tool) {
  135
+        var ret = {};
  136
+        // Y.log("enable tool: " + tool);
  137
+
  138
+        // if called in client, use single global context.
2
Caridy Patino Owner
caridy added a note January 09, 2013

In my experience, trying to accomodate a component to be common when in reality the behavior of the client and server are very different will bring problems. I wonder if we should split this into server and client.

bthaeler
bthaeler added a note January 09, 2013

If I rename this to 'global_hooks' I think this resolved this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/app/autoload/hooks.common.js
((60 lines not shown))
  60
+     * @method hook
  61
+     * @param {String} hook_name
  62
+     * @param {Object} hook context (from req.hook, or adapter.hook)
  63
+     * @param {} hook specific data
  64
+     *
  65
+     * Example:
  66
+     * <pre>
  67
+     * Y.mojito.hooks.hook('test_hook', ctx, ...);
  68
+     * </pre>
  69
+     */
  70
+    Y.mojito.hooks.hook = function () {
  71
+        // Y.log("hooks disabled");
  72
+    };
  73
+
  74
+    Y.mojito.hooks.real_hook = function (hook, ctx) {
  75
+        // Y.log("in hook handler: " + hook);
1
Caridy Patino Owner
caridy added a note January 09, 2013

Please, preserve all Y.log(msg, 'info or debug or error', NAME), instead of commenting them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Caridy Patino caridy commented on the diff January 09, 2013
lib/app/autoload/hooks.common.js
((5 lines not shown))
  5
+ */
  6
+
  7
+
  8
+/*jslint anon:true, sloppy:true*/
  9
+/*global YUI*/
  10
+
  11
+/**
  12
+ * The hook system provides a way for application or add on developers to access the inner working of mojito.
  13
+ * There are two steps to using an hook. First, an addon needs to register an interest in a hook by calling
  14
+ * registerHook with the name of the hook and a callback function. The second step involves enabling a hook
  15
+ * your addon to recieve hooks. You need to call the enableHookGroup with the name of your addon.
  16
+ *
  17
+ * List of hooks
  18
+ *
  19
+ * <ul>
  20
+ * <li>Y.mojito.hooks.hook('adapterBuffer', hook_ctx, 'start', this);
3
Caridy Patino Owner
caridy added a note January 09, 2013

Why do we have these docs here? this is the hook utility, it will be difficult to keep this in sync with all the hooks defined across the mojito or the app. We can create a tool to generate such as list, or we can document them in a form of YUI events on site, rather than here.

Caridy Patino Owner
caridy added a note January 11, 2013

any update on this one?

bthaeler
bthaeler added a note January 11, 2013

Sounds like you are proposing two options. One is to create a script that runs at build time to generate this list, and the second one is using some kind of YUI event to do this? I don't now what would be better. Is this something we want to solve now or latter? If you feel strongly about this, I can try and figure something out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/app/addons/view-engines/hb.server.js
@@ -39,26 +39,32 @@ YUI.add('mojito-hb', function(Y, NAME) {
39 39
          */
40 40
         render: function (data, mojitType, tmpl, adapter, meta, more) {
41 41
             var cacheTemplates = meta && meta.view && meta.view.cacheTemplates,
42  
-                perf = Y.mojito.perf.timeline('mojito', 'hb:render',
43  
-                    'time to render a template', tmpl),
44  
-                handler = function (err, obj) {
45  
-                    var output;
  42
+                handler;
2
Caridy Patino Owner
caridy added a note January 09, 2013

Any particular reason for moving handler's function definition down rather than in the var statement?

bthaeler
bthaeler added a note January 09, 2013

No, i will fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/app/addons/ac/composite.common.js
@@ -204,8 +209,10 @@ callback({
204 209
             var ac = this.ac,
205 210
                 buffer = {},
206 211
                 content = {},
207  
-                meta = {},
208  
-                perf;
  212
+                // HookSystem::StartBlock
  213
+                self = this,
1
Caridy Patino Owner
caridy added a note January 09, 2013

my convention, we use my = this instead of self = this. Don't ask my why :)

And by the way, don't need to define the comments for that line, it is ok to have my defined even when u don't use it later on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/app/addons/ac/composite.common.js
((6 lines not shown))
18 18
         this.buffer = buffer;
19 19
         this.id = id;
20 20
         this.callback = callback;
21 21
         this.__meta = [];
22  
-        this.__perf = Y.mojito.perf.timeline(NAME, 'child', 'the whole child', id);
  22
+        // HookSystem::StartBlock
  23
+        this.__hook_ctx = hook_ctx;
1
Caridy Patino Owner
caridy added a note January 09, 2013

Can you elaborate more about this __hook_ctx very private reference?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/app/addons/ac/composite.common.js
@@ -14,19 +14,24 @@
14 14
  */
15 15
 YUI.add('mojito-composite-addon', function(Y, NAME) {
16 16
 
17  
-    function AdapterBuffer(buffer, id, callback) {
  17
+    function AdapterBuffer(buffer, id, callback, hook_ctx) {
1
Caridy Patino Owner
caridy added a note January 09, 2013

This is the only part that I really don't like. Extending the signature of the class to add the hook reference after the callback is not pretty. Let's try to find another way to keep that reference around.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/app/addons/ac/composite.common.js
@@ -335,7 +346,7 @@ callback({
335 346
                     };
336 347
 
337 348
                     childAdapter = new AdapterBuffer(buffer, childName,
338  
-                            callback);
  349
+                            callback, this.adapter.hook);
1
Caridy Patino Owner
caridy added a note January 09, 2013

same as above, we should not do this here. Let's find another way for the adapter to pull a reference of the current hook.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Caridy Patino
Owner

@bthaeler that's all for now. Let's work together to accomodate those details so we can finally merge it.

Caridy Patino
Owner

+1

@bthaeler I think we are ready to merge it. Since we have a release coming up on monday, I will hold this until after that.

/cc @jenny

bthaeler

Any idea when you will do the merge for this pull request?

Caridy Patino
Owner

today or tomorrow! 0.5.2 is out!

Caridy Patino caridy merged commit 15e22ca into from January 15, 2013
Caridy Patino caridy closed this January 15, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 10 unique commits by 1 author.

Dec 19, 2012
bthaeler build hook system into mojito
remove perf markers, and replace with hooks.
ff4c55a
bthaeler build hook system into mojito
remove perf markers, and replace with hooks.
f687db6
bthaeler changed to null context for clients.
updated documents.
04654fc
Jan 03, 2013
bthaeler more stuff. ac4bb7a
bthaeler Merge branch 'develop' of git://github.com/yahoo/mojito into sweetand…
…sour2

Conflicts:
	lib/app/autoload/store.server.js
6cd7b4d
Jan 07, 2013
bthaeler add hook to template renderiing. e019db5
Jan 08, 2013
bthaeler added some missing hook dependencys.
added stub to test harnas
842ac5b
Jan 09, 2013
bthaeler some changes recommended by Caridy
Plus fixes for end hook points
c5ba1b8
Jan 10, 2013
bthaeler added enableHookGroup to test stubs c368ffb
bthaeler cliend needs to use global_hooks_ctx too. 028e28b
This page is out of date. Refresh to see the latest.
22  lib/app/addons/ac/composite.common.js
@@ -19,14 +19,15 @@ YUI.add('mojito-composite-addon', function(Y, NAME) {
19 19
         this.id = id;
20 20
         this.callback = callback;
21 21
         this.__meta = [];
22  
-        this.__perf = Y.mojito.perf.timeline(NAME, 'child', 'the whole child', id);
23 22
     }
24 23
 
25 24
 
26 25
     AdapterBuffer.prototype = {
27 26
 
28 27
         done: function(data, meta) {
29  
-            this.__perf.done();
  28
+            // HookSystem::StartBlock
  29
+            Y.mojito.hooks.hook('adapterBuffer', this.hook, 'end', this);
  30
+            // HookSystem::EndBlock
30 31
             this.buffer[this.id].meta = meta;
31 32
             this.buffer[this.id].data += data;
32 33
             this.buffer.__counter__ -= 1;
@@ -204,8 +205,8 @@ callback({
204 205
             var ac = this.ac,
205 206
                 buffer = {},
206 207
                 content = {},
207  
-                meta = {},
208  
-                perf;
  208
+                my = this,
  209
+                meta = {};
209 210
 
210 211
             cfg.children = cfg.children || {};
211 212
 
@@ -215,7 +216,9 @@ callback({
215 216
                                 ' array. \'children\' must be an object.');
216 217
             }
217 218
 
218  
-            perf = Y.mojito.perf.timeline(NAME, 'execute', Y.Object.keys(cfg.children).join(','), ac.command);
  219
+            // HookSystem::StartBlock
  220
+            Y.mojito.hooks.hook('addon', this.hook, 'start', my, cfg);
  221
+            // HookSystem::EndBlock
219 222
 
220 223
             meta.children = cfg.children;
221 224
 
@@ -246,7 +249,9 @@ callback({
246 249
                         ac.assets.mixAssets(meta.assets, cfg.assets);
247 250
                     }
248 251
 
249  
-                    perf.done();
  252
+                    // HookSystem::StartBlock
  253
+                    Y.mojito.hooks.hook('addon', my.hook, 'end', my);
  254
+                    // HookSystem::EndBlock
250 255
 
251 256
                     cb(content, meta);
252 257
                 });
@@ -336,6 +341,9 @@ callback({
336 341
 
337 342
                     childAdapter = new AdapterBuffer(buffer, childName,
338 343
                             callback);
  344
+                    // HookSystem::StartBlock
  345
+                    Y.mojito.hooks.hook('adapterBuffer', this.adapter.hook, 'start', childAdapter);
  346
+                    // HookSystem::EndBlock
339 347
                     childAdapter = Y.mix(childAdapter, this.adapter);
340 348
 
341 349
                     this.dispatch(newCommand, childAdapter);
@@ -349,7 +357,7 @@ callback({
349 357
 }, '0.1.0', {requires: [
350 358
     'mojito',
351 359
     'mojito-util',
352  
-    'mojito-perf',
  360
+    'mojito-hooks',
353 361
     'mojito-assets-addon',
354 362
     'mojito-params-addon'
355 363
 ]});
13  lib/app/addons/view-engines/hb.server.js
@@ -39,8 +39,6 @@ YUI.add('mojito-hb', function(Y, NAME) {
39 39
          */
40 40
         render: function (data, mojitType, tmpl, adapter, meta, more) {
41 41
             var cacheTemplates = meta && meta.view && meta.view.cacheTemplates,
42  
-                perf = Y.mojito.perf.timeline('mojito', 'hb:render',
43  
-                    'time to render a template', tmpl),
44 42
                 handler = function (err, obj) {
45 43
                     var output;
46 44
 
@@ -51,7 +49,9 @@ YUI.add('mojito-hb', function(Y, NAME) {
51 49
 
52 50
                     output = obj.compiled(data);
53 51
 
54  
-                    perf.done(); // closing the 'hb:render' timeline
  52
+                    // HookSystem::StartBlock
  53
+                    Y.mojito.hooks.hook('hb', adapter.hook, 'end', tmpl);
  54
+                    // HookSystem::EndBlock
55 55
 
56 56
                     if (more) {
57 57
                         adapter.flush(output, meta);
@@ -60,6 +60,10 @@ YUI.add('mojito-hb', function(Y, NAME) {
60 60
                     }
61 61
                 };
62 62
 
  63
+            // HookSystem::StartBlock
  64
+            Y.mojito.hooks.hook('hb', adapter.hook, 'start', tmpl);
  65
+            // HookSystem::EndBlock
  66
+
63 67
             this._getTemplateObj(tmpl, !cacheTemplates, handler);
64 68
         },
65 69
 
@@ -129,4 +133,5 @@ YUI.add('mojito-hb', function(Y, NAME) {
129 133
     Y.namespace('mojito.addons.viewEngines').hb = HandleBarsAdapter;
130 134
 
131 135
 }, '0.1.0', {requires: [
132  
-    'mojito-perf']});
  136
+    'mojito-hooks'
  137
+]});
56  lib/app/autoload/action-context.common.js
@@ -238,9 +238,10 @@ YUI.add('mojito-action-context', function(Y, NAME) {
238 238
         var i,
239 239
             addon,
240 240
             addonName,
241  
-            acAddons = command.instance.acAddons || [],
242  
-            perf = Y.mojito.perf.timeline('mojito', 'ac:addons',
243  
-                'attaching addons to AC object', command);
  241
+            acAddons = command.instance.acAddons || [];
  242
+        // HookSystem::StartBlock
  243
+        Y.mojito.hooks.hook('attachActionContext', adapter.hook, 'start', command);
  244
+        // HookSystem::EndBlock
244 245
 
245 246
         for (i = 0; i < acAddons.length; i += 1) {
246 247
             addonName = acAddons[i];
@@ -262,7 +263,9 @@ YUI.add('mojito-action-context', function(Y, NAME) {
262 263
             }
263 264
         }
264 265
 
265  
-        perf.done();  // closing the 'ac:addons' timeline
  266
+        // HookSystem::StartBlock
  267
+        Y.mojito.hooks.hook('attachActionContext', adapter.hook, 'end', command);
  268
+        // HookSystem::EndBlock
266 269
 
267 270
     }
268 271
 
@@ -280,12 +283,16 @@ YUI.add('mojito-action-context', function(Y, NAME) {
280 283
             command = opts.command,
281 284
             store = opts.store,
282 285
             actionFunction,
283  
-            perf = Y.mojito.perf.timeline('mojito', 'ac:init', 'set up AC object', command),
284 286
             error,
285  
-            staticAppConfig = store.getAppConfig(store.getStaticContext()),
  287
+            staticAppConfig,
286 288
             my;
287 289
 
288 290
         my = this;
  291
+        // HookSystem::StartBlock
  292
+        Y.mojito.hooks.hook('actionContext', opts.adapter.hook, 'start', my, opts);
  293
+        // HookSystem::EndBlock
  294
+
  295
+        staticAppConfig = store.getAppConfig(store.getStaticContext());
289 296
 
290 297
         // It's possible to setup a route that calls "foo.", which means that
291 298
         // the default action in the instance should be used instead.
@@ -333,12 +340,9 @@ YUI.add('mojito-action-context', function(Y, NAME) {
333 340
             }
334 341
         }
335 342
 
336  
-        perf.done(); // closing the 'ac:init' timeline
337  
-
338  
-        Y.mojito.perf.mark('mojito', 'action:start', 'before the action', command);
339  
-
340  
-        perf = Y.mojito.perf.timeline('mojito', 'action:call',
341  
-            'the initial syncronous part of the action', command);
  343
+        // HookSystem::StartBlock
  344
+        Y.mojito.hooks.hook('actionContext', opts.adapter.hook, 'end1', my, opts);
  345
+        // HookSystem::EndBlock
342 346
 
343 347
         // Reap the request/ac process within the timeout. If ac.done or
344 348
         // ac.error is invoked by user code prior to the time limit this
@@ -366,7 +370,9 @@ YUI.add('mojito-action-context', function(Y, NAME) {
366 370
 
367 371
         controller[actionFunction](this);
368 372
 
369  
-        perf.done(); // closing the 'action:call' timeline
  373
+        // HookSystem::StartBlock
  374
+        Y.mojito.hooks.hook('actionContext', opts.adapter.hook, 'end2', my, opts);
  375
+        // HookSystem::EndBlock
370 376
 
371 377
     }
372 378
 
@@ -409,10 +415,11 @@ YUI.add('mojito-action-context', function(Y, NAME) {
409 415
                 // static app configuration options
410 416
                 pathToRoot          = this.staticAppConfig.pathToRoot,
411 417
                 cacheViewTemplates  = this.staticAppConfig.cacheViewTemplates,
412  
-                viewEngineOptions   = this.staticAppConfig.viewEngine || {},
  418
+                viewEngineOptions   = this.staticAppConfig.viewEngine || {};
413 419
 
414  
-                perf = Y.mojito.perf.timeline('mojito', 'ac.done',
415  
-                    'time to execute ac.done process', this.command);
  420
+            // HookSystem::StartBlock
  421
+            Y.mojito.hooks.hook('actionContextDone', adapter.hook, 'start', this);
  422
+            // HookSystem::EndBlock
416 423
 
417 424
             if (Y.Lang.isString(meta)) {
418 425
                 // If the meta string is a serializer set it
@@ -527,7 +534,9 @@ YUI.add('mojito-action-context', function(Y, NAME) {
527 534
                 //Y.log('pushing to native adapter', 'info', NAME);
528 535
                 adapter[callbackFunc](data, meta);
529 536
 
530  
-                perf.done(); // closing the 'ac.done' timeline
  537
+                // HookSystem::StartBlock
  538
+                Y.mojito.hooks.hook('actionContextDone', adapter.hook, 'end1', this);
  539
+                // HookSystem::EndBlock
531 540
 
532 541
                 return;
533 542
             }
@@ -555,6 +564,10 @@ YUI.add('mojito-action-context', function(Y, NAME) {
555 564
                     contentPath = pathToRoot + contentPath;
556 565
                 }
557 566
 
  567
+                // HookSystem::StartBlock
  568
+                Y.mojito.hooks.hook('Render', adapter.hook, data, instance, contentPath);
  569
+                // HookSystem::EndBlock
  570
+
558 571
                 // optimize for server only
559 572
                 if ('server' === context.runtime) {
560 573
                     renderer = CACHE.renderers[mojitView.engine];
@@ -583,9 +596,9 @@ YUI.add('mojito-action-context', function(Y, NAME) {
583 596
                 adapter[callbackFunc](data, meta);
584 597
             }
585 598
 
586  
-            perf.done(); // closing the 'ac.done' timeline
587  
-
588  
-            Y.mojito.perf.mark('mojito', 'action:stop', 'after the action', this.command);
  599
+            // HookSystem::StartBlock
  600
+            Y.mojito.hooks.hook('actionContextDone', adapter.hook, 'end2', this);
  601
+            // HookSystem::EndBlock
589 602
         },
590 603
 
591 604
         /**
@@ -618,5 +631,6 @@ YUI.add('mojito-action-context', function(Y, NAME) {
618 631
     'json-stringify',
619 632
     'event-custom-base',
620 633
     'mojito-view-renderer',
621  
-    'mojito-util'
  634
+    'mojito-util',
  635
+    'mojito-hooks'
622 636
 ]});
28  lib/app/autoload/dispatch.client.js
@@ -92,9 +92,11 @@ YUI.add('mojito-dispatcher', function (Y, NAME) {
92 92
          * @param {OutputAdapter} adapter the output adapter
93 93
          */
94 94
         _createActionContext: function (command, adapter) {
95  
-            var ac,
96  
-                perf = Y.mojito.perf.timeline('mojito', 'ac:ctor',
97  
-                    'create ControllerContext', command);
  95
+            var ac;
  96
+
  97
+            // HookSystem::StartBlock
  98
+            Y.mojito.hooks.hook('dispatchCreateAction', adapter.hook, 'start', command);
  99
+            // HookSystem::EndBlock
98 100
 
99 101
             // the controller is not stateless on the client, we
100 102
             // store it for re-use.
@@ -124,7 +126,9 @@ YUI.add('mojito-dispatcher', function (Y, NAME) {
124 126
                 Y.log(e.stack, 'error', NAME);
125 127
                 adapter.error(e);
126 128
             }
127  
-            perf.done(); // closing the 'ac:ctor' timeline
  129
+            // HookSystem::StartBlock
  130
+            Y.mojito.hooks.hook('dispatchCreateAction', adapter.hook, 'end', command);
  131
+            // HookSystem::EndBlock
128 132
         },
129 133
 
130 134
         /**
@@ -164,10 +168,11 @@ YUI.add('mojito-dispatcher', function (Y, NAME) {
164 168
         dispatch: function (command, adapter) {
165 169
 
166 170
             var my = this,
167  
-                store = this.store,
168  
-                perf = Y.mojito.perf.timeline('mojito',
169  
-                    'dispatch:expandInstance',
170  
-                    'gather details about mojit', command);
  171
+                store = this.store;
  172
+
  173
+            // HookSystem::StartBlock
  174
+            Y.mojito.hooks.hook('dispatch', adapter.hook, 'start', command);
  175
+            // HookSystem::EndBlock
171 176
 
172 177
             store.validateContext(command.context);
173 178
 
@@ -191,7 +196,9 @@ YUI.add('mojito-dispatcher', function (Y, NAME) {
191 196
             store.expandInstance(command.instance, command.context,
192 197
                 function (err, instance) {
193 198
 
194  
-                    perf.done(); // closing 'dispatch:expandInstance' timeline
  199
+                    // HookSystem::StartBlock
  200
+                    Y.mojito.hooks.hook('dispatch', adapter.hook, 'end', command);
  201
+                    // HookSystem::EndBlock
195 202
 
196 203
                     if (err || !instance || !instance.controller) {
197 204
 
@@ -228,5 +235,6 @@ YUI.add('mojito-dispatcher', function (Y, NAME) {
228 235
 
229 236
 }, '0.1.0', {requires: [
230 237
     'mojito-action-context',
231  
-    'mojito-util'
  238
+    'mojito-util',
  239
+    'mojito-hooks'
232 240
 ]});
27  lib/app/autoload/dispatch.server.js
@@ -55,9 +55,11 @@ YUI.add('mojito-dispatcher', function (Y, NAME) {
55 55
          */
56 56
         _createActionContext: function (command, adapter) {
57 57
             var ac,
58  
-                controller = Y.mojito.controllers[command.instance.controller],
59  
-                perf = Y.mojito.perf.timeline('mojito', 'ac:ctor',
60  
-                    'create ControllerContext', command);
  58
+                controller = Y.mojito.controllers[command.instance.controller];
  59
+
  60
+            // HookSystem::StartBlock
  61
+            Y.mojito.hooks.hook('dispatchCreateAction', adapter.hook, 'start', command);
  62
+            // HookSystem::EndBlock
61 63
 
62 64
             // Note that creation of an ActionContext current causes
63 65
             // immediate invocation of the dispatch() call.
@@ -77,7 +79,9 @@ YUI.add('mojito-dispatcher', function (Y, NAME) {
77 79
                 Y.log(e.stack, 'error', NAME);
78 80
                 adapter.error(e);
79 81
             }
80  
-            perf.done(); // closing the 'ac:ctor' timeline
  82
+            // HookSystem::StartBlock
  83
+            Y.mojito.hooks.hook('dispatchCreateAction', adapter.hook, 'end', command);
  84
+            // HookSystem::EndBlock
81 85
         },
82 86
 
83 87
         /**
@@ -113,11 +117,11 @@ YUI.add('mojito-dispatcher', function (Y, NAME) {
113 117
         dispatch: function (command, adapter) {
114 118
 
115 119
             var my = this,
116  
-                store = this.store,
117  
-                perf = Y.mojito.perf.timeline('mojito',
118  
-                    'dispatch:expandInstance',
119  
-                    'gather details about mojit', command);
  120
+                store = this.store;
120 121
 
  122
+            // HookSystem::StartBlock
  123
+            Y.mojito.hooks.hook('dispatch', adapter.hook, 'start', command);
  124
+            // HookSystem::EndBlock
121 125
             store.validateContext(command.context);
122 126
 
123 127
             if (command.rpc) {
@@ -129,7 +133,9 @@ YUI.add('mojito-dispatcher', function (Y, NAME) {
129 133
             store.expandInstance(command.instance, command.context,
130 134
                 function (err, instance) {
131 135
 
132  
-                    perf.done(); // closing 'dispatch:expandInstance' timeline
  136
+                    // HookSystem::StartBlock
  137
+                    Y.mojito.hooks.hook('dispatch', adapter.hook, 'end', command);
  138
+                    // HookSystem::EndBlock
133 139
 
134 140
                     if (err || !instance || !instance.controller) {
135 141
 
@@ -159,5 +165,6 @@ YUI.add('mojito-dispatcher', function (Y, NAME) {
159 165
 
160 166
 }, '0.1.0', {requires: [
161 167
     'mojito-action-context',
162  
-    'mojito-util'
  168
+    'mojito-util',
  169
+    'mojito-hooks'
163 170
 ]});
161  lib/app/autoload/hooks.common.js
... ...
@@ -0,0 +1,161 @@
  1
+/*
  2
+ * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
  3
+ * Copyrights licensed under the New BSD License.
  4
+ * See the accompanying LICENSE file for terms.
  5
+ */
  6
+
  7
+
  8
+/*jslint anon:true, sloppy:true*/
  9
+/*global YUI*/
  10
+
  11
+/**
  12
+ * The hook system provides a way for application or add on developers to access the inner working of mojito.
  13
+ * There are two steps to using an hook. First, an addon needs to register an interest in a hook by calling
  14
+ * registerHook with the name of the hook and a callback function. The second step involves enabling a hook
  15
+ * your addon to recieve hooks. You need to call the enableHookGroup with the name of your addon.
  16
+ *
  17
+ * List of hooks
  18
+ *
  19
+ * <ul>
  20
+ * <li>Y.mojito.hooks.hook('adapterBuffer', hook_ctx, 'start', this);
  21
+ * <li>Y.mojito.hooks.hook('adapterBuffer', this.__hook_ctx, 'end', this);
  22
+ * <li>Y.mojito.hooks.hook('addon', this.adapter.hook, 'start', self, cfg);
  23
+ * <li>Y.mojito.hooks.hook('addon', this.adapter.hook, 'end', self);
  24
+ * <li>Y.mojito.hooks.hook('hb', adapter.hook, 'start', tmpl);
  25
+ * <li>Y.mojito.hooks.hook('hb', adapter.hook, 'end');
  26
+ * <li>Y.mojito.hooks.hook('attachActionContext', adapter.hook, 'start', command);
  27
+ * <li>Y.mojito.hooks.hook('attachActionContext', adapter.hook, 'end');
  28
+ * <li>Y.mojito.hooks.hook('actionContext', opts.adapter.hook, 'start', my, opts);
  29
+ * <li>Y.mojito.hooks.hook('actionContext', opts.adapter.hook, 'end1', my, opts);
  30
+ * <li>Y.mojito.hooks.hook('actionContext', opts.adapter.hook, 'end2', my, opts);
  31
+ * <li>Y.mojito.hooks.hook('actionContextDone', adapter.hook, 'start', this);
  32
+ * <li>Y.mojito.hooks.hook('actionContextDone', adapter.hook, 'end1', this);
  33
+ * <li>Y.mojito.hooks.hook('actionContextDone', adapter.hook, 'end2', this);
  34
+ * <li>Y.mojito.hooks.hook('dispatchCreateAction', adapter.hook, 'start', command);
  35
+ * <li>Y.mojito.hooks.hook('dispatchCreateAction', adapter.hook, 'end');
  36
+ * <li>Y.mojito.hooks.hook('dispatch', adapter.hook, 'start', command);
  37
+ * <li>Y.mojito.hooks.hook('dispatch', adapter.hook, 'end');
  38
+ * <li>Y.mojito.hooks.hook('storeTypeDetails', instance.hook, 'start', spec);
  39
+ * <li>Y.mojito.hooks.hook('storeTypeDetails', instance.hook, 'end');
  40
+ * <li>Y.mojito.hooks.hook('mojitoClient', null, 'start', this);
  41
+ * <li>Y.mojito.hooks.hook('mojitoClient', null, 'end', this);
  42
+ * <li>Y.mojito.hooks.hook('mojitoClientBind', null, 'start', this);
  43
+ * <li>Y.mojito.hooks.hook('mojitoClientBind', null, 'resume', this);
  44
+ * <li>Y.mojito.hooks.hook('mojitoClientBind', null, 'end', this);
  45
+ * <li>Y.mojito.hooks.hook('mojitoClientBindComplete', null, 'start', this);
  46
+ * <li>Y.mojito.hooks.hook('mojitoClientBindComplete', null, 'end', this);
  47
+ * <li>Y.mojito.hooks.hook('AppDispatch', req.hook, req, res);
  48
+ * </ul>
  49
+ * @module MojitoHooks
  50
+ */
  51
+
  52
+
  53
+YUI.add('mojito-hooks', function(Y, NAME) {
  54
+    var map_hook_tool = {};
  55
+
  56
+    Y.namespace('mojito.hooks');
  57
+
  58
+    /**
  59
+     * Trigger a hook
  60
+     * @method hook
  61
+     * @param {String} hook_name
  62
+     * @param {Object} hook context (from req.hook, or adapter.hook)
  63
+     * @param {} hook specific data
  64
+     *
  65
+     * Example:
  66
+     * <pre>
  67
+     * Y.mojito.hooks.hook('test_hook', ctx, ...);
  68
+     * </pre>
  69
+     */
  70
+    Y.mojito.hooks.hook = function (hook) {
  71
+        Y.log("hooks disabled (" + hook + ")", 'debug', NAME);
  72
+    };
  73
+
  74
+    Y.mojito.hooks.real_hook = function (hook, ctx) {
  75
+        Y.log("in hook handler: " + hook, 'debug', NAME);
  76
+        var tool,
  77
+            ds_args;
  78
+
  79
+        if (!map_hook_tool[hook] || (!ctx && !Y.mojito.hooks.global_hooks_ctx)) {
  80
+            return;
  81
+        }
  82
+        ds_args = [].slice.apply(arguments).slice(2);
  83
+        for (tool in map_hook_tool[hook]) {
  84
+            if (map_hook_tool[hook].hasOwnProperty(tool)) {
  85
+                if (ctx && ctx[tool]) {
  86
+                    map_hook_tool[hook][tool].apply(ctx[tool], ds_args);
  87
+                }
  88
+                if (Y.mojito.hooks.global_hooks_ctx && Y.mojito.hooks.global_hooks_ctx[tool]) {
  89
+                    map_hook_tool[hook][tool].apply(Y.mojito.hooks.global_hooks_ctx[tool], ds_args);
  90
+                }
  91
+            }
  92
+        }
  93
+    };
  94
+
  95
+    /**
  96
+     * A Mojito addon/tool can register an interest in a hook. The call context for all tools
  97
+     * will be the same for all hooks registered for a tool, and it will be new and unique for
  98
+     * each request. The params to the call back function are hook specific.
  99
+     *
  100
+     * @method registerhook
  101
+     * @param {String} tool tool name for this hook
  102
+     * @param {String} hook name of hook
  103
+     * @param {Function} cb call back function
  104
+     *
  105
+     * Example:
  106
+     * <pre>
  107
+     * Y.mojito.hooks.registerHook('test_tool, 'test_hook', function (reg, data) {});
  108
+     * </pre>
  109
+     */
  110
+    Y.mojito.hooks.registerHook = function (tool, hook, cb) {
  111
+        if (!map_hook_tool[hook]) {
  112
+            map_hook_tool[hook] = {};
  113
+        }
  114
+        map_hook_tool[hook][tool] = cb;
  115
+
  116
+        Y.mojito.hooks.hook = Y.mojito.hooks.real_hook;
  117
+
  118
+        Y.log("register tool: " + tool + ", hook: " + hook, 'debug', NAME);
  119
+    };
  120
+
  121
+    /**
  122
+     * Enable a tool for a request.
  123
+     *
  124
+     * @method enableHookGroup
  125
+     * @param {Object} req Request object to enable hook on
  126
+     * @param {String} tool Name of tool to enable
  127
+     * @return {Object} this tools context used for its hook callbacks
  128
+     *
  129
+     * Example:
  130
+     * <pre>
  131
+     * Y.mojito.hooks.enableHookGroup(req, 'test_tool');
  132
+     * </pre>
  133
+     */
  134
+    Y.mojito.hooks.enableHookGroup = function (req, tool) {
  135
+        var ret = {};
  136
+        Y.log("enable tool: " + tool, 'debug', NAME);
  137
+
  138
+        // if called in client, use single global context.
  139
+        if (!req) {
  140
+            if (!Y.mojito.hooks.global_hooks_ctx) {
  141
+                Y.mojito.hooks.global_hooks_ctx = {};
  142
+            }
  143
+            if (!Y.mojito.hooks.global_hooks_ctx[tool]) {
  144
+                Y.mojito.hooks.global_hooks_ctx[tool] = {};
  145
+            }
  146
+            ret = Y.mojito.hooks.global_hooks_ctx[tool];
  147
+        } else {
  148
+            if (!req.hook) {
  149
+                req.hook = {};
  150
+            }
  151
+
  152
+            if (!req.hook[tool]) {
  153
+                req.hook[tool] = {};
  154
+            }
  155
+            ret = req.hook[tool];
  156
+        }
  157
+
  158
+        return ret;
  159
+    };
  160
+
  161
+}, '0.1.0', {requires: []});
38  lib/app/autoload/mojito-client.client.js
@@ -317,7 +317,9 @@ YUI.add('mojito-client', function(Y, NAME) {
317 317
 
318 318
         init: function(config) {
319 319
             fireLifecycle('pre-init', {config: config});
320  
-            Y.mojito.perf.mark('mojito', 'core_client_start');
  320
+            // HookSystem::StartBlock
  321
+            Y.mojito.hooks.hook('mojitoClient', null, 'start', this);
  322
+            // HookSystem::EndBlock
321 323
             var that = this,
322 324
                 appConfig = config.appConfig;
323 325
 
@@ -356,8 +358,9 @@ YUI.add('mojito-client', function(Y, NAME) {
356 358
             // id
357 359
             this._mojits = {};
358 360
 
359  
-            Y.mojito.perf.mark('mojito',
360  
-                'core_client_end', 'Mojito client library loaded');
  361
+            // HookSystem::StartBlock
  362
+            Y.mojito.hooks.hook('mojitoClient', null, 'end', this);
  363
+            // HookSystem::EndBlock
361 364
             /* FUTURE -- perhaps only do this once a user needs it
362 365
             var singletons;
363 366
             singletons = {
@@ -438,12 +441,18 @@ YUI.add('mojito-client', function(Y, NAME) {
438 441
             binderMap = eventData.binderMap;
439 442
             parentId = eventData.parentId;
440 443
             topLevelMojitViewId = eventData.topLevelMojitViewId;
441  
-            Y.mojito.perf.mark('mojito', 'core_binders_start');
  444
+            // HookSystem::StartBlock
  445
+            Y.mojito.hooks.hook('mojitoClientBind', null, 'start', this);
  446
+            // HookSystem::EndBlock
442 447
 
443 448
             if (!totalBinders) {
444  
-                Y.mojito.perf.mark('mojito', 'core_binders_resume');
  449
+                // HookSystem::StartBlock
  450
+                Y.mojito.hooks.hook('mojitoClientBind', null, 'resume', this);
  451
+                // HookSystem::EndBlock
445 452
                 me.resume();
446  
-                Y.mojito.perf.mark('mojito', 'core_binders_end');
  453
+                // HookSystem::StartBlock
  454
+                Y.mojito.hooks.hook('mojitoClientBind', null, 'end', this);
  455
+                // HookSystem::EndBlock
447 456
                 fireLifecycle('post-attach-binders', {});
448 457
                 return;
449 458
             }
@@ -486,9 +495,13 @@ YUI.add('mojito-client', function(Y, NAME) {
486 495
                     recordBoundMojit(me._mojits, parentId, viewid, proxy.type);
487 496
                 });
488 497
 
489  
-                Y.mojito.perf.mark('mojito', 'core_binders_resume');
  498
+                // HookSystem::StartBlock
  499
+                Y.mojito.hooks.hook('mojitoClientBindComplete', null, 'start', this);
  500
+                // HookSystem::EndBlock
490 501
                 me.resume();
491  
-                Y.mojito.perf.mark('mojito', 'core_binders_end');
  502
+                // HookSystem::StartBlock
  503
+                Y.mojito.hooks.hook('mojitoClientBindComplete', null, 'end', this);
  504
+                // HookSystem::EndBlock
492 505
                 fireLifecycle('post-attach-binders', {});
493 506
             };
494 507
 
@@ -629,6 +642,12 @@ YUI.add('mojito-client', function(Y, NAME) {
629 642
 
630 643
             outputHandler = new Y.mojito.OutputHandler(viewId, cb, this);
631 644
 
  645
+            // HookSystem::StartBlock
  646
+            if (Y.mojito.hooks) {
  647
+                outputHandler.hook = Y.mojito.hooks.global_hooks_ctx;
  648
+            }
  649
+            // HookSystem::EndBlock
  650
+
632 651
             this.dispatcher.dispatch(command, outputHandler);
633 652
         },
634 653
 
@@ -994,5 +1013,6 @@ YUI.add('mojito-client', function(Y, NAME) {
994 1013
     'mojito-mojit-proxy',
995 1014
     'mojito-tunnel-client',
996 1015
     'mojito-output-handler',
997  
-    'mojito-util'
  1016
+    'mojito-util',
  1017
+    'mojito-hooks'
998 1018
 ]});
89  lib/app/autoload/perf.client.js
@@ -112,5 +112,92 @@ YUI.add('mojito-perf', function(Y, NAME) {
112 112
         }
113 113
     };
114 114
 
  115
+    if (perfEnabled) {
  116
+        Y.mojito.hooks.enableHookGroup(null, 'mojito-perf');
  117
+    }
  118
+
  119
+    Y.mojito.hooks.registerHook(NAME, 'adapterBuffer', function(w, adapter) {
  120
+        if (w === 'start') {
  121
+            this.ab_perf = Y.mojito.perf.timeline('mojito-composite-addon', 'child', 'the whole child', adapter.id);
  122
+        } else {
  123
+            this.ab_perf.done();
  124
+        }
  125
+    });
  126
+    Y.mojito.hooks.registerHook(NAME, 'addon', function(w, addOn, cfg) {
  127
+        if (w === 'start') {
  128
+            this.ad_perf = Y.mojito.perf.timeline('mojito-composite-addon', 'execute', Y.Object.keys(cfg.children).join(','), addOn.ac.command);
  129
+        } else {
  130
+            this.ad_perf.done();
  131
+        }
  132
+    });
  133
+    Y.mojito.hooks.registerHook(NAME, 'attachActionContext', function(w, command) {
  134
+        if (w === 'start') {
  135
+            this.acc_perf = Y.mojito.perf.timeline('mojito', 'ac:addons', 'attaching addons to AC object', command);
  136
+        } else {
  137
+            this.acc_perf.done();
  138
+        }
  139
+    });
  140
+    Y.mojito.hooks.registerHook(NAME, 'actionContext', function(w, ac, opts) {
  141
+        if (w === 'start') {
  142
+            this.ac_perf = Y.mojito.perf.timeline('mojito', 'ac:init', 'set up AC object', opts.command);
  143
+        } else if (w === 'end1') {
  144
+            this.ac_perf.done();
  145
+            Y.mojito.perf.mark('mojito', 'action:start', 'before the action', opts.command);
  146
+            this.ac_perf = Y.mojito.perf.timeline('mojito', 'action:call', 'the initial syncronous part of the action', opts.command);
  147
+        } else {
  148
+            this.ac_perf.done();
  149
+        }
  150
+    });
  151
+    Y.mojito.hooks.registerHook(NAME, 'actionContextDone', function(w, ac) {
  152
+        if (w === 'start') {
  153
+            this.acd_perf = Y.mojito.perf.timeline('mojito', 'ac.done', 'time to execute ac.done process', ac.command);
  154
+        } else if (w === 'end1') {
  155
+            this.acd_perf.done();
  156
+        } else {
  157
+            this.acd_perf.done();
  158
+            Y.mojito.perf.mark('mojito', 'action:stop', 'after the action', ac.command);
  159
+        }
  160
+    });
  161
+    Y.mojito.hooks.registerHook(NAME, 'dispatchCreateAction', function(w, command) {
  162
+        if (w === 'start') {
  163
+            this.dac_perf = Y.mojito.perf.timeline('mojito', 'ac:ctor', 'create ControllerContext', command);
  164
+        } else {
  165
+            this.dac_perf.done();
  166
+        }
  167
+    });
  168
+    Y.mojito.hooks.registerHook(NAME, 'dispatch', function(w, command) {
  169
+        if (w === 'start') {
  170
+            this.dis_perf = Y.mojito.perf.timeline('mojito', 'dispatch:expandInstance', 'gather details about mojit', command);
  171
+        } else {
  172
+            this.dis_perf.done();
  173
+        }
  174
+    });
  175
+    Y.mojito.hooks.registerHook(NAME, 'mojitoClient', function(w, client) {
  176
+        if (w === 'start') {
  177
+            Y.mojito.perf.mark('mojito', 'core_client_start');
  178
+        } else {
  179
+            Y.mojito.perf.mark('mojito',
  180
+                'core_client_end', 'Mojito client library loaded');
  181
+        }
  182
+    });
  183
+    Y.mojito.hooks.registerHook(NAME, 'mojitoClientBind', function(w, client) {
  184
+        if (w === 'start') {
  185
+            Y.mojito.perf.mark('mojito', 'core_client_start');
  186
+        } else if (w === 'resume') {
  187
+            Y.mojito.perf.mark('mojito', 'core_binders_resume');
  188
+        } else {
  189
+            Y.mojito.perf.mark('mojito', 'core_binders_end');
  190
+        }
  191
+    });
  192
+    Y.mojito.hooks.registerHook(NAME, 'mojitoClientBindComplete', function(w, client) {
  193
+        if (w === 'start') {
  194
+            Y.mojito.perf.mark('mojito', 'core_binders_resume');
  195
+        } else {
  196
+            Y.mojito.perf.mark('mojito', 'core_binders_end');
  197
+        }
  198
+    });
  199
+
115 200
     Y.namespace('mojito').perf = perf;
116  
-});
  201
+}, '0.1.0', {requires: [
  202
+    'mojito-hooks'
  203
+]});
83  lib/app/autoload/perf.server.js
@@ -309,6 +309,87 @@ YUI.add('mojito-perf', function (Y, NAME) {
309 309
         config = {};
310 310
     }
311 311
 
  312
+    // Hook profiles in
  313
+    Y.mojito.hooks.registerHook(NAME, 'adapterBuffer', function(w, adapter) {
  314
+        if (w === 'start') {
  315
+            this.ab_perf = Y.mojito.perf.timeline('mojito-composite-addon', 'child', 'the whole child', adapter.id);
  316
+        } else {
  317
+            this.ab_perf.done();
  318
+        }
  319
+    });
  320
+    Y.mojito.hooks.registerHook(NAME, 'addon', function(w, addOn, cfg) {
  321
+        if (w === 'start') {
  322
+            this.ad_perf = Y.mojito.perf.timeline('mojito-composite-addon', 'execute', Y.Object.keys(cfg.children).join(','), addOn.ac.command);
  323
+        } else {
  324
+            this.ad_perf.done();
  325
+        }
  326
+    });
  327
+    Y.mojito.hooks.registerHook(NAME, 'hb', function(w, tmpl) {
  328
+        if (w === 'start') {
  329
+            this.hb_perf = Y.mojito.perf.timeline('mojito', 'hb:render', 'time to render a template', tmpl);
  330
+        } else {
  331
+            this.hb_perf.done();
  332
+        }
  333
+    });
  334
+    Y.mojito.hooks.registerHook(NAME, 'attachActionContext', function(w, command) {
  335
+        if (w === 'start') {
  336
+            this.acc_perf = Y.mojito.perf.timeline('mojito', 'ac:addons', 'attaching addons to AC object', command);
  337
+        } else {
  338
+            this.acc_perf.done();
  339
+        }
  340
+    });
  341
+    Y.mojito.hooks.registerHook(NAME, 'actionContext', function(w, ac, opts) {
  342
+        if (w === 'start') {
  343
+            this.ac_perf = Y.mojito.perf.timeline('mojito', 'ac:init', 'set up AC object', opts.command);
  344
+        } else if (w === 'end1') {
  345
+            this.ac_perf.done();
  346
+            Y.mojito.perf.mark('mojito', 'action:start', 'before the action', opts.command);
  347
+            this.ac_perf = Y.mojito.perf.timeline('mojito', 'action:call', 'the initial syncronous part of the action', opts.command);
  348
+        } else {
  349
+            this.ac_perf.done();
  350
+        }
  351
+    });
  352
+    Y.mojito.hooks.registerHook(NAME, 'actionContextDone', function(w, ac) {
  353
+        if (w === 'start') {
  354
+            this.acd_perf = Y.mojito.perf.timeline('mojito', 'ac.done', 'time to execute ac.done process', ac.command);
  355
+        } else if (w === 'end1') {
  356
+            this.acd_perf.done();
  357
+        } else {
  358
+            this.acd_perf.done();
  359
+            Y.mojito.perf.mark('mojito', 'action:stop', 'after the action', ac.command);
  360
+        }
  361
+    });
  362
+    Y.mojito.hooks.registerHook(NAME, 'dispatchCreateAction', function(w, command) {
  363
+        if (w === 'start') {
  364
+            this.dac_perf = Y.mojito.perf.timeline('mojito', 'ac:ctor', 'create ControllerContext', command);
  365
+        } else {
  366
+            this.dac_perf.done();
  367
+        }
  368
+    });
  369
+    Y.mojito.hooks.registerHook(NAME, 'dispatch', function(w, command) {
  370
+        if (w === 'start') {
  371
+            this.dis_perf = Y.mojito.perf.timeline('mojito', 'dispatch:expandInstance', 'gather details about mojit', command);
  372
+        } else {
  373
+            this.dis_perf.done();
  374
+        }
  375
+    });
  376
+    Y.mojito.hooks.registerHook(NAME, 'storeTypeDetails', function(w, spec) {
  377
+        if (w === 'start') {
  378
+            this.st_perf = Y.mojito.perf.timeline('mojito', 'store:getMojitTypeDetails', 'expand mojit spec', spec.type);
  379
+        } else {
  380
+            this.st_perf.done();
  381
+        }
  382
+    });
  383
+    Y.mojito.hooks.registerHook(NAME, 'AppDispatch', function(req, res) {
  384
+        // if perf metrics are on, we should hook into
  385
+        // the mojito request to flush metrics when
  386
+        // the connection is closed.
  387
+        if (Y.mojito.perf.instrumentMojitoRequest) {
  388
+            Y.mojito.perf.instrumentMojitoRequest(req, res);
  389
+        }
  390
+    });
  391
+
312 392
 }, '0.1.0', {requires: [
313  
-    'mojito'
  393
+    'mojito',
  394
+    'mojito-hooks'
314 395
 ]});
22  lib/app/autoload/store.server.js
@@ -216,6 +216,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) {
216 216
                         this._config.mojitoRoot,
217 217
                         'app/addons/rs/config.js'
218 218
                     )
  219
+                },
  220
+                'mojito-hooks': {
  221
+                    fullpath: this._libs.path.join(
  222
+                        this._config.mojitoRoot,
  223
+                        'app/autoload/hooks.common.js'
  224
+                    )
219 225
                 }
220 226
             });
221 227
             this.plug(Y.mojito.addons.rs.config, { appRoot: this._config.root, mojitoRoot: this._config.mojitoRoot });
@@ -536,8 +542,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) {
536 542
                 },
537 543
                 spec,
538 544
                 typeDetails,
539  
-                newInst,
540  
-                perf;
  545
+                newInst;
541 546
 
542 547
             // TODO:  should this be done here, or somewhere else?
543 548
             ctx.runtime = env;
@@ -559,13 +564,17 @@ YUI.add('mojito-resource-store', function(Y, NAME) {
559 564
 
560 565
             // type details
561 566
             try {
562  
-                perf = Y.mojito.perf.timeline('mojito', 'store:getMojitTypeDetails',
563  
-                    'expand mojit spec', spec.type);
  567
+                // HookSystem::StartBlock
  568
+                Y.mojito.hooks.hook('storeTypeDetails', instance.hook, 'start', spec);
  569
+                // HookSystem::EndBlock
  570
+
564 571
                 typeDetails = this.getMojitTypeDetails(env, ctx, spec.type, null, true);
565 572
             } catch (err2) {
566 573
                 return cb(err2);
567 574
             } finally {
568  
-                perf.done();
  575
+                // HookSystem::StartBlock
  576
+                Y.mojito.hooks.hook('storeTypeDetails', instance.hook, 'end');
  577
+                // HookSystem::EndBlock
569 578
             }
570 579
 
571 580
             // priority (most important to least):
@@ -2093,5 +2102,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) {
2093 2102
 }, '0.0.1', { requires: [
2094 2103
     'base',
2095 2104
     'oop',
2096  
-    'mojito-util'
  2105
+    'mojito-util',
  2106
+    'mojito-hooks'
2097 2107
 ]});
6  lib/app/autoload/view-renderer.client.js
@@ -79,6 +79,9 @@ YUI.add('mojito-view-renderer', function (Y) {
79 79
          */
80 80
         render: function (data, mojitType, tmpl, adapter, meta, more) {
81 81
             var my = this;
  82
+            // HookSystem::StartBlock
  83
+            Y.mojito.hooks.hook('Render', adapter.hook, data, mojitType, tmpl, adapter, meta, more);
  84
+            // HookSystem::EndBlock
82 85
             /*
83 86
                 TODO: this method should also do:
84 87
                     a) load template when needed
@@ -103,5 +106,6 @@ YUI.add('mojito-view-renderer', function (Y) {
103 106
     Y.namespace('mojito').ViewRenderer = Renderer;
104 107