Skip to content

Commit

Permalink
Improve “real app” booting
Browse files Browse the repository at this point in the history
This is a series of improvements made to get Skylight booting on this
branch:

* Implement the new version of `makeBoundHelper` semantics and the
  param `apply`ing behavior of `Handlebars.makeBoundHelper`. The
  dependent key behavior of `makeBoundHelper` is not yet implemented.
* Implement the HTMLBars helper hooks.
* Make `each` support Ember Array-likes and falsy arrays.
* Implement partials, including bound partial names
* Support pass-through routes with no templates
* Implement a very simple `link-to` stub
* Implement a passable but not-very-good attribute bindings including
  fixing up view streams
  • Loading branch information
tilde-engineering committed Mar 1, 2015
1 parent 81a8592 commit 222dd70
Show file tree
Hide file tree
Showing 23 changed files with 136 additions and 338 deletions.
101 changes: 6 additions & 95 deletions packages/ember-htmlbars/lib/compat/make-bound-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,8 @@
@submodule ember-htmlbars
*/

import Ember from "ember-metal/core"; // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup
import { IS_BINDING } from "ember-metal/mixin";
import Helper from "ember-htmlbars/system/helper";

import Stream from "ember-metal/streams/stream";
import {
readArray,
scanArray,
scanHash,
readHash,
isStream
} from "ember-metal/streams/utils";

/**
A helper function used by `registerBoundHelper`. Takes the
provided Handlebars helper function fn and returns it in wrapped
Expand All @@ -42,88 +31,10 @@ import {
@since 1.2.0
@deprecated
*/
export default function makeBoundHelper(fn, compatMode) {
var dependentKeys = [];
for (var i = 1; i < arguments.length; i++) {
dependentKeys.push(arguments[i]);
}

function helperFunc(params, hash, options, env) {
var view = this;
var numParams = params.length;
var param;

Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.template);

for (var prop in hash) {
if (IS_BINDING.test(prop)) {
hash[prop.slice(0, -7)] = view.getStream(hash[prop]);
delete hash[prop];
}
}

function valueFn() {
var args = readArray(params);
var properties = new Array(params.length);
for (var i = 0, l = params.length; i < l; i++) {
param = params[i];

if (isStream(param)) {
properties[i] = param._label;
} else {
properties[i] = param;
}
}

args.push({
hash: readHash(hash),
data: { properties: properties }
});
return fn.apply(view, args);
}

// If none of the hash parameters are bound, act as an unbound helper.
// This prevents views from being unnecessarily created
var hasStream = scanArray(params) || scanHash(hash);
if (hasStream) {
var lazyValue = new Stream(valueFn);

for (i = 0; i < numParams; i++) {
param = params[i];
if (isStream(param)) {
param.subscribe(lazyValue.notify, lazyValue);
}
}

for (prop in hash) {
param = hash[prop];
if (isStream(param)) {
param.subscribe(lazyValue.notify, lazyValue);
}
}

if (numParams > 0) {
var firstParam = params[0];
// Only bother with subscriptions if the first argument
// is a stream itself, and not a primitive.
if (isStream(firstParam)) {
var onDependentKeyNotify = function onDependentKeyNotify(stream) {
stream.value();
lazyValue.notify();
};
for (i = 0; i < dependentKeys.length; i++) {
var childParam = firstParam.get(dependentKeys[i]);
childParam.value();
childParam.subscribe(onDependentKeyNotify);
}
}
}

return lazyValue;
} else {
return valueFn();
}
}

return new Helper(helperFunc);
export default function makeBoundHelper(fn) {
return new Helper(function(params, hash, templates) {
var args = params.slice();
args.push({ hash: hash, templates: templates });
fn.apply(undefined, args);
});
}
10 changes: 8 additions & 2 deletions packages/ember-htmlbars/lib/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import getValue from "ember-htmlbars/hooks/get-value";
import cleanup from "ember-htmlbars/hooks/cleanup";
import classify from "ember-htmlbars/hooks/classify";
import component from "ember-htmlbars/hooks/component";
import lookupHelper from "ember-htmlbars/hooks/lookup-helper";
import hasHelper from "ember-htmlbars/hooks/has-helper";

import helpers from "ember-htmlbars/helpers";

Expand All @@ -38,7 +40,9 @@ merge(emberHooks, {
concat: concat,
cleanup: cleanup,
classify: classify,
component: component
component: component,
lookupHelper: lookupHelper,
hasHelper: hasHelper
});

import debuggerKeyword from "ember-htmlbars/keywords/debugger";
Expand All @@ -47,14 +51,16 @@ import outlet from "ember-htmlbars/keywords/outlet";
import unbound from "ember-htmlbars/keywords/unbound";
import view from "ember-htmlbars/keywords/view";
import componentKeyword from "ember-htmlbars/keywords/component";
import partial from "ember-htmlbars/keywords/partial";

merge(emberHooks.keywords, {
"debugger": debuggerKeyword,
"with": withKeyword,
outlet: outlet,
unbound: unbound,
view: view,
component: componentKeyword
component: componentKeyword,
partial: partial
});

export default {
Expand Down
16 changes: 10 additions & 6 deletions packages/ember-htmlbars/lib/helpers/each.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { guidFor } from "ember-metal/utils";
import { get } from "ember-metal/property_get";
import { forEach } from "ember-metal/enumerable_utils";

export default function eachHelper(params, hash, blocks) {
var list = params[0];
var keyPath = hash.key;

for (var i=0, l=list.length; i<l; i++) {
var item = list[i];
var key = keyPath ? get(item, keyPath) : guidFor(item);

this.yieldItem(key, [item, i]);
// TODO: Correct falsy semantics
if (!list) {
if (blocks.inverse.yield) { blocks.inverse.yield(); }
return;
}

forEach(list, function(item, i) {
var key = keyPath ? get(item, keyPath) : String(i);
blocks.template.yieldItem(key, [item, i]);
});
}
2 changes: 1 addition & 1 deletion packages/ember-htmlbars/lib/hooks/bind-self.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { get } from "ember-metal/property_get";
import updateScope from "ember-htmlbars/utils/update-scope";

export default function bindSelf(scope, self) {
export default function bindSelf(env, scope, self) {
Ember.assert("BUG: scope.attrs and self.isView should not both be true", !(scope.attrs && self.isView));

if (self.isView) {
Expand Down
1 change: 1 addition & 0 deletions packages/ember-htmlbars/lib/hooks/create-shadow-scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

export default function createShadowScope(env, parentScope, options) {
var shadowScope = env.hooks.createFreshScope();
if (!options) { return shadowScope; }

if (options.view) {
shadowScope.renderNode = options.renderNode;
Expand Down
27 changes: 7 additions & 20 deletions packages/ember-htmlbars/lib/hooks/get-root.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
@submodule ember-htmlbars
*/

//import Ember from "ember-metal/core";
import Stream from "ember-metal/streams/stream";
import { read } from "ember-metal/streams/utils";
import Ember from "ember-metal/core";

export default function getRoot(scope, key) {
if (key === 'this') {
Expand Down Expand Up @@ -33,21 +31,10 @@ function getKey(scope, key) {
var value = self[key];
if (value !== undefined) { return value; }

var viewKey = scope.locals.view.getKey(key);

var stream = new Stream(function() {
var attrs = scope.attrs;

if (key in attrs) {
Ember.deprecate("You accessed the `" + key + "` attribute directly. Please use `attrs." + key + "` instead.");
return read(attrs[key]);
}

return read(viewKey);
});

stream.addDependency(viewKey);
stream.addDependency(scope.attrs[key]);

return stream;
if (key in scope.attrs) {
Ember.deprecate("You accessed the `" + key + "` attribute directly. Please use `attrs." + key + "` instead.");
return scope.attrs[key];
} else {
return scope.locals.view.getKey(key);
}
}
5 changes: 5 additions & 0 deletions packages/ember-htmlbars/lib/hooks/has-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import lookupHelper from "ember-htmlbars/system/lookup-helper";

export default function hasHelperHook(env, scope, helperName) {
return !!lookupHelper(helperName, scope.self, env);
}
5 changes: 5 additions & 0 deletions packages/ember-htmlbars/lib/hooks/lookup-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import lookupHelper from "ember-htmlbars/system/lookup-helper";

export default function lookupHelperHook(env, scope, helperName) {
return lookupHelper(helperName, scope.self, env);
}
8 changes: 8 additions & 0 deletions packages/ember-htmlbars/lib/keywords/outlet.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import merge from "ember-metal/merge";
import ShadowRoot from "ember-htmlbars/system/shadow-root";

import topLevelViewTemplate from "ember-htmlbars/templates/top-level-view";
topLevelViewTemplate.revision = 'Ember@VERSION_STRING_PLACEHOLDER';

export default {
willRender: function(renderNode, env) {
env.view.ownerView._outlets.push(renderNode);
Expand All @@ -18,6 +21,11 @@ export default {
var outletName = read(params[0]) || 'main';
var selectedOutletState = outletState[outletName];

var toRender = selectedOutletState && selectedOutletState.render;
if (toRender && !toRender.template && !toRender.ViewClass) {
toRender.template = topLevelViewTemplate;
}

state.lastOutletState = state.outletState;
state.outletState = selectedOutletState;
},
Expand Down
17 changes: 17 additions & 0 deletions packages/ember-htmlbars/lib/keywords/partial.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
@module ember
@submodule ember-htmlbars
*/

import lookupPartial from "ember-views/system/lookup_partial";
import { internal } from "htmlbars-runtime";

export default function partialKeyword(morph, env, scope, params, hash, template, inverse, visitor) {
var found = lookupPartial(env, env.hooks.getValue(params[0])).raw;

internal.hostBlock(morph, env, scope, found, null, null, visitor, function(options) {
options.templates.template.yield();
});

return true;
}
4 changes: 2 additions & 2 deletions packages/ember-htmlbars/lib/system/lookup-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function lookupHelper(name, view, env) {
return helper;
}

var container = view.container;
var container = env.container;

if (!container || ISNT_HELPER_CACHE.get(name)) {
return;
Expand All @@ -59,5 +59,5 @@ export default function lookupHelper(name, view, env) {
container._registry.register(helperName, helper);
}

return helper;
return helper.helperFunction;
}
45 changes: 1 addition & 44 deletions packages/ember-htmlbars/lib/system/make_bound_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,8 @@
@submodule ember-htmlbars
*/

import Ember from "ember-metal/core"; // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup
import Helper from "ember-htmlbars/system/helper";

import Stream from "ember-metal/streams/stream";
import {
readArray,
readHash,
subscribe,
scanHash,
scanArray
} from "ember-metal/streams/utils";

/**
Create a bound helper. Accepts a function that receives the ordered and hash parameters
from the template. If a bound property was provided in the template it will be resolved to its
Expand Down Expand Up @@ -59,38 +49,5 @@ import {
@since 1.10.0
*/
export default function makeBoundHelper(fn) {
function helperFunc(params, hash, options, env) {
var view = this;
var numParams = params.length;
var param, prop;

Ember.assert("makeBoundHelper generated helpers do not support use with blocks", !options.template);

function valueFn() {
return fn.call(view, readArray(params), readHash(hash), options, env);
}

// If none of the hash parameters are bound, act as an unbound helper.
// This prevents views from being unnecessarily created
var hasStream = scanArray(params) || scanHash(hash);
if (hasStream) {
var lazyValue = new Stream(valueFn);

for (var i = 0; i < numParams; i++) {
param = params[i];
subscribe(param, lazyValue.notify, lazyValue);
}

for (prop in hash) {
param = hash[prop];
subscribe(param, lazyValue.notify, lazyValue);
}

return lazyValue;
} else {
return valueFn();
}
}

return new Helper(helperFunc);
return new Helper(fn);
}
1 change: 1 addition & 0 deletions packages/ember-htmlbars/lib/templates/link-to.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<a href={{href}}>{{yield}}</a>
7 changes: 7 additions & 0 deletions packages/ember-metal/lib/streams/stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ function Stream(fn) {
this.valueFn = fn;
}

var KeyStream;

Stream.prototype = {
isStream: true,

Expand All @@ -125,6 +127,11 @@ Stream.prototype = {
this.gotValueWhileInactive = false;
},

_makeChildStream: function(key) {
KeyStream = KeyStream || Ember.__loader.require('ember-views/streams/key_stream').default;
return new KeyStream(this, key);
},

removeChild: function(key) {
delete this.children[key];
},
Expand Down
Loading

0 comments on commit 222dd70

Please sign in to comment.