linkedin / dustjs

Asynchronous Javascript templating for the browser and server
http://dustjs.com
MIT License
2.91k stars 479 forks source link

support dot syntax to access objects in different levels of context #270

Closed carchrae closed 11 years ago

carchrae commented 11 years ago

update: this description is now out of date. the issue is how Context.getPath searches through context.

update2: if you want to use this right now, include this gist after including dust.js https://gist.github.com/carchrae/5825054


global variable access does not support objects well because it uses globals[key] to retrieve the value. See: https://github.com/linkedin/dustjs/blob/master/lib/dust.js#L153

this leads to confusing (to developer) behavior - and is inconsistent with the behavior of local context variables (which can use . to access fields.)

for example, if i store a global "foo"={ bar : 123 } and then try to access it in a template, as {foo.bar} it is undefined. "foo" exists but cannot be used with expressions like {?foo.bar} and so on.

my suggestion is you roll out the key access. so, something like

var result = global;
for (keyPart in key.split(".")){ 
    if (result){
       result = result[keyPart];
    } else {
       return undefined;
    }
}
return result;
rragan commented 11 years ago

See https://github.com/linkedin/dustjs/pull/174#issuecomment-15903945. If Julian completes what the suggests dealing with in this comment, then I think objects in global will then work

carchrae commented 11 years ago

thanks for directing me to that PR. i did not realize that the . notation was not working in regular contexts either - although it is clear by looking at the code i linked to above.

so, the above code fragment should be applied to https://github.com/linkedin/dustjs/blob/master/lib/dust.js#L146 as well:

Context.prototype.get = function(key) {
  var ctx = this.stack, value;

  while(ctx) {
    if (ctx.isObject) {
      value = ctx.head[key];
      if (!(value === undefined)) {
        return value;
      }
    }
    ctx = ctx.tail;
  }
  return this.global ? this.global[key] : undefined;
};

becomes

Context.prototype.get = function(key) {
  var ctx = this.stack, value;

  while(ctx) {
    if (ctx.isObject) {
      value = getDotPath(ctx.head,key);
      if (!(value === undefined)) {
        return value;
      }
    }
    ctx = ctx.tail;
  }
  return this.global ? getDotPath(this.global,key) : undefined;
};

where getDotPath is the code i posted above

carchrae commented 11 years ago

and sure, you might split the key before the ctx stack search for performance.

also, it is arguable if it should stop if it finds a partial match (not complete path) in the ctx stack search. eg, if the current context has { a : { b : 2 } } and a parent context has { a : { c : 'here it is' } } and you are looking for {a.c}.

carchrae commented 11 years ago

the code in the PR seems to work well. btw, please give feedback on the style of adding the helper fuction dotGet - is this the correct way to add it, or should it be part of the prototype? (function does not need this, so adding to prototype seemed wrong)

carchrae commented 11 years ago

while i have a working fix to this that passes all tests, i think the solution to this should be in the compiler where it should detect a path - as it does in a scope context reference. (edit: i'm actually a bit confused why this happened at all - perhaps the bug i saw with a global {?foo.bar} is actually a bug elsewhere that did not detect the path correctly!)

rragan commented 11 years ago

A reference like {#arr} {a.b} {/obj} may work if arr contains a.b at runtime or may need to climb up the stack if it does not. How can the compiler help?

carchrae commented 11 years ago

In the case of a context reference, the compiler should generate "ctx.getPath" when there is a path (eg, "this.is.a.path". For some reason, it did not when I accessed a global, I think when passed as a parameter - I'll see if I can make this case appear again - clearly it should be a test case.

On Mon, Jun 3, 2013 at 3:13 PM, Richard Ragan notifications@github.comwrote:

A reference like {#arr} {a.b} {/obj} may work if arr contains a.b at runtime or may need to climb up the stack if it does not. How can the compiler help?

— Reply to this email directly or view it on GitHubhttps://github.com/linkedin/dustjs/issues/270#issuecomment-18876678 .

vybs commented 11 years ago

271 merged.