Skip to content

Commit

Permalink
Make the env more generally consistent (JIT)
Browse files Browse the repository at this point in the history
Previously, the `env` was a bag of properties, and got frequently
extended or modified (in largely predictable ways).

The consequence is that even though the environment is conceptually a
single concept, it got treated internally as a megamorphic shapeless
entity. For what it’s worth, this JIT confusion was somewhat mirrored
by a confusion when reading the code: it was never precisely clear what
the `env` would look like from the perspective of a human reader.

This commit changes the environment to be a single class. There are only
two kinds of child environments (a change in the `view` and a change to
`outletState`), and there are now methods on `env` to make that simple
and predictable.

The consequence is that all methods that take an `env` now go down a
fast path when accessing it, and human readers of the code can feel
confident about what the environment looks like.
  • Loading branch information
tomdale committed Jun 4, 2015
1 parent b902083 commit d1d993d
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 29 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"express": "^4.5.0",
"github": "^0.2.3",
"glob": "~4.3.2",
"htmlbars": "0.13.22",
"htmlbars": "0.13.25",
"qunit-extras": "^1.3.0",
"qunitjs": "^1.16.0",
"route-recognizer": "0.1.5",
Expand Down
4 changes: 2 additions & 2 deletions packages/ember-htmlbars/lib/keywords/real_outlet.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export default {
return { outletState: selectedOutletState, hasParentOutlet: env.hasParentOutlet };
},

childEnv(state) {
return { outletState: state.outletState && state.outletState.outlets, hasParentOutlet: true };
childEnv(state, env) {
return env.childWithOutletState(state.outletState && state.outletState.outlets, true);
},

isStable(lastState, nextState) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,7 @@ ComponentNodeManager.prototype.render = function(_env, visitor) {
var { component, attrs } = this;

return instrument(component, function() {
let env = _env;

env = assign({ view: component }, env);
let env = _env.childWithView(component);

var snapshot = takeSnapshot(attrs);
env.renderer.componentInitAttrs(this.component, snapshot);
Expand Down Expand Up @@ -210,7 +208,7 @@ ComponentNodeManager.prototype.rerender = function(_env, attrs, visitor) {
var snapshot = takeSnapshot(attrs);

if (component._renderNode.shouldReceiveAttrs) {
env.renderer.componentUpdateAttrs(component, component.attrs, snapshot);
env.renderer.componentUpdateAttrs(component, snapshot);

if (!component._isAngleBracket) {
setProperties(component, mergeBindings({}, shadowedAttrs(component, snapshot)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ ViewNodeManager.prototype.render = function(env, attrs, visitor) {

var newEnv = env;
if (component) {
newEnv = merge({}, env);
newEnv.view = component;
newEnv = env.childWithView(component);
}

if (component) {
Expand Down Expand Up @@ -124,8 +123,7 @@ ViewNodeManager.prototype.rerender = function(env, attrs, visitor) {
return instrument(component, function() {
var newEnv = env;
if (component) {
newEnv = merge({}, env);
newEnv.view = component;
newEnv = env.childWithView(component);

var snapshot = takeSnapshot(attrs);

Expand Down
56 changes: 56 additions & 0 deletions packages/ember-htmlbars/lib/system/render-env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import defaultEnv from "ember-htmlbars/env";

export default function RenderEnv(options) {
this.lifecycleHooks = options.lifecycleHooks || [];
this.renderedViews = options.renderedViews || [];
this.renderedNodes = options.renderedNodes || {};
this.hasParentOutlet = options.hasParentOutlet || false;

this.view = options.view;
this.outletState = options.outletState;
this.container = options.container;
this.renderer = options.renderer;
this.dom = options.dom;

this.hooks = defaultEnv.hooks;
this.helpers = defaultEnv.helpers;
this.useFragmentCache = defaultEnv.useFragmentCache;
}

RenderEnv.build = function(view) {
return new RenderEnv({
view: view,
outletState: view.outletState,
container: view.container,
renderer: view.renderer,
dom: view.renderer._dom
});
};

RenderEnv.prototype.childWithView = function(view) {
return new RenderEnv({
view: view,
outletState: this.outletState,
container: this.container,
renderer: this.renderer,
dom: this.dom,
lifecycleHooks: this.lifecycleHooks,
renderedViews: this.renderedViews,
renderedNodes: this.renderedNodes,
hasParentOutlet: this.hasParentOutlet
});
};

RenderEnv.prototype.childWithOutletState = function(outletState, hasParentOutlet=this.hasParentOutlet) {
return new RenderEnv({
view: this.view,
outletState: outletState,
container: this.container,
renderer: this.renderer,
dom: this.dom,
lifecycleHooks: this.lifecycleHooks,
renderedViews: this.renderedViews,
renderedNodes: this.renderedNodes,
hasParentOutlet: hasParentOutlet
});
};
16 changes: 2 additions & 14 deletions packages/ember-htmlbars/lib/system/render-view.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
import defaultEnv from "ember-htmlbars/env";
import ViewNodeManager, { createOrUpdateComponent } from "ember-htmlbars/node-managers/view-node-manager";
import RenderEnv from "ember-htmlbars/system/render-env";

// This function only gets called once per render of a "root view" (`appendTo`). Otherwise,
// HTMLBars propagates the existing env and renders templates for a given render node.
export function renderHTMLBarsBlock(view, block, renderNode) {
var env = {
lifecycleHooks: [],
renderedViews: [],
renderedNodes: {},
view: view,
outletState: view.outletState,
container: view.container,
renderer: view.renderer,
dom: view.renderer._dom,
hooks: defaultEnv.hooks,
helpers: defaultEnv.helpers,
useFragmentCache: defaultEnv.useFragmentCache
};
var env = RenderEnv.build(view);

view.env = env;
createOrUpdateComponent(view, {}, null, renderNode, env);
Expand Down
13 changes: 11 additions & 2 deletions packages/ember-metal-views/lib/renderer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import run from "ember-metal/run_loop";
import { get } from "ember-metal/property_get";
import { set } from "ember-metal/property_set";
import { assign } from "ember-metal/merge";
import setProperties from "ember-metal/set_properties";
import buildComponentTemplate from "ember-views/system/build-component-template";
import { indexOf } from "ember-metal/enumerable_utils";
//import { deprecation } from "ember-views/compat/attrs-proxy";
Expand Down Expand Up @@ -161,8 +163,15 @@ Renderer.prototype.updateAttrs = function (view, attrs) {
this.setAttrs(view, attrs);
}; // setting new attrs

Renderer.prototype.componentUpdateAttrs = function (component, oldAttrs, newAttrs) {
set(component, 'attrs', newAttrs);
Renderer.prototype.componentUpdateAttrs = function (component, newAttrs) {
let oldAttrs = null;

if (component.attrs) {
oldAttrs = assign({}, component.attrs);
setProperties(component.attrs, newAttrs);
} else {
set(component, 'attrs', newAttrs);
}

component.trigger('didUpdateAttrs', { oldAttrs, newAttrs });
component.trigger('didReceiveAttrs', { oldAttrs, newAttrs });
Expand Down
4 changes: 2 additions & 2 deletions packages/ember-routing-htmlbars/lib/keywords/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ export default {
};
},

childEnv(state) {
return { outletState: state.childOutletState };
childEnv(state, env) {
return env.childWithOutletState(state.childOutletState);
},

isStable(lastState, nextState) {
Expand Down

0 comments on commit d1d993d

Please sign in to comment.