shindig-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Ryan J Baxter" <rjbax...@us.ibm.com>
Subject Re: javascript readability..
Date Tue, 02 Aug 2011 01:15:45 GMT
+1, once the feature is ready of course.

-Ryan

Email: rjbaxter@us.ibm.com
Phone: 978-899-3041
developerWorks Profile



From:   Henry Saputra <henry.saputra@gmail.com>
To:     dev@shindig.apache.org, 
Date:   08/01/2011 09:13 PM
Subject:        Re: javascript readability..



I think a wiki entry for this exportJs feature would greatly help
Shindig community to understand more about design and the impl chosen.

- Henry

On Mon, Aug 1, 2011 at 5:54 PM, John Hjelmstad <fargo@google.com> wrote:
> Hey Jesse:
>
> We haven't loudly advertised exportJs() yet since we've been adding a 
final
> piece of functionality to it for deferred symbol binding, and because
> Shindig (on account of it being still by default on JDK 1.5 if nothing 
else)
> doesn't use closure compiled JS by default yet.
>
> For context, deferred symbol binding applies to features consisting of
> methods that return nothing synchronously. Synthetic example:
>
> // namespace setup here
> gadgets.foo.doSomething = function(key, value, callback) {
>  // do something with key and value
>  callback(somethingComputed);
> };
>
> Deferred symbol binding allows us to emit a tiny JS loader "stub" that 
just
> enqueues arguments to a method with this name. That stub -- actually the
> async JS loader -- then loads the payload, which when loaded executes 
the
> real method.
>
> This allows fast integration of code via:
> <script src="http://myserver.com/gadgets/js/foofeature.js"></script>
> <script>
>  gadgets.foo.doSomething('myKey', 'myValue', function(data) { /* 
myCallback
> */ });
> </script>
>
> ...for syntactic convenience while maintaining asynchronous loading
> semantics for latency purposes (ie. not blocking the page load).
>
> The functionality overall is activated by the flag supportDefer="true" 
in
> feature.xml, and server-side configuration for same. Without it, the 
above
> syntax is not activated and things work as before.
>
> I implemented a version of this with both deferred and full symbol 
binding
> in exportJs() but found a few shortcomings with the approach. we're
> rejiggering it today. I'd hold off for a short time while we do so to 
avoid
> merge conflicts. The relevant JS CL is: 
https://reviews.apache.org/r/1237/
>
> Cheers,
> John
> PS. Time will tell whether I regret it, but thanks for the props on the
> method overall :) It's hard to measure precisely how valuable the 
technique
> is since much of its value is preventative, ie. A) disallowing use of 
helper
> methods as if they were supported APIs, and B) prevents symbol
> clashes/occlusion with arbitrary contexts in which "our" JS is imported 
--
> hopefully all while allowing a coding style that "feels" as if none of 
this
> edifice exists.
>
> On Fri, Jul 29, 2011 at 7:09 AM, Ciancetta, Jesse E. 
<jcian@mitre.org>wrote:
>
>> 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:fargo@google.com]
>> >Sent: Thursday, July 28, 2011 5:58 PM
>> >To: dev@shindig.apache.org
>> >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. <jcian@mitre.org
>> >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:mhermanto@gmail.com]
>> >> >Sent: Tuesday, July 26, 2011 6:15 PM
>> >> >To: dev@shindig.apache.org
>> >> >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 <jasvir@google.com>
>> >> >
>> >> >> 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 
<fargo@google.com>
>> >> >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 
<ddumont@us.ibm.com>
>> >> >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 <fargo@google.com>
>> >> >> > > To:     dev@shindig.apache.org,
>> >> >> > > 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 <
>> rjbaxter@us.ibm.com
>> >> >
>> >> >> > > wrote:
>> >> >> > >
>> >> >> > > > +1 As well, I think its easier to read.
>> >> >> > > >
>> >> >> > > > -Ryan
>> >> >> > > >
>> >> >> > > > Email: rjbaxter@us.ibm.com
>> >> >> > > > Phone: 978-899-3041
>> >> >> > > > developerWorks Profile
>> >> >> > > >
>> >> >> > > >
>> >> >> > > >
>> >> >> > > > From:   Dan Dumont/Westford/IBM@Lotus
>> >> >> > > > To:     dev@shindig.apache.org,
>> >> >> > > > Date:   07/26/2011 09:00 AM
>> >> >> > > > Subject:        Re: javascript readability..
>> >> >> > > >
>> >> >> > > >
>> >> >> > > >
>> >> >> > > > +1
>> >> >> > > >
>> >> >> > > >
>> >> >> > > >
>> >> >> > > > From:   Paul Lindner <lindner@inuus.com>
>> >> >> > > > To:     dev@shindig.apache.org,
>> >> >> > > > 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 -- lindner@inuus.com -- 
linkedin.com/in/plindner
>> >> >> > > >
>> >> >> > > >
>> >> >> > > >
>> >> >> > > >
>> >> >> > > >
>> >> >> > > >
>> >> >> > > >
>> >> >> > >
>> >> >> > >
>> >> >> > >
>> >> >> > >
>> >> >> >
>> >> >>
>> >>
>>
>




Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message