AMD questions

48 views
Skip to first unread message

Paul Spencer

unread,
Mar 13, 2012, 2:24:33 PM3/13/12
to jx...@googlegroups.com
Hi Jon,

I wanted to thank you for the work on AMD stuff and also ask a couple of questions.  I've had a chance to work with requirejs quite a bit on a new project (unfortunately not involving Jx!) and have had some great success with it so I'm starting to understand enough to ask questions.  The approach I am taking is that each module is a require and the dependencies are listed in the code

require(['module1', 'module2'], function(module1, module2) {

  var module3 = function() {
    // some use of module1 and module2 ...
  }

  return module3;
});

I'm comparing this to your approach recently committed to trunk, you are using define and there are no explicit module dependencies in the code.  I'm curious about the two different approaches.  I know that JxLib also has the MooTool dependency and that we are using YAML headers for compilation and dependencies, so that is probably the key factor in choosing the current approach.  In the 'require' approach, I am using the requirejs optimizer r.js to compile the final module for public which works if you use require with an array of module dependencies as the first argument.

One advantage I see to the require approach is that you could use parts of JxLib on the fly by just requiring, say, Jx.Button and it would require all its dependencies (Jx.Widget etc) then give you a reference to just the Jx.Button class in your module.  I don't think there is a way to do this with the current approach.  

Somewhat related to both this and the Theme Engine discussion - we've also been using a library called Handlebars.js (based on Mustache templating).  The require.js text plugin is pretty cool and allows you to load in the templates as module dependencies.  I'm wondering if we could use that with templating in some way.  I don't have a refined idea of how it would work at the moment, I'm just speculating :)

Looking forward to your comments and insight

Cheers

Paul

Jon Bomgardner

unread,
Mar 13, 2012, 5:07:57 PM3/13/12
to jx...@googlegroups.com
Paul,
 
Glad to see you had a chance to take a look... I was beginning to think there wasn't anyone around anymore... almost gave out the Ferris Bueller reference... lol... anyway..
 
So, the reason I went the way I did is because this is the way Mootools will be doing it when they finally release verison 1.5. (https://github.com/mootools/mootools-core/issues/2102). Also, in the case of some modules there's no way to know in advance what plugins, adaptors, or other classes could be needed.
 
This method uses the CommonJS wrapper technique and besides not listing the dependencies in the call to define it pretty much works the way you would expect it. Outside of Jx (as if I'm using it in an app) I can do as you say:
 
require(['jx/button'], function(Button){
  //button loads and is usable here
});
 
The only difference comes in writing a component of the lib itself.  For a part of Jx itself you should use define and the CommonJS style:
 
define('jx/path/to/file', function(require, exports, module){
 
    //inside here you assign the final class to module.exports which requirejs recognizes as the same as a return
    //you also use require() - synchronous version - to get the parts you need as the requirejs library scans the code
    //for require() calls and loads those for you.
 
});
 
If you look in the examples/require directory you should find a test.html file that illustrates the usage outside of the library. The only issue I've come up against at this point is any dynamic requires (ones passed in such as plugins or adaptors or those determined at run time) because those cause exceptions to be thrown at the moment. I'm also seeing some empty objects being passed in for the normal require calls. I'm still trying to figure these out. 
 
I will admit that I'm considering the following changes:
 
1) switch back to the "normal" syntax but keep using define rather than require
2) For any plugins/adaptors we would require the developer to pre-load them through their require statement
 
I'm also looking at a possible fix for this without changing the above however it will require either using the asynchronous version of require() and delaying some processing until we know that the needed modules are loaded, or possibly coding a shim around require that would be called inside jx modules (jx.require globally or base.require in modules) that would check to see if the module is already loaded and if so just pass it back and if not, call require to load it and then block until it's returned (not ideal obviously).
 
In any case, you can use modules on the fly using require().  There are currently 2 ways to get the modules: 
 
  1. Just load the dependency with the file path: Jx.Button would be 'jx/button'
  2. Use the jx require plugin. This allows you to do 'jx!Button.Flyout'. You simply lowercase the first jx (so require recognizes it) and exchange the first '.' with a '!' so require recognizes this as a plugin. Do the rest of the filename as you would in global mode and it should load up for you.
 Also, the mootools dependencies would need to be loaded separately before require until 1.5 when they will be AMD compatible. I tried loading them using require and it just got messy. I'm willing to try again though if you like....
 
As for the r.js optimizer... it also works with CommonJS modules from what I understand but there is no way that I've been able to figure out to build the entire library all at once as r.js doesn't scan directories but rather starts with a single .js file and builds out the dependencies from there. The way I have it set up we can continue to use the build process we have established to build the "full" build which includes a global.js file that is the magic piece for making this version a global drop-in replacement without having to adjust much code and you can use r.js on your main application file to build a customized version without the stuff you don't need.
 
As for the templating part - I'll look at handlebars again and let you know what I think. As for the text plugin, it could be handy but I don't see a way to do the following:
 
1) change the directory based on loaded theme.
2) reload templates when the theme changes
 
Anyway, I realize I just wrote a small book and that this is a lot to digest. As always, I'm open to suggestions, modifications, or anything else.
 
Thanks,
Jon

Jon Bomgardner

unread,
Mar 15, 2012, 2:46:57 AM3/15/12
to jx...@googlegroups.com
Paul,

So, part of the problem I'm seeing is one of circular references. It seems Jx.Object requires Jx.Plugin which requires Jx.Object. And I'm fairly certain that that this probably happens through out the codebase in many places. I tried loading Jx.Plugin within Jx.Object using another require statement but it keeps sending back either an empty object or throwing an exception... Any ideas or thoughts on how we may get around this?

Jon

On Tuesday, March 13, 2012 11:24:33 AM UTC-7, pagameba wrote:

aek

unread,
Mar 15, 2012, 10:02:30 AM3/15/12
to jx...@googlegroups.com
That is the so called spaghetti code and can be solved using patterns. 
For example:
SpringFramework in Java resolve this issue using setter injection in properties instead of using constructor injection and making one bean dependent of the creation of the other bean. The patterns here are among other IoC/DI Factory and Decorator

In C++ that issue can be observed in the headers #include and can be resolved declaring the headers in one level up, in a manager class that use one of the other two classes that need to include the other class, To be more clear: Class A need to use Class B and Class B need to use Class A, declaring the #include of Class A or Class B in Class C that manage or is used first than Class A or Class B resolve the problem

In JxLib could be resolved by having a base set of classes pre-loaded

Jon Bomgardner

unread,
Mar 15, 2012, 12:39:46 PM3/15/12
to jx...@googlegroups.com
Actually, in this case the problem is created by requirejs itself. Using the CommonJS pattern that I chose it scans the code for all require() function calls that are just plain strings and loads them as pre-reqs for that function. For Jx.Object that means it ends up loading Jx.Plugin as a prereq which itself has Jx.Object as a prereq.... I'm not sure that what your suggesting is gonna fix it.
 
Thanks,
Jon

Jon Bomgardner

unread,
Mar 15, 2012, 12:43:10 PM3/15/12
to jx...@googlegroups.com
Though perhaps having Jx.Object, Jx.Widget, and Jx.Plugin all preloaded might not be a bad solution... they are used by just about every other class in the framework...

aek

unread,
Mar 15, 2012, 1:36:04 PM3/15/12
to jx...@googlegroups.com

El jueves 15 de marzo de 2012 12:43:10 UTC-4, Jon Bomgardner escribió:
Though perhaps having Jx.Object, Jx.Widget, and Jx.Plugin all preloaded might not be a bad solution... they are used by just about every other class in the framework...

 
That's what I'm talking about !!!

Jon Bomgardner

unread,
Mar 15, 2012, 2:09:29 PM3/15/12
to jx...@googlegroups.com
The question is about the best way to do so with requirejs... will have to give that some thought... since they would still use the define/require methods this may still have the same issue but we'll see I guess....
 
Jon

Jon Bomgardner

unread,
Mar 15, 2012, 11:58:22 PM3/15/12
to jx...@googlegroups.com
So, after an hour or so of playing around with this I've come to the conclusion that we have 2 choices here:

1) Make Jx.Plugin no longer extend from Jx.Object. We'll have to replicate some of the Jx.Object code to do this but it is doable. The only problem with using this solution is that I don't yet know where else a similar problem may occur and we don't want to just randomly decouple classes from Object or Widget since that woudl defeat the purpose of having them (obviously).
 
2) Change the JxLib API to accomodate for being more asynchronous. For example,. in order to load Jx.Plugin in Jx.Object we need to use a require call inside Jx.Object.initPlugins() like so:

initPlugins: function(){
    require(['./plugin'], function(Plugin){
        .. the rest of the plugin init as well as everything in initialize after the call to initPlugins goes here...
   }.bind(this));
}

The issue with this is that it breaks anything that chains off of the initial class creation such as this familiar pattern:

new Button().addTo('somediv');

That code creates a problem as the new Button is not fully defined when we call .addTo() and thus puts nothing into the div.  Now, you can work around that by using the `parent` option when creating the button:

new Button({parent: 'somediv'});

and it works fine but I'm sure there are a lot of places through out the framework this pattern may occur. We could also adopt a pattern of using deffereds/promises in some places to help alleviate this but you obviously couldn't return a promise from a constructor.

So, at this point I'm open to suggestions. I also tried "preloading" object/plugin/widget but because of the way that require works it has the same problem....

Thoughts? Suggestions?

I'm kinda at a stand still until we make a decision.

Thanks,
Jon

aek

unread,
Mar 16, 2012, 11:21:09 AM3/16/12
to jx...@googlegroups.com
After reviewing a little the docs around requirejs I think that the solution remains in the proper use of "exports" args while using define/require in modules.

Jx.Object:
define('jx/object',function(require, exports, module){

should be:
define('jx/object',['require', 'exports', 'module']function(require, exports, module){

RequireJs docs says that this is the way to solve circular dependencies

Paul Spencer

unread,
Mar 16, 2012, 11:49:05 AM3/16/12
to jx...@googlegroups.com, jx...@googlegroups.com
While that may work, I think circular dependencies in general indicate architectural flaws or weaknesses.  I'd like to review the code and see if it can be refactored.

Sent from my iPhone
--
You received this message because you are subscribed to the Google Groups "JxLib" group.
To view this discussion on the web visit https://groups.google.com/d/msg/jxlib/-/0FTreaSMI8MJ.
To post to this group, send email to jx...@googlegroups.com.
To unsubscribe from this group, send email to jxlib+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/jxlib?hl=en.

Jon Bomgardner

unread,
Mar 16, 2012, 1:30:08 PM3/16/12
to jx...@googlegroups.com
actually, the key for that was in using a second require() call inside the original... just changing the define signature won't fix it.... I'm with Paul on this and think we need to look at why we have these circular dependencies and see if they can just be eliminated somehow.
 
Jon

Jon Bomgardner

unread,
Mar 16, 2012, 1:31:13 PM3/16/12
to jx...@googlegroups.com
Paul,
 
I don't have my most recent attempts at this in github anywhere... I'll try to get them up loaded this weekend sometime.
 
Thanks,
Jon
To unsubscribe from this group, send email to jxlib+unsubscribe@googlegroups.com.

Jon Bomgardner

unread,
Mar 16, 2012, 1:40:07 PM3/16/12
to jx...@googlegroups.com
The other thing I realized is with the upcoming changes for Themeing we're going to need to adjust API anyway as the ThemeManager requests for resources (HTML, CSS) will end up being async anyway...
 
Jon

On Friday, March 16, 2012 8:49:05 AM UTC-7, pagameba wrote:
To unsubscribe from this group, send email to jxlib+unsubscribe@googlegroups.com.

Paul Spencer

unread,
Mar 16, 2012, 3:34:07 PM3/16/12
to jx...@googlegroups.com
So other than init being called by initialize, which gives us some house keeping around setOptions and the locale stuff, does Jx.Plugin get anything else from Jx.Object?

I propose that the Jx.Object initailize method be reimplemented within Jx.Plugin and perhaps streamlined (can plugins have plugins?), and we introduce a new mixin class called maybe lang.js that is required by both and is used in the Implements.

We could also refactor the options initialization into a separate mixin if you are worried about that being duplicated between the two.

Does this make any sense?

Cheers

Paul

To view this discussion on the web visit https://groups.google.com/d/msg/jxlib/-/S7s_8DRzonUJ.

To post to this group, send email to jx...@googlegroups.com.
To unsubscribe from this group, send email to jxlib+un...@googlegroups.com.

Jon Bomgardner

unread,
Mar 16, 2012, 4:54:49 PM3/16/12
to jx...@googlegroups.com
Paul,
 
I would definitely like to see us keep the duplication of code down to a minimum so I'll look at creating a couple of mixins that both could use (lang and options).
 
That will solve this problem but as I pointed out in another post, we're going to run into the need to alter the API in order to do the Theme stuff due to lazy  loading of the HTML and CSS for widgets (though not f. Any thoughts on that one (perhaps a new thread might be needed?)?
 
Thanks,
Jon

Jon Bomgardner

unread,
Mar 19, 2012, 2:39:59 AM3/19/12
to jx...@googlegroups.com
So, I've been rewritting all weekend... changed from CommonJS syntax to standard AMD... add those 2 mixins, (may have 1 or 2 more to do) and am still debugging to make sure the Global and require versions will work.  Hopefully will have something in master in the next day or 3.

Jon

On Friday, March 16, 2012 12:34:07 PM UTC-7, pagameba wrote:

Paul Spencer

unread,
Mar 19, 2012, 8:01:44 AM3/19/12
to jx...@googlegroups.com
Awesome work Jon.

To view this discussion on the web visit https://groups.google.com/d/msg/jxlib/-/YyEKp5tTR_cJ.

To post to this group, send email to jx...@googlegroups.com.
To unsubscribe from this group, send email to jxlib+un...@googlegroups.com.

Jon Bomgardner

unread,
Mar 25, 2012, 11:18:40 AM3/25/12
to jx...@googlegroups.com
Just committed changes to master to get back to basic AMD syntax. Everything works fine in "global mode" and I still need to do some more testing in require mode. There is one caveat to using this in require mode -- your top-level require must be sure to load all plugins, objects, and adaptors that you may need or you will likely see exceptions thrown by require.
Reply all
Reply to author
Forward
0 new messages