Hey John,
I hadn’t seen the exportJs support before (I haven’t done any work with
features in a long time) -- that does look very similar! (and as an aside --
hats off to whoever came up with that scheme to be able to closure compile JS
in isolation but still have fully qualified symbols available in the global
space -- its absolutely brilliant)
I wrote a quick test gadget which required feature exportjs and did some
testing -- and it comes pretty close to doing everything we'd need already, so
I don’t see any reason why it couldn’t be augmented to handle all the cases my
helper function does. Then it'd just be a matter of making it part of the core
feature set so it would be available for use in any features that wanted to
take advantage of it.
So I guess that comes back to Paul's original question though -- if enough
people preferred the first style Paul proposed I'd be happy to take a crack at
augmenting exportJs to support this use case. So we'd end up with JS that
looked something like this:
//could be from foo-utils.js
exportJs("shindig.util", (function() {
var privateData = "this is private";
var privateFunction = function() {
console.log(privateData);
};
var calculateFoo = function() {
privateFunction();
};
var doAlotOfFoo = function() {
//do alot of foo
};
return {
calculateFoo: calculateFoo,
doAlotOfFoo: doAlotOfFoo
};
})());
//could be from bar-utils.js
exportJs("shindig.util", (function() {
var privateFunction = function() {
console.log("calculateBar called");
};
var calculateBar = function() {
privateFunction();
};
return {
publicData: "this is public",
calculateBar: calculateBar
};
})());
exportJs("shindig.some.emptyNamespace", {});
shindig.some.emptyNamespace.xyz = "ABC";
--Jesse
>-----Original Message-----
>From: John Hjelmstad [mailto:[email protected]]
>Sent: Thursday, July 28, 2011 5:58 PM
>To: [email protected]
>Subject: Re: javascript readability..
>
>Hey Jesse,
>
>Interesting idea here -- this sounds suspiciously akin to the functionality
>already present in exportJs(...) used for exporting symbols. This method
>upserts the namespace in much the same way as your helper method does.
>Thoughts on whether we could reuse or augment that?
>
>-j
>
>On Thu, Jul 28, 2011 at 7:52 AM, Ciancetta, Jesse E. <[email protected]>wrote:
>
>> I agree with John -- I also prefer the first style Paul presented (which I
>> believe is also the most prevalent style in Shindig currently).
>>
>> With respect to the issues that Michael raised about building up a
>> namespace from across different files -- this got me thinking about a little
>> namespace utility a colleague of mine wrote a while back. That utility just
>> took a target namespace like "shindig.util.foo", created an empty object in
>> it (without clobbering any parts of the target namespace that may have
>> already existed) and returned it -- so you could then do something like
>> shindig.util.foo.x = ... directly without needing to create each piece of
>> the namespace manually.
>>
>> And with some (well, a lot) of tweaking I was able to turn that function
>> into a more general purpose namespacing function that takes an optional
>> object to place into the target namespace -- and if the target namespace
>> already exists it takes the properties of the optional object and appends
>> them to the existing namespace.
>>
>> So I think if enough people preferred the first style Paul presented,
>> something like this could be used to work around the issues Michael raised.
>> Here is the function and some sample usages (with the caveat that I haven’t
>> actually used this for anything real so there could be some bugs in it):
>>
>> var shindig = shindig || (function() {
>> var namespace = function(targetNamespace, optObject) {
>> //get a reference to the global window object
>> var object = window;
>>
>> //if no object was provided to put into the namespace create an
>> empty one
>> optObject = optObject || {};
>>
>> //split the namespace into its parts
>> var namespaces = targetNamespace.split(".");
>>
>> //for each part of the namespace
>> for (var i = 0; i < namespaces.length; i++) {
>> //see if this part is already defined
>> if (typeof object[namespaces[i]] === "undefined") {
>> //if its not defined already -- put an object into it
>> //if we're at the end of the targetNamespace, put the new
>> object into it,
>> //otherwise drop in an empty object as a placeholder
>> object[namespaces[i]] = (i + 1) == namespaces.length ?
>> optObject : {};
>> } else {
>> //there is already something here -- see if we're at the end
>> of the targetNamespace
>> if ((i + 1) == namespaces.length) {
>> //make sure its an object we can append to
>> if (typeof object[namespaces[i]] === "object") {
>> //go ahead and add all the public properties on
>> optObject to the target namespace
>> for (var property in optObject) {
>> object[namespaces[i]][property] =
>> optObject[property];
>> }
>> } else {
>> throw "Cannot append new properties to object of
>> type " + typeof object[namespaces[i]];
>> }
>> }
>> }
>>
>> object = object[namespaces[i]];
>> }
>>
>> return object;
>> };
>>
>> return {
>> namespace: namespace
>> };
>> })();
>>
>> //could be from foo-utils.js
>> shindig.namespace("shindig.util", (function() {
>> var privateData = "this is private";
>>
>> var privateFunction = function() {
>> console.log(privateData);
>> };
>>
>> var calculateFoo = function() {
>> privateFunction();
>> };
>>
>> var doAlotOfFoo = function() {
>> //do alot of foo
>> };
>>
>> return {
>> calculateFoo: calculateFoo,
>> doAlotOfFoo: doAlotOfFoo
>> };
>> })());
>>
>> //could be from bar-utils.js
>> shindig.namespace("shindig.util", (function() {
>> var privateFunction = function() {
>> console.log("calculateBar called");
>> };
>>
>> var calculateBar = function() {
>> privateFunction();
>> };
>>
>> return {
>> publicData: "this is public",
>> calculateBar: calculateBar
>> };
>> })());
>>
>> shindig.namespace("shindig.some.emptyNamespace");
>> shindig.some.emptyNamespace.xyz = "ABC";
>>
>> >-----Original Message-----
>> >From: Michael Hermanto [mailto:[email protected]]
>> >Sent: Tuesday, July 26, 2011 6:15 PM
>> >To: [email protected]
>> >Subject: Re: javascript readability..
>> >
>> >What didn't work for me with --
>> > shindig.foo = function() {
>> > return { 'bar': ... };
>> > }();
>> >... is that methods in the same namespace have to be all
>> >defined/implemented
>> >in one file.
>> >
>> >ie: it's fine if all gadgets.rpc.* is implemented in a file rpc.js, but
>> not
>> >fine when gadgets.util.* are implemented across different files/features
>> >across core.util.dom, core.util.string, etc. When feature core.util pulls
>> >all these files in, it will effectively stomp all the previously-defined
>> >core.util.*.
>> >
>> >The tendency has been to make libraries smaller and more focussed to
>what
>> >they do, ie: this means breaking up features into sub-features. To do so,
>> I
>> >had to convert more-focussed core.util.[dom|string|...] to fully-defined
>> >names. Unless the above style (with shindig.foo) doesn't have this
>> >limitation (ie: can incrementally define more methods on top what has
>been
>> >defined), I personally prefer the fully-defined names, for consistency.
>> >
>> >
>> >
>> >2011/7/26 ๏̯͡๏ Jasvir Nagra <[email protected]>
>> >
>> >> I don't have a strong preference either way although it's nice when
>> >> reviewing code to have all the exported things exported once when they
>> >> share
>> >> a scope full of private helpers and other state. It also helps convey
>> when
>> >> the setup of a particular part of the object graph is done.
>> >> Eg.
>> >>
>> >> shindig.foo = (function() {
>> >> // ... all the helper functions and shared state ...
>> >> ...
>> >> return { bar: bar, baz: baz };
>> >> }();
>> >> // at this point I know from convention that shindig.foo is done being
>> >> setup
>> >> and will have just "bar" and "baz"
>> >>
>> >> vs.
>> >>
>> >> shindig.foo.bar = function() { ... }
>> >> /// ... a lot of code later
>> >> shindig.foo.baz = function() { ... }
>> >>
>> >> If the amount of code between shindig.foo and the return is long, I'd
>> >> suggest another alternative that I think has the advantage of both:
>> >>
>> >> (function() {
>> >> // ... all the helper functions and shared state ...
>> >>
>> >> shindig.foo = {
>> >> bar: bar,
>> >> baz: baz
>> >> }
>> >> })();
>> >>
>> >> On Tue, Jul 26, 2011 at 2:19 PM, John Hjelmstad <[email protected]>
>> >wrote:
>> >>
>> >> > With the model we're using with exportJs, you actually can't as easily
>> do
>> >> > that or wrap "singleton"/namespaced items, unless you . exportJs(...)
>> is
>> >> > injected after the closure.
>> >> >
>> >> > function() {
>> >> > foo.bar.baz = function() { }
>> >> > }();
>> >> > exportJs("foo.bar", [foo,foo.bar], {baz:"baz"});
>> >> >
>> >> > Of course, you can if you also update the style guide to prepend
>> window:
>> >> >
>> >> > function() {
>> >> > window.foo.bar.baz = function() { }
>> >> > }();
>> >> >
>> >> > ...though that requirement seems a little awkward and verbose to me.
>> >> >
>> >> > --j
>> >> >
>> >> > On Tue, Jul 26, 2011 at 2:12 PM, Dan Dumont <[email protected]>
>> >wrote:
>> >> >
>> >> > > As mentioned by Paul before you can define:
>> >> > >
>> >> > > function(){
>> >> > > FooClass.prototype.method = function() { }
>> >> > > FooClass.prototype.method2 = function() { }
>> >> > > }();
>> >> > >
>> >> > > to get a local scope.
>> >> > >
>> >> > > I think this makes it easier to audit what must be included in an
>> >> export.
>> >> > > And when you come up for air soon, maybe we can talk about AMD
>> >format
>> >> and
>> >> > > what that brings to the table. :)
>> >> > >
>> >> > >
>> >> > >
>> >> > > From: John Hjelmstad <[email protected]>
>> >> > > To: [email protected],
>> >> > > Date: 07/26/2011 04:43 PM
>> >> > > Subject: Re: javascript readability..
>> >> > >
>> >> > >
>> >> > >
>> >> > > I still prefer status quo, as it reads more like a proper class to
>> me,
>> >> > > while
>> >> > > being less verbose and centralizing the exported method definitions
>> in
>> >> a
>> >> > > single place.
>> >> > >
>> >> > > As well, this question's corollary is whether to convert all
>> >> instantiable
>> >> > > objects to the form:
>> >> > >
>> >> > > FooClass.prototype.method = function() { }
>> >> > > FooClass.prototype.method2 = function() { }
>> >> > >
>> >> > > ...from:
>> >> > > FooClass = function() {
>> >> > > // private state
>> >> > > function method() { }
>> >> > > function method2() { }
>> >> > > return {
>> >> > > method: method,
>> >> > > method2: method2
>> >> > > };
>> >> > > };
>> >> > >
>> >> > > On this note, I'm conflicted. I like having actual private state,
>> but
>> >> > > prototype-style is more efficient.
>> >> > >
>> >> > > Enough people have complained over time about each of the existing
>> >> idioms
>> >> > > though that I suppose I could go the other direction, if it's
>> causing
>> >> > > development trouble.
>> >> > >
>> >> > > --j
>> >> > >
>> >> > > On Tue, Jul 26, 2011 at 6:17 AM, Ryan J Baxter <[email protected]
>> >
>> >> > > wrote:
>> >> > >
>> >> > > > +1 As well, I think its easier to read.
>> >> > > >
>> >> > > > -Ryan
>> >> > > >
>> >> > > > Email: [email protected]
>> >> > > > Phone: 978-899-3041
>> >> > > > developerWorks Profile
>> >> > > >
>> >> > > >
>> >> > > >
>> >> > > > From: Dan Dumont/Westford/IBM@Lotus
>> >> > > > To: [email protected],
>> >> > > > Date: 07/26/2011 09:00 AM
>> >> > > > Subject: Re: javascript readability..
>> >> > > >
>> >> > > >
>> >> > > >
>> >> > > > +1
>> >> > > >
>> >> > > >
>> >> > > >
>> >> > > > From: Paul Lindner <[email protected]>
>> >> > > > To: [email protected],
>> >> > > > Date: 07/26/2011 02:51 AM
>> >> > > > Subject: javascript readability..
>> >> > > >
>> >> > > >
>> >> > > >
>> >> > > > Hi,
>> >> > > >
>> >> > > > I'm curious to know what people think about some of the idioms in
>> the
>> >> > JS
>> >> > > > code you find in shindig. There's an awful lot of stuff like
>> this:
>> >> > > >
>> >> > > > shindig.foo = function(){
>> >> > > > //...
>> >> > > > var myFunction = function() {
>> >> > > > }
>> >> > > >
>> >> > > > return {'foo': myFunction,
>> >> > > > 'bar': function() {
>> >> > > > return 'bar';
>> >> > > > }};
>> >> > > > }();
>> >> > > >
>> >> > > >
>> >> > > > Just search for @member to see the various places.
>> >> > > >
>> >> > > > What would people think if we moved to fully defined names for
>> >> > > > function/method definitions instead?
>> >> > > >
>> >> > > > You could still wrap this inside a closure if you wanted local
>> scope:
>> >> > > >
>> >> > > > function() {
>> >> > > > shindig.foo.foo = function() {
>> >> > > > ...
>> >> > > > }
>> >> > > > shindig.foo.bar = function() {
>> >> > > > ...
>> >> > > > }
>> >> > > > }();
>> >> > > >
>> >> > > > --
>> >> > > > Paul Lindner -- [email protected] -- linkedin.com/in/plindner
>> >> > > >
>> >> > > >
>> >> > > >
>> >> > > >
>> >> > > >
>> >> > > >
>> >> > > >
>> >> > >
>> >> > >
>> >> > >
>> >> > >
>> >> >
>> >>
>>