Dependencies branch

1 view
Skip to first unread message

Nickolay Platonov

unread,
Feb 8, 2009, 2:27:59 PM2/8/09
to joos...@googlegroups.com
Hi all,

I'd like to describe the current state and new features in the dependencies branch.

It was derived from MCB branch, and have a slightly different kernel organization:
http://joose-js.googlecode.com/svn/branches/dependencies/doc_images/Kernel%20Organization.pdf


The primary change is that Class becomes isa Namespace (refactored from Joose.Module).
The new features, which follows from that change are below:

================================
1) Module call now accept props as 2nd argument, the former function at this place is now default for "body" property
(Class also has now this property)

Module('Test', function(){})
eq
Module('Test', {
    body : function(){}
})


================================
2) Module can be promoted to Class
Module('Test', {});

...
Class('Test', {});

================================
3) Class can have further Modules/Classes in namespace chain:
Class('Test', {});

...
Module('Test.Testy',{});
...
Class('Test.Testy.Testier',{});

================================
4) Modules and Classes can be freely nested (the same as above in pseudo code):
Class('Test', {
   
Module('Testy',{
      
Class('Testier',{
          ...
       });

    });
});

Collisions between namespace elements and class methods are detecting and exception throwing.
Generally speaking, the Module is a namespace, the Class operates as "default constructor" in its Module.



The actual dependency handling was implemented at Module level, via "use" property and works as follows:

Class('Test', {
    use : [ 'Test.Testy', 'Test.Testy.Testier']
});

...
Module('Test.Testy',{
    use :
'Test.Testy.Testier'
});
...
Class('Test.Testy.Testier',{});

The algorithm is based on Observable pattern and was already implemented in jScout, so I just cleared it from Ext dependencies and integrated into Joose.
Dependencies are loaded asynchronously and simultaneously. The "body" property of each call to Module, will be executed after the all dependencies of module (even from multiple calls to Module) were loaded.

Also was implemented a simple versioning scheme (version numbers are compared using usual "<" operator without any assumptions about format).

Class('Test', {
    version : 0.1,
    use : [ { Module : 'Test.Testy', version : 0.2} , 'Test.Testy.Testier']
});

...
Module('Test.Testy',{
    version : 0.2,
    use : { Module : 'Test.Testy.Testier', version : 0.3}

});
...
Class('Test.Testy.Testier',{});


There is a stress testing suite for this, which passes FF, IE6,7 and Chrome. Safari passes also, but fails in another part of suite.
http://joose-js.googlecode.com/svn/branches/dependencies/tests/libroot/StressTest/

quick link to whole test suite: http://joose-js.googlecode.com/svn/branches/dependencies/tests/test.html

Currently, implemented only loading via dynamic <script> tag. This approach benefit from being free from same-origin policy, but suffer from that fact, that its impossible to detect the errors (wrong URLs for example)

TODO:
- circular reference detections
- errors detections (seems possible not in all "transport mechanisms")
- more "transport mechanisms" (via async/sync Joose.SimpleRequest or external AJAX calls for example)
- "external" scripts support (3rd party apis, like GMap, etc)
- improved versioning? (possible including version in the URL, to avoid browser caching, when the version was increased)
- server-side librarian component, which will allow to load Class with any number of recursive dependencies in 2 http calls

There are probably more features to implement.

P.S. During work on this branch, I've encountered the issue with current Test.TAP.Class implementation. Test.TAP.Class execute all tests in the same global scope and clears it after each test.
This way the async tests, which relied on the presence of certain globals fails. This was fixed with dirty hack, and probably requires a better solution.


Regards, Nickolay

Malte Ubl

unread,
Feb 8, 2009, 3:38:10 PM2/8/09
to joos...@googlegroups.com
Hey,

this looks very promising, indeed.

Could you incorporate loading on the server side (e.g inside Rhino)
into your testing? How do you set the strategy for loading stuff into
the system?
Would it be possible to do a dry run with Rhino which generates a
manifest for building a single file which merges all modules in the
correct order?

Bye
Malte

Nickolay Platonov

unread,
Feb 8, 2009, 5:35:26 PM2/8/09
to joos...@googlegroups.com
The strategy is quite simple - each Module operates as follows (description in the scope of Module):

Creation
1) During creation, we 1st process "use" property, and the procession of "body" property puts into subscription on our own "ready" event. ("I'll process it when I'll be ready")
2) We creates the stub for each dependecy, and subscribes to its "ready" event ("when you'll be ready - let me know"). Each subscription removes the dependency from the list and perform checkDependencies()
3) We initiates the actual Loading of our dependencies.
4) checkDependencies()

Loading
1) After loading - we checkDependencies()
(note that after loading the loaded script will be already executed and will pass the 1-2)) of Creation and will gain dependencies)


checkDependencies()
1) we checks if we have remained dependencies.
2) If no - we fires our "ready" event.

This pattern repeats for each Module and the pieces are comes together.
The process starts from the inner-most Module which have no dependencies - it fires its own "ready" event and removes itself from its subcriptors lists.
They are then checks their's dependencies, etc.


The actual loading is handling in the "load" method of Namespace, so its possible to override it with Role and implement another "transport layer".
Though not sure how to do it in Rhino - have no experience with it, but its surely possible, algorithm itself doesn't rely on browser presence.

About building dependencies manifest - yes, sure and this will clear the road for the librarian component.

Regards, Nickolay

Malte Ubl

unread,
Feb 10, 2009, 4:19:08 PM2/10/09
to joos...@googlegroups.com
Cool,

some more questions:

Ist there support for non-Joose depencies (simple loading) and do you
have provisions for something like a list of search paths?

Nickolay Platonov

unread,
Feb 10, 2009, 5:36:30 PM2/10/09
to joos...@googlegroups.com
Yes, non-Joose deps are supported.

I can see 2 cases here
1) 3rd party APIs (like GMap) which are loading from arbitrary url.
Since the <script> tag transport is free from same-origin policy they can be loaded.
Sample use case: http://joose-js.googlecode.com/svn/branches/dependencies/tests/14_modules_dependencies_basic.t.js

2) non-Joose deps, which stores in the "main library" and can be loaded on url, which correspond to class name.
For example, if Joose is using with Ext bridge, and we have Ext.ux.GMapPanel extension. Then it will be possible
to include 'Ext.ux.GMapPanel' as dependency, even if its actually non-Joose class.
This is though untested yet.

About list of search paths: It will be very usefull to have this feature, but to implement it
we need a mechanism which will detect a presense of source file on particularly URL.
As I know, this is possible for AJAX calls, but I cant find the way for it for <script> tag transport.

<script onerror> property works only in FF..

So, seems we need to switch to AJAX calls as default transport, but the then again arise the question with 3rd party APIs..

May be this feature should be delegated to librarian component?

Nickolay Platonov

unread,
Feb 10, 2009, 6:07:07 PM2/10/09
to joos...@googlegroups.com
After some thinking, this feature seems critical for local lib management and package distribution implementation..
Reply all
Reply to author
Forward
0 new messages