Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Another checkpoint, using Container now

  • Loading branch information...
commit 25f444603b660976c00eb4cb4b7be31d1da01f86 1 parent 479d6bb
@wycats wycats authored
Showing with 534 additions and 174 deletions.
  1. +1 −0  .gitignore
  2. +1 −0  .jshintrc
  3. +138 −0 packages/container/lib/main.js
  4. +30 −0 packages/container/package.json
  5. +181 −0 packages/container/tests/container_test.js
  6. +1 −0  packages/ember-application/lib/system.js
  7. +53 −60 packages/ember-application/lib/system/application.js
  8. +1 −0  packages/ember-application/package.json
  9. +12 −7 packages/ember-application/tests/system/application_test.js
  10. +6 −3 packages/ember-application/tests/system/injections_test.js
  11. +5 −4 packages/ember-application/tests/system/readiness_test.js
  12. +1 −1  packages/ember-metal/lib/observer.js
  13. +1 −0  packages/ember-routing/lib/system.js
  14. +21 −28 packages/ember-routing/lib/system/route.js
  15. +20 −23 packages/ember-routing/lib/system/router.js
  16. +2 −1  packages/ember-routing/package.json
  17. +13 −11 packages/ember-routing/tests/helpers/link_to_test.js
  18. +25 −20 packages/ember-routing/tests/integration/basic_test.js
  19. +2 −4 packages/ember-runtime/lib/system/core_object.js
  20. +1 −0  packages/ember-views/lib/system/controller.js
  21. +1 −1  packages/ember-views/lib/views/container_view.js
  22. +1 −0  packages/ember-views/package.json
  23. +4 −1 packages/ember-views/tests/views/container_view_test.js
  24. +13 −10 tests/index.html
View
1  .gitignore
@@ -34,3 +34,4 @@ tmp*.gem
tmp.bpm
tmp.spade
tests/source
+node_modules
View
1  .jshintrc
@@ -19,6 +19,7 @@
"testBoth",
"testWithDefault",
"raises",
+ "throws",
"deepEqual",
"start",
"stop",
View
138 packages/container/lib/main.js
@@ -0,0 +1,138 @@
+var get = Ember.get, set = Ember.set;
+
+function Container() {
+ this.registry = {};
+ this.cache = {};
+ this.typeInjections = {};
+ this.injections = {};
+ this.options = {};
+ this.typeOptions = {};
+}
+
+Container.prototype = {
+ set: function(object, key, value) {
+ object[key] = value;
+ },
+
+ register: function(type, name, factory, options) {
+ this.registry[type + ":" + name] = factory;
+ this.options[type + ":" + name] = options || {};
+ },
+
+ resolve: function(fullName) {
+ if (this.registry.hasOwnProperty(fullName)) {
+ return this.registry[fullName];
+ }
+ },
+
+ lookup: function(fullName) {
+ if (this.cache.hasOwnProperty(fullName)) {
+ return this.cache[fullName];
+ }
+
+ var value = instantiate(this, fullName);
+
+ if (!value) { return; }
+
+ if (isSingleton(this, fullName)) {
+ this.cache[fullName] = value;
+ }
+
+ return value;
+ },
+
+ optionsForType: function(type, options) {
+ this.typeOptions[type] = options;
+ },
+
+ typeInjection: function(type, property, fullName) {
+ var injections = this.typeInjections[type] = this.typeInjections[type] || [];
+ injections.push({ property: property, fullName: fullName });
+ },
+
+ injection: function(factoryName, property, injectionName) {
+ var injections = this.injections[factoryName] = this.injections[factoryName] || [];
+ injections.push({ property: property, fullName: injectionName });
+ },
+
+ destroy: function() {
+ eachDestroyable(this, function(item) {
+ item.isDestroying = true;
+ });
+
+ eachDestroyable(this, function(item) {
+ item.destroy();
+ });
+
+ this.isDestroyed = true;
+ }
+};
+
+function isSingleton(container, fullName) {
+ var singleton = option(container, fullName, 'singleton');
+
+ return singleton !== false;
+}
+
+function applyInjections(container, value, injections) {
+ if (!injections) { return; }
+
+ var injection, lookup;
+
+ for (var i=0, l=injections.length; i<l; i++) {
+ injection = injections[i];
+ lookup = container.lookup(injection.fullName);
+ container.set(value, injection.property, lookup);
+ }
+}
+
+function option(container, fullName, optionName) {
+ var options = container.options[fullName];
+
+ if (options && options[optionName] !== undefined) {
+ return options[optionName];
+ }
+
+ var type = fullName.split(":")[0];
+ options = container.typeOptions[type];
+
+ if (options) {
+ return options[optionName];
+ }
+}
+
+function instantiate(container, fullName) {
+ var splitName = fullName.split(":"),
+ type = splitName[0], name = splitName[1],
+ value;
+
+ var factory = container.resolve(fullName);
+
+ if (option(container, fullName, 'instantiate') === false) {
+ return factory;
+ }
+
+ if (factory) {
+ value = factory.create({ container: container });
+
+ var injections = [];
+ injections = injections.concat(container.typeInjections[type] || []);
+ injections = injections.concat(container.injections[fullName] || []);
+
+ applyInjections(container, value, injections);
+
+ return value;
+ }
+}
+
+function eachDestroyable(container, callback) {
+ var cache = container.cache;
+
+ for (var prop in cache) {
+ if (!cache.hasOwnProperty(prop)) { continue; }
+ if (option(container, prop, 'instantiate') === false) { continue; }
+ callback(cache[prop]);
+ }
+}
+
+Ember.Container = Container;
View
30 packages/container/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "container",
+ "summary": "A lightweight dependency injection container",
+ "description": "A lightweight library that provides a pluggable dependency injection container designed for use in Ember.js",
+ "homepage": "https://github.com/emberjs/ember.js",
+ "authors": ["Yehuda Katz", "Tom Dale"],
+ "version": "1.0.0-pre.2",
+
+ "dependencies": {
+ "spade": "~> 1.0.0"
+ },
+
+ "directories": {
+ "lib": "lib"
+ },
+
+ "bpm:build": {
+ "bpm_libs.js": {
+ "files": ["lib"],
+ "modes": "*"
+ },
+
+ "handlebars/bpm_tests.js": {
+ "files": ["tests"],
+ "modes": ["debug"]
+ }
+ }
+}
+
+
View
181 packages/container/tests/container_test.js
@@ -0,0 +1,181 @@
+var get = Ember.get;
+
+module("Container");
+
+function factory() {
+ var Klass = function(container) {
+ this.container = container;
+ };
+
+ Klass.prototype.destroy = function() {
+ this.isDestroyed = true;
+ };
+
+ Klass.create = function(options) {
+ return new Klass(options.container);
+ };
+
+ return Klass;
+}
+
+test("A registered factory returns the same instance each time", function() {
+ var container = new Ember.Container();
+ var PostController = factory();
+
+ container.register('controller', 'post', PostController);
+
+ var postController = container.lookup('controller:post');
+
+ ok(postController instanceof PostController, "The lookup is an instance of the factory");
+
+ equal(postController, container.lookup('controller:post'));
+});
+
+test("A container lookup has access to the container", function() {
+ var container = new Ember.Container();
+ var PostController = factory();
+
+ container.register('controller', 'post', PostController);
+
+ var postController = container.lookup('controller:post');
+
+ equal(postController.container, container);
+});
+
+test("A factory type with a registered injection receives the injection", function() {
+ var container = new Ember.Container();
+ var PostController = factory();
+ var Store = factory();
+
+ container.register('controller', 'post', PostController);
+ container.register('store', 'main', Store);
+
+ container.typeInjection('controller', 'store', 'store:main');
+
+ var postController = container.lookup('controller:post');
+ var store = container.lookup('store:main');
+
+ equal(postController.store, store);
+});
+
+test("An individual factory with a registered injection receives the injection", function() {
+ var container = new Ember.Container();
+ var PostController = factory();
+ var Store = factory();
+
+ container.register('controller', 'post', PostController);
+ container.register('store', 'main', Store);
+
+ container.injection('controller:post', 'store', 'store:main');
+
+ var postController = container.lookup('controller:post');
+ var store = container.lookup('store:main');
+
+ equal(postController.store, store);
+});
+
+test("A factory with both type and individual injections", function() {
+ var container = new Ember.Container();
+ var PostController = factory();
+ var Store = factory();
+ var Router = factory();
+
+ container.register('controller', 'post', PostController);
+ container.register('store', 'main', Store);
+ container.register('router', 'main', Router);
+
+ container.injection('controller:post', 'store', 'store:main');
+ container.typeInjection('controller', 'router', 'router:main');
+
+ var postController = container.lookup('controller:post');
+ var store = container.lookup('store:main');
+ var router = container.lookup('router:main');
+
+ equal(postController.store, store);
+ equal(postController.router, router);
+});
+
+test("A non-singleton factory is never cached", function() {
+ var container = new Ember.Container();
+ var PostView = factory();
+
+ container.register('view', 'post', PostView, { singleton: false });
+
+ var postView1 = container.lookup('view:post');
+ var postView2 = container.lookup('view:post');
+
+ ok(postView1 !== postView2, "Non-singletons are not cached");
+});
+
+test("A non-instantiated property is not instantiated", function() {
+ var container = new Ember.Container();
+
+ var template = function() {};
+ container.register('template', 'foo', template, { instantiate: false });
+ equal(container.lookup('template:foo'), template);
+});
+
+test("A failed lookup returns undefined", function() {
+ var container = new Ember.Container();
+
+ equal(container.lookup("doesnot:exist"), undefined);
+});
+
+test("Destroying the container destroys any cached singletons", function() {
+ var container = new Ember.Container();
+ var PostController = factory();
+ var PostView = factory();
+ var template = function() {};
+
+ container.register('controller', 'post', PostController);
+ container.register('view', 'post', PostView, { singleton: false });
+ container.register('template', 'post', template, { instantiate: false });
+
+ container.injection('controller:post', 'postView', 'view:post');
+
+ var postController = container.lookup('controller:post');
+ var postView = postController.postView;
+
+ ok(postView instanceof PostView, "The non-singleton was injected");
+
+ container.destroy();
+
+ ok(postController.isDestroyed, "Singletons are destroyed");
+ ok(!postView.isDestroyed, "Non-singletons are not destroyed");
+});
+
+test("The container can take a hook to resolve factories lazily", function() {
+ var container = new Ember.Container();
+ var PostController = factory();
+
+ container.resolve = function(fullName) {
+ if (fullName === 'controller:post') {
+ return PostController;
+ }
+ };
+
+ var postController = container.lookup('controller:post');
+
+ ok(postController instanceof PostController, "The correct factory was provided");
+});
+
+test("The container can get options that should be applied to all factories for a given type", function() {
+ var container = new Ember.Container();
+ var PostView = factory();
+
+ container.resolve = function(fullName) {
+ if (fullName === 'view:post') {
+ return PostView;
+ }
+ };
+
+ container.optionsForType('view', { singleton: false });
+
+ var postView1 = container.lookup('view:post');
+ var postView2 = container.lookup('view:post');
+
+ ok(postView1 instanceof PostView, "The correct factory was provided");
+ ok(postView2 instanceof PostView, "The correct factory was provided");
+
+ ok(postView1 !== postView2, "The two lookups are different");
+});
View
1  packages/ember-application/lib/system.js
@@ -1,2 +1,3 @@
+require('container');
require('ember-application/system/dag');
require('ember-application/system/application');
View
113 packages/ember-application/lib/system/application.js
@@ -3,7 +3,9 @@
@submodule ember-application
*/
-var get = Ember.get, set = Ember.set;
+var get = Ember.get, set = Ember.set, classify = Ember.String.classify;
+
+Ember.Container.set = Ember.set;
/**
An instance of `Ember.Application` is the starting point for every Ember
@@ -189,7 +191,7 @@ var get = Ember.get, set = Ember.set;
@namespace Ember
@extends Ember.Namespace
*/
-Ember.Application = Ember.Namespace.extend(
+var Application = Ember.Application = Ember.Namespace.extend(
/** @scope Ember.Application.prototype */{
/**
@@ -267,11 +269,10 @@ Ember.Application = Ember.Namespace.extend(
init: function() {
if (!this.$) { this.$ = Ember.$; }
- this._super();
+ var container = this.container = Application.buildContainer(this);
+ container.register('view', 'application', Ember.View.extend());
- if (this.Router === undefined && this.router === undefined) {
- this.Router = Ember.Router.extend();
- }
+ this._super();
this.createEventDispatcher();
@@ -345,13 +346,15 @@ Ember.Application = Ember.Namespace.extend(
@method initialize
@param router {Ember.Router}
*/
- initialize: function(router) {
+ initialize: function() {
Ember.assert("Application initialize may only be called once", !this.isInitialized);
Ember.assert("Application not destroyed", !this.isDestroyed);
- router = this.setupRouter(router);
+ var Router = this.Router;
+ if (!Router && this.router === undefined) { Router = Ember.Router.extend(); }
+ this.container.register('router', 'main', Router);
- this.runInjections(router);
+ this.runInjections();
Ember.runLoadHooks('application', this);
@@ -365,8 +368,9 @@ Ember.Application = Ember.Namespace.extend(
},
/** @private */
- runInjections: function(router) {
- var injections = get(this.constructor, 'injections'),
+ runInjections: function() {
+ var router = this.container.lookup('router:main'),
+ injections = get(this.constructor, 'injections'),
graph = new Ember.DAG(),
namespace = this,
properties, i, injection;
@@ -386,30 +390,10 @@ Ember.Application = Ember.Namespace.extend(
},
/** @private */
- setupRouter: function(router) {
- if (!router && Ember.Router.detect(this.Router)) {
- router = this.Router.create();
- this._createdRouter = router;
- }
-
- if (router) {
- // By default, the router's namespace is the current application.
- //
- // This allows it to find model classes when a state has a
- // route like `/posts/:post_id`. In that case, it would first
- // convert `post_id` into `Post`, and then look it up on its
- // namespace.
- set(router, 'namespace', this);
- }
-
- return router;
- },
-
- /** @private */
didBecomeReady: function() {
var eventDispatcher = get(this, 'eventDispatcher'),
customEvents = get(this, 'customEvents'),
- router = this._createdRouter;
+ router = this.container.lookup('router:main');
eventDispatcher.setup(customEvents);
@@ -426,33 +410,8 @@ Ember.Application = Ember.Namespace.extend(
createApplicationView: function () {
var rootElement = get(this, 'rootElement'),
- router = this._createdRouter,
- applicationViewOptions = {},
- applicationViewClass = this.ApplicationView,
- applicationTemplate = Ember.TEMPLATES.application,
- applicationController, applicationView;
-
- // don't do anything unless there is an ApplicationView or application template
- if (!applicationViewClass && !applicationTemplate) return;
-
- if (router) {
- applicationController = get(router, 'applicationController');
- if (applicationController) {
- applicationViewOptions.controller = applicationController;
- }
- }
-
- if (applicationTemplate) {
- applicationViewOptions.template = applicationTemplate;
- }
-
- if (!applicationViewClass) {
- applicationViewClass = Ember.View;
- }
-
- applicationView = applicationViewClass.create(applicationViewOptions);
-
- this._createdApplicationView = applicationView;
+ router = this.container.lookup('router:main'),
+ applicationView = this.container.lookup('view:application');
if (router) {
router._activeViews.application = applicationView;
@@ -491,7 +450,7 @@ Ember.Application = Ember.Namespace.extend(
willDestroy: function() {
get(this, 'eventDispatcher').destroy();
- if (this._createdRouter) { this._createdRouter.destroy(); }
+ this.container.destroy();
if (this._createdApplicationView) { this._createdApplicationView.destroy(); }
},
@@ -511,9 +470,43 @@ Ember.Application.reopenClass({
Ember.assert("An injection cannot be registered without an injection function", Ember.canInvoke(injection, 'injection'));
injections.push(injection);
+ },
+
+ buildContainer: function(namespace) {
+ var container = new Ember.Container();
+ container.set = Ember.set;
+ container.resolve = resolveFor(namespace);
+ container.optionsForType('view', { singleton: false });
+ container.optionsForType('template', { instantiate: false });
+ container.register('application', 'main', namespace, { instantiate: false });
+ container.injection('router:main', 'namespace', 'application:main');
+ container.typeInjection('controller', 'target', 'router:main');
+
+ container.injection('view:application', 'controller', 'controller:application');
+ container.injection('view:application', 'defaultTemplate', 'template:application');
+
+ return container;
}
});
+function resolveFor(namespace) {
+ return function(fullName) {
+ var nameParts = fullName.split(":"),
+ type = nameParts[0], name = nameParts[1];
+
+ if (type === 'template' && Ember.TEMPLATES[name]) {
+ return Ember.TEMPLATES[name];
+ }
+
+ var className = classify(name) + classify(type);
+ var factory = get(namespace, className);
+
+ if (factory) { return factory; }
+
+ return Ember.Container.prototype.resolve.call(this, fullName);
+ };
+}
+
Ember.Application.registerInjection({
name: 'controllers',
injection: function(app, router, property) {
View
1  packages/ember-application/package.json
@@ -9,6 +9,7 @@
"ember-views": "1.0.0-pre.2",
"ember-states": "1.0.0-pre.2",
"ember-routing": "1.0.0-pre.2"
+ "container": "1.0.0-pre.2"
},
"directories": {
View
19 packages/ember-application/tests/system/application_test.js
@@ -62,11 +62,11 @@ test("you cannot make two default applications without a rootElement error", fun
});
Ember.run(function() {
- application = Ember.Application.create().initialize();
+ application = Ember.Application.create({ router: false }).initialize();
});
raises(function() {
Ember.run(function() {
- Ember.Application.create().initialize();
+ Ember.Application.create({ router: false }).initialize();
});
}, Error);
});
@@ -94,6 +94,7 @@ var app;
module("Ember.Application initialization", {
teardown: function() {
+ Ember.TEMPLATES = {};
Ember.run(function(){ app.destroy(); });
}
});
@@ -112,8 +113,8 @@ test('initialized application go to initial route', function() {
match("/").to("index");
});
- Ember.TEMPLATES.application = Ember.Handlebars.compile(
- "{{outlet}}"
+ app.container.register('template', 'application',
+ Ember.Handlebars.compile("{{outlet}}")
);
Ember.TEMPLATES.index = Ember.Handlebars.compile(
@@ -149,8 +150,9 @@ test("initialize application via initialize call", function() {
app.initialize();
});
- equal(app._createdRouter instanceof Ember.Router, true, "Router was set from initialize call");
- equal(app._createdRouter.location instanceof Ember.NoneLocation, true, "Location was set from location implementation name");
+ var router = app.container.lookup('router:main');
+ equal(router instanceof Ember.Router, true, "Router was set from initialize call");
+ equal(router.location instanceof Ember.NoneLocation, true, "Location was set from location implementation name");
});
test("initialize application with stateManager via initialize call from Router class", function() {
@@ -178,7 +180,8 @@ test("initialize application with stateManager via initialize call from Router c
app.initialize();
});
- equal(app._createdRouter instanceof Ember.Router, true, "Router was set from initialize call");
+ var router = app.container.lookup('router:main');
+ equal(router instanceof Ember.Router, true, "Router was set from initialize call");
equal(Ember.$("#qunit-fixture h1").text(), "Hello!");
});
@@ -213,6 +216,7 @@ test("ApplicationView is inserted into the page", function() {
test("Application initialized twice raises error", function() {
Ember.run(function() {
app = Ember.Application.create({
+ router: false,
rootElement: '#qunit-fixture'
}).initialize();
});
@@ -228,6 +232,7 @@ test("Minimal Application initialized with just an application template", functi
Ember.$('#qunit-fixture').html('<script type="text/x-handlebars">Hello World</script>');
Ember.run(function () {
app = Ember.Application.create({
+ router: false,
rootElement: '#qunit-fixture'
}).initialize();
});
View
9 packages/ember-application/tests/system/injections_test.js
@@ -61,10 +61,11 @@ test("injections can be registered in a specified order", function() {
Ember.run(function() {
app = Ember.Application.create({
+ router: false,
order: order,
rootElement: '#qunit-fixture'
});
- app.initialize(Ember.Object.create());
+ app.initialize();
});
deepEqual(order, ['first', 'second', 'third', 'fourth', 'fifth']);
@@ -118,10 +119,11 @@ test("injections can have multiple dependencies", function () {
Ember.run(function() {
app = Ember.Application.create({
+ router: false,
order: order,
rootElement: '#qunit-fixture'
});
- app.initialize(Ember.Object.create());
+ app.initialize();
});
ok(indexOf.call(order, a.name) < indexOf.call(order, b.name), 'a < b');
@@ -156,9 +158,10 @@ test("injections are passed properties created from previous injections", functi
Ember.run(function() {
app = Ember.Application.create({
+ router: false,
rootElement: '#qunit-fixture'
});
- app.initialize(Ember.Object.create());
+ app.initialize();
});
ok(secondInjectionWasPassedProperty, "second injections wasn't passed the property created in the first");
View
9 packages/ember-application/tests/system/readiness_test.js
@@ -61,7 +61,7 @@ test("Ember.Application's ready event is called right away if jQuery is already
jQuery.isReady = true;
Ember.run(function() {
- application = Application.create().initialize();
+ application = Application.create({ router: false }).initialize();
});
equal(readyWasCalled, 1, "ready was called");
@@ -75,7 +75,7 @@ test("Ember.Application's ready event is called right away if jQuery is already
test("Ember.Application's ready event is called after the document becomes ready", function() {
Ember.run(function() {
- application = Application.create().initialize();
+ application = Application.create({ router: false }).initialize();
});
equal(readyWasCalled, 0, "ready wasn't called yet");
@@ -90,6 +90,7 @@ test("Ember.Application's ready event is called after the document becomes ready
test("Ember.Application's ready event is called after the document becomes ready without initialize if autoinit is set", function() {
Ember.run(function() {
application = Application.create({
+ router: false,
autoinit: true
});
});
@@ -105,7 +106,7 @@ test("Ember.Application's ready event is called after the document becomes ready
test("Ember.Application's ready event can be deferred by other components", function() {
Ember.run(function() {
- application = Application.create();
+ application = Application.create({ router: false });
});
application.deferReadiness();
@@ -133,7 +134,7 @@ test("Ember.Application's ready event can be deferred by other components", func
jQuery.isReady = true;
Ember.run(function() {
- application = Application.create();
+ application = Application.create({ router: false });
});
application.deferReadiness();
View
2  packages/ember-metal/lib/observer.js
@@ -66,7 +66,7 @@ ObserverSet.prototype.flush = function() {
for (i=0, len=observers.length; i < len; ++i) {
observer = observers[i];
sender = observer.sender;
- if (sender.isDestroyed) { continue; }
+ if (sender.isDestroying || sender.isDestroyed) { continue; }
Ember.sendEvent(sender, observer.eventName, [sender, observer.keyName], observer.listeners);
}
};
View
1  packages/ember-routing/lib/system.js
@@ -1,2 +1,3 @@
+require('container');
require('ember-routing/system/router');
require('ember-routing/system/route');
View
49 packages/ember-routing/lib/system/route.js
@@ -5,7 +5,6 @@ var DefaultView = Ember.View.extend(Ember._Metamorph);
Ember.Route = Ember.Object.extend({
init: function() {
var router = this.router;
- this._container = router._container;
this._activeViews = router._activeViews;
this.namespace = router.namespace;
},
@@ -16,16 +15,23 @@ Ember.Route = Ember.Object.extend({
This hook is the entry point for router.js
*/
setup: function(context) {
+ var container = this.router.container;
+
var templateName = this.templateName,
- controller = this.lookup('controller', templateName, function() {
- if (context && context.isSCArray) {
- return Ember.ArrayController.create({ content: context });
- } else if (context) {
- return Ember.ObjectController.create({ content: context });
- } else {
- return Ember.Controller.create();
- }
- });
+ controller = container.lookup('controller:' + templateName);
+
+ if (!controller) {
+ if (context && context.isSCArray) {
@wagenet Owner
wagenet added a note

Seems crazy that any of our code is using isSCArray, and not just because it's SC, but because this seems like a weird way of testing array-ness.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ controller = Ember.ArrayController.extend({ content: context });
+ } else if (context) {
+ controller = Ember.ObjectController.extend({ content: context });
+ } else {
+ controller = Ember.Controller.extend();
+ }
+
+ container.register('controller', templateName, controller);
+ controller = container.lookup('controller:' + templateName);
+ }
this.setupControllers(controller, context);
this.renderTemplates(context);
@@ -71,7 +77,7 @@ Ember.Route = Ember.Object.extend({
},
controller: function(name) {
- return this.lookup('controller', name);
+ return this.router.container.lookup('controller:' + name);
},
renderTemplates: function(context) {
@@ -80,12 +86,11 @@ Ember.Route = Ember.Object.extend({
render: function(name, options) {
var templateName = this.templateName,
+ container = this.router.container,
className = classify(templateName),
- viewClassName = className + "View",
- viewClass = this.namespace[viewClassName] || DefaultView;
+ view = container.lookup('view:' + templateName) || DefaultView.create();
- var view = this._activeViews[templateName] =
- viewClass.create({ templateName: templateName });
+ set(view, 'template', container.lookup('template:' + templateName));
options = options || {};
var into = options.into || 'application';
@@ -93,24 +98,12 @@ Ember.Route = Ember.Object.extend({
var controller = options.controller || templateName;
if (typeof controller === 'string') {
- controller = this.lookup('controller', controller);
+ controller = container.lookup('controller:' + controller);
}
- set(controller, 'target', this.router);
set(view, 'controller', controller);
var parentView = this._activeViews[into];
parentView.connectOutlet(outlet, view);
- },
-
- lookup: function(kind, name, callback) {
- var object = this._container[kind][name];
-
- if (!object && callback) {
- object = callback.call(this);
- this._container[kind][name] = object;
- }
-
- return object;
}
});
View
43 packages/ember-routing/lib/system/router.js
@@ -3,6 +3,18 @@ var get = Ember.get, set = Ember.set, classify = Ember.String.classify;
var DefaultView = Ember.View.extend(Ember._Metamorph);
+function setupLocation(router) {
+ var location = get(router, 'location'),
+ rootURL = get(router, 'rootURL');
+
+ if ('string' === typeof location) {
+ set(router, 'location', Ember.Location.create({
+ implementation: location,
+ rootURL: rootURL
+ }));
+ }
+}
+
Ember.Router = Ember.Object.extend({
location: 'hash',
@@ -10,25 +22,9 @@ Ember.Router = Ember.Object.extend({
var router = this.router = new Router(),
self = this, handlers = {};
- // This is a temporary implementation. Apps should go through
- // the `lookup` API and not try to use this data structure
- // directly.
- var container = this._container = {
- view: {},
- controller: {}
- };
-
var activeViews = this._activeViews = {};
- var location = get(this, 'location'),
- rootURL = get(this, 'rootURL');
-
- if ('string' === typeof location) {
- location = set(this, 'location', Ember.Location.create({
- implementation: location,
- rootURL: rootURL
- }));
- }
+ setupLocation(this);
Ember.assert("You must call " + this.constructor.toString() + ".map() before your application is initialized", this.constructor.callback);
router.map(this.constructor.callback);
@@ -36,17 +32,18 @@ Ember.Router = Ember.Object.extend({
startRouting: function() {
var router = this.router,
- location = get(this, 'location');
+ location = get(this, 'location'),
+ container = this.container;
- router.getHandler = getHandlerFunction(this, this._container, this._activeViews);
+ router.getHandler = getHandlerFunction(this, this._activeViews);
router.updateURL = function() {
location.setURL.apply(location, arguments);
};
- if (!this._container.view.application) {
- this._container.view.application = DefaultView.create({
+ if (!container.lookup('view:application')) {
+ container.register('view', 'application', DefaultView.create({
templateName: 'application'
- });
+ }));
}
router.handleURL(location.getURL());
@@ -76,7 +73,7 @@ Ember.Router = Ember.Object.extend({
}
});
-function getHandlerFunction(router, container, activeViews) {
+function getHandlerFunction(router, activeViews) {
var handlers = {}, namespace = get(router, 'namespace');
return function(name) {
View
3  packages/ember-routing/package.json
@@ -12,7 +12,8 @@
"dependencies": {
"spade": "~> 1.0",
- "ember-views": "1.0.0-pre.2"
+ "ember-views": "1.0.0-pre.2",
+ "container": "1.0.0-pre.2"
},
"dependencies:development": {
View
24 packages/ember-routing/tests/helpers/link_to_test.js
@@ -1,10 +1,10 @@
-var Router, App, AppView, templates, router, eventDispatcher;
+require('ember-application');
+
+var Router, App, AppView, templates, router, eventDispatcher, container;
var get = Ember.get, set = Ember.set;
function bootApplication() {
- router = Router.create({
- location: 'none'
- });
+ router = container.lookup('router:main');
Ember.run(function() {
router._activeViews.application = AppView.create().appendTo('#qunit-fixture');
@@ -18,20 +18,24 @@ module("The {{linkTo}} helper", {
App = Ember.Namespace.create();
App.toString = function() { return "App"; };
+ container = Ember.Application.buildContainer(App);
+
Ember.TEMPLATES.app = Ember.Handlebars.compile("{{outlet}}");
Ember.TEMPLATES.home = Ember.Handlebars.compile("<h3>Home</h3>{{#linkTo about id='about-link'}}About{{/linkTo}}");
Ember.TEMPLATES.about = Ember.Handlebars.compile("<h3>About</h3>{{#linkTo home id='home-link'}}Home{{/linkTo}}");
Ember.TEMPLATES.item = Ember.Handlebars.compile("<h3>Item</h3><p>{{name}}</p>{{#linkTo home id='home-link'}}Home{{/linkTo}}");
- AppView = Ember.View.extend({
- template: Ember.TEMPLATES.app
+ Router = Ember.Router.extend({
+ location: 'none'
});
- Router = Ember.Router.extend({
- namespace: App,
- templates: Ember.TEMPLATES
+ AppView = Ember.View.extend({
+ templateName: 'app'
});
+ container.register('view', 'app');
+ container.register('router', 'main', Router);
+
eventDispatcher = Ember.EventDispatcher.create();
eventDispatcher.setup();
});
@@ -56,8 +60,6 @@ test("The {{linkTo}} helper moves into the named route", function() {
equal(Ember.$('h3:contains(Home)', '#qunit-fixture').length, 1, "The home template was rendered");
- console.log(Ember.$('#qunit-fixture')[0]);
-
Ember.run(function() {
Ember.$('a', '#qunit-fixture').click();
});
View
45 packages/ember-routing/tests/integration/basic_test.js
@@ -1,10 +1,8 @@
-var Router, App, AppView, templates, router;
+var Router, App, AppView, templates, router, container;
var get = Ember.get, set = Ember.set;
function bootApplication() {
- router = Router.create({
- location: 'none'
- });
+ router = container.lookup('router:main');
Ember.run(function() {
router._activeViews.application = AppView.create().appendTo('#qunit-fixture');
@@ -18,6 +16,9 @@ module("Basic Routing", {
App = Ember.Namespace.create();
App.toString = function() { return "App"; };
+
+ container = Ember.Application.buildContainer(App);
+
App.LoadingRoute = Ember.Route.extend({
});
@@ -25,14 +26,16 @@ module("Basic Routing", {
Ember.TEMPLATES.home = Ember.Handlebars.compile("<h3>Hours</h3>");
Ember.TEMPLATES.homepage = Ember.TEMPLATES.home;
+ Router = Ember.Router.extend({
+ location: 'none'
+ });
+
AppView = Ember.View.extend({
template: Ember.TEMPLATES.app
});
- Router = Ember.Router.extend({
- namespace: App,
- templates: Ember.TEMPLATES
- });
+ container.register('view', 'app');
+ container.register('router', 'main', Router);
});
}
});
@@ -95,7 +98,7 @@ test("The Homepage with a `setupControllers` hook", function() {
bootApplication();
- router._container.controller.home = Ember.Controller.create();
+ container.register('controller', 'home', Ember.Controller.extend());
Ember.run(function() {
router.handleURL("/");
@@ -125,7 +128,7 @@ test("The Homepage with a `setupControllers` hook modifying other controllers",
bootApplication();
- router._container.controller.home = Ember.Controller.create();
+ container.register('controller', 'home', Ember.Controller.extend());
Ember.run(function() {
router.handleURL("/");
@@ -159,7 +162,7 @@ test("The Homepage getting its controller context via model", function() {
bootApplication();
- router._container.controller.home = Ember.Controller.create();
+ container.register('controller', 'home', Ember.Controller.extend());
Ember.run(function() {
router.handleURL("/");
@@ -192,7 +195,7 @@ test("The Specials Page getting its controller context by deserializing the para
bootApplication();
- router._container.controller.special = Ember.Controller.create();
+ container.register('controller', 'special', Ember.Controller.extend());
Ember.run(function() {
router.handleURL("/specials/1");
@@ -226,7 +229,7 @@ test("The Specials Page defaults to looking models up via `find`", function() {
bootApplication();
- router._container.controller.special = Ember.Controller.create();
+ container.register('controller', 'special', Ember.Controller.extend());
Ember.run(function() {
router.handleURL("/specials/1");
@@ -271,7 +274,7 @@ test("The Special Page returning a promise puts the app into a loading state unt
bootApplication();
- router._container.controller.special = Ember.Controller.create();
+ container.register('controller', 'special', Ember.Controller.extend());
Ember.run(function() {
router.handleURL("/specials/1");
@@ -331,7 +334,7 @@ test("Moving from one page to another triggers the correct callbacks", function(
bootApplication();
- router._container.controller.special = Ember.Controller.create();
+ container.register('controller', 'special', Ember.Controller.extend());
Ember.run(function() {
router.handleURL("/");
@@ -414,7 +417,7 @@ test("Nested callbacks are not exited when moving to siblings", function() {
bootApplication();
});
- router._container.controller.special = Ember.Controller.create();
+ container.register('controller', 'special', Ember.Controller.extend());
equal(Ember.$('h3', '#qunit-fixture').text(), "Home", "The app is now in the initial state");
equal(rootSetup, 1, "The root setup was triggered");
@@ -462,8 +465,10 @@ asyncTest("Events are triggered on the current state", function() {
bootApplication();
- var controller = router._container.controller.home = Ember.Controller.create();
- controller.target = router;
+ container.register('controller', 'home', Ember.Controller.extend());
+
+ //var controller = router._container.controller.home = Ember.Controller.create();
+ //controller.target = router;
Ember.run(function() {
router.handleURL("/");
@@ -506,8 +511,8 @@ asyncTest("Events are triggered on the current state", function() {
bootApplication();
- var controller = router._container.controller.home = Ember.Controller.create();
- controller.target = router;
+ //var controller = router._container.controller.home = Ember.Controller.create();
+ //controller.target = router;
Ember.run(function() {
router.handleURL("/");
View
6 packages/ember-runtime/lib/system/core_object.js
@@ -194,7 +194,6 @@ CoreObject.PrototypeMixin = Mixin.create({
if (this.willDestroy) { this.willDestroy(); }
- set(this, 'isDestroyed', true);
schedule('destroy', this, this._scheduledDestroy);
return this;
},
@@ -209,6 +208,8 @@ CoreObject.PrototypeMixin = Mixin.create({
*/
_scheduledDestroy: function() {
destroy(this);
+ set(this, 'isDestroyed', true);
+
if (this.didDestroy) { this.didDestroy(); }
},
@@ -407,6 +408,3 @@ ClassMixin.apply(CoreObject);
@namespace Ember
*/
Ember.CoreObject = CoreObject;
-
-
-
View
1  packages/ember-views/lib/system/controller.js
@@ -20,6 +20,7 @@ Ember.ControllerMixin.reopen({
controllers: null,
namespace: null,
view: null,
+ container: null,
/**
Convenience method to connect controllers. This method makes other controllers
View
2  packages/ember-views/lib/views/container_view.js
@@ -386,8 +386,8 @@ Ember.ContainerView = Ember.View.extend({
currentView = get(this, 'currentView');
if (currentView) {
- childViews.removeObject(currentView);
currentView.destroy();
+ childViews.removeObject(currentView);
}
}, 'currentView'),
View
1  packages/ember-views/package.json
@@ -14,6 +14,7 @@
"spade": "~> 1.0",
"jquery": "~> 1.7",
"ember-runtime": "1.0.0-pre.2"
+ "container": "1.0.0-pre.2"
},
"dependencies:development": {
View
5 packages/ember-views/tests/views/container_view_test.js
@@ -90,7 +90,10 @@ test("should set the parentView property on views that are added to the child vi
equal(get(thirdView, 'parentView'), container, "sets the parent view of the third view");
equal(get(fourthView, 'parentView'), container, "sets the parent view of the fourth view");
- childViews.replace(2, 2);
+ Ember.run(function() {
+ childViews.replace(2, 2);
+ });
+
equal(get(view, 'parentView'), container, "doesn't change non-removed view");
equal(get(thirdView, 'parentView'), container, "doesn't change non-removed view");
equal(get(secondView, 'parentView'), null, "clears the parent view of the third view");
View
23 tests/index.html
@@ -14,17 +14,20 @@
if (origOpts && origOpts.setup) { opts.setup = origOpts.setup; }
opts.teardown = function() {
if (origOpts && origOpts.teardown) { origOpts.teardown(); }
- if (Ember.run.currentRunLoop) {
- ok(false, "Should not be in a run loop at end of test");
- while (Ember.run.currentRunLoop) {
- Ember.run.end();
+
+ if (Ember && Ember.run) {
+ if (Ember.run.currentRunLoop) {
+ ok(false, "Should not be in a run loop at end of test");
+ while (Ember.run.currentRunLoop) {
+ Ember.run.end();
+ }
+ }
+ if (Ember.run.hasScheduledTimers()) {
+ // Use `ok` so we get full description.
+ // Gate inside of `if` so that we don't mess up `expects` counts
+ ok(false, "Ember run should not have scheduled timers at end of test");
+ Ember.run.cancelTimers();
}
- }
- if (Ember.run.hasScheduledTimers()) {
- // Use `ok` so we get full description.
- // Gate inside of `if` so that we don't mess up `expects` counts
- ok(false, "Ember run should not have scheduled timers at end of test");
- Ember.run.cancelTimers();
}
}
return originalModule(name, opts);
Please sign in to comment.
Something went wrong with that request. Please try again.