Permalink
Browse files

Merge branch '1-0-beta'

Conflicts:
	packages/ember-application/lib/system/application.js
	packages/ember-runtime/lib/controllers.js
  • Loading branch information...
2 parents 3e26ac9 + 6041912 commit b71cae78d3ba11ec4bfbe0d5d36ee1fe2d4dbcf0 @krisselden krisselden committed May 24, 2012
@@ -94,29 +94,39 @@ Ember.Application = Ember.Namespace.extend(
...
});
- App.initialize(stateManager);
+ App.initialize(router);
- stateManager.get('postsController') // <App.PostsController:ember1234>
- stateManager.get('commentsController') // <App.CommentsController:ember1235>
+ router.get('postsController') // <App.PostsController:ember1234>
+ router.get('commentsController') // <App.CommentsController:ember1235>
- stateManager.getPath('postsController.stateManager') // stateManager
+ router.getPath('postsController.router') // router
*/
- initialize: function(stateManager) {
+ initialize: function(router) {
var properties = Ember.A(Ember.keys(this)),
injections = get(this.constructor, 'injections'),
namespace = this, controller, name;
- if (!stateManager && Ember.Router.detect(namespace['Router'])) {
- stateManager = namespace['Router'].create();
+ if (!router && Ember.Router.detect(namespace['Router'])) {
+ router = namespace['Router'].create();
}
- set(this, 'stateManager', stateManager);
+ if (router) {
+ set(this, 'stateManager', 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);
Ember.runLoadHooks('application', this);
properties.forEach(function(property) {
injections.forEach(function(injection) {
- injection(namespace, stateManager, property);
+ injection(namespace, router, property);
});
});
},
@@ -182,12 +192,14 @@ Ember.Application.reopenClass({
}
});
-Ember.Application.registerInjection(function(app, stateManager, property) {
+Ember.Application.registerInjection(function(app, router, property) {
if (!/^[A-Z].*Controller$/.test(property)) { return; }
var name = property[0].toLowerCase() + property.substr(1),
controller = app[property].create();
- stateManager.set(name, controller);
- controller.set('target', stateManager);
+ router.set(name, controller);
+
+ controller.set('target', router);
+ controller.set('controllers', router);
});
@@ -13,3 +13,4 @@ require("ember-handlebars/helpers/each");
require("ember-handlebars/helpers/template");
require("ember-handlebars/helpers/action");
require("ember-handlebars/helpers/yield");
+require("ember-handlebars/helpers/outlet");
@@ -0,0 +1,39 @@
+require('ember-handlebars/helpers/view');
+
+/**
+ The `outlet` helper allows you to specify that the current
+ view's controller will fill in the view for a given area.
+
+ {{outlet}}
+
+ By default, when the the current controller's `view`
+ property changes, the outlet will replace its current
+ view with the new view.
+
+ controller.set('view', someView);
+
+ You can also specify a particular name, other than view:
+
+ {{outlet masterView}}
+ {{outlet detailView}}
+
+ Then, you can control several outlets from a single
+ controller:
+
+ controller.set('masterView', postsView);
+ controller.set('detailView', postView);
+
+ @name Handlebars.helpers.outlet
+ @param {String} property the property on the controller
+ that holds the view for this outlet
+*/
+Ember.Handlebars.registerHelper('outlet', function(property, options) {
+ if (property && property.data && property.data.isRenderData) {
+ options = property;
+ property = 'view';
+ }
+
+ options.hash.currentViewBinding = "controller." + property;
+
+ return Ember.Handlebars.helpers.view.call(this, Ember.ContainerView, options);
+});
@@ -0,0 +1,64 @@
+var appendView = function(view) {
+ Ember.run(function() { view.appendTo('#qunit-fixture'); });
+};
+
+var compile = function(template) {
+ return Ember.Handlebars.compile(template);
+};
+
+var view;
+
+module("Handlebars {{outlet}} helpers", {
+ teardown: function() {
+ Ember.run(function () {
+ if (view) {
+ view.destroy();
+ }
+ });
+ }
+});
+
+test("outlet should allow controllers to fill in slots", function() {
+ var controller = Ember.Object.create();
+
+ var template = "<h1>HI</h1>{{outlet}}";
+ view = Ember.View.create({
+ controller: controller,
+ template: Ember.Handlebars.compile(template)
+ });
+
+ appendView(view);
+
+ equal(view.$().text(), 'HI');
+
+ Ember.run(function() {
+ controller.set('view', Ember.View.create({
+ template: compile("<p>BYE</p>")
+ }));
+ });
+
+ equal(view.$().text(), 'HIBYE');
+});
+
+
+test("outlet should support an optional name", function() {
+ var controller = Ember.Object.create();
+
+ var template = "<h1>HI</h1>{{outlet mainView}}";
+ view = Ember.View.create({
+ controller: controller,
+ template: Ember.Handlebars.compile(template)
+ });
+
+ appendView(view);
+
+ equal(view.$().text(), 'HI');
+
+ Ember.run(function() {
+ controller.set('mainView', Ember.View.create({
+ template: compile("<p>BYE</p>")
+ }));
+ });
+
+ equal(view.$().text(), 'HIBYE');
+});
@@ -358,6 +358,8 @@ Mixin.prototype.reopen = function() {
mixin.properties = this.properties;
delete this.properties;
this.mixins = [mixin];
+ } else if (!this.mixins) {
+ this.mixins = [];
}
var len = arguments.length, mixins = this.mixins, idx;
@@ -1,2 +1,3 @@
require('ember-runtime/controllers/array_controller');
require('ember-runtime/controllers/object_controller');
+require('ember-runtime/controllers/controller');
@@ -5,6 +5,7 @@
// ==========================================================================
require('ember-runtime/system/array_proxy');
+require('ember-runtime/controllers/controller');
/**
@class
@@ -45,4 +46,4 @@ require('ember-runtime/system/array_proxy');
@extends Ember.ArrayProxy
*/
-Ember.ArrayController = Ember.ArrayProxy.extend();
+Ember.ArrayController = Ember.ArrayProxy.extend(Ember.ControllerMixin);
@@ -0,0 +1,6 @@
+require('ember-runtime/system/object');
+require('ember-runtime/system/string');
+
+Ember.ControllerMixin = Ember.Mixin.create();
+
+Ember.Controller = Ember.Object.extend(Ember.ControllerMixin);
@@ -1,3 +1,4 @@
require('ember-runtime/system/object_proxy');
+require('ember-runtime/controllers/controller');
-Ember.ObjectController = Ember.ObjectProxy.extend();
+Ember.ObjectController = Ember.ObjectProxy.extend(Ember.ControllerMixin);
@@ -154,7 +154,7 @@ Ember.String = {
/**
Returns the lowerCaseCamel form of a string.
-
+
'innerHTML'.camelize() => 'innerHTML'
'action_name'.camelize() => 'actionName'
'css-class-name'.camelize() => 'cssClassName'
@@ -171,6 +171,19 @@ Ember.String = {
});
},
+ /**
+ Returns the UpperCamelCase form of a string.
+
+ 'innerHTML'.classify() => 'InnerHTML'
+ 'action_name'.classify() => 'ActionName'
+ 'css-class-name'.classify() => 'CssClassName'
+ 'my favorite items'.classift() => 'MyFavoriteItems'
+ */
+ classify: function(str) {
+ var camelized = Ember.String.camelize(str);
+ return camelized.charAt(0).toUpperCase() + camelized.substr(1);
+ },
+
/**
More general than decamelize. Returns the lower_case_and_underscored
form of a string.
@@ -189,4 +202,4 @@ Ember.String = {
return str.replace(STRING_UNDERSCORE_REGEXP_1, '$1_$2').
replace(STRING_UNDERSCORE_REGEXP_2, '_').toLowerCase();
}
-};
+};
@@ -9,9 +9,17 @@ var get = Ember.get, getPath = Ember.getPath;
// * .onURLChange(callback) - this happens when the user presses
// the back or forward button
+var paramForClass = function(classObject) {
+ var className = classObject.toString(),
+ parts = className.split("."),
+ last = parts[parts.length - 1];
+
+ return Ember.String.underscore(last) + "_id";
+};
+
Ember.Routable = Ember.Mixin.create({
init: function() {
- this.on('setupControllers', this, this.stashContext);
+ this.on('connectOutlets', this, this.stashContext);
this._super();
},
@@ -59,14 +67,73 @@ Ember.Routable = Ember.Mixin.create({
}).cacheable(),
routeMatcher: Ember.computed(function() {
- return Ember._RouteMatcher.create({ route: get(this, 'route') });
+ if (get(this, 'route')) {
+ return Ember._RouteMatcher.create({ route: get(this, 'route') });
+ }
}).cacheable(),
- deserialize: function(manager, context) {
- return context;
+ modelClass: Ember.computed(function() {
+ var modelType = get(this, 'modelType');
+
+ if (typeof modelType === 'string') {
+ return Ember.getPath(window, modelType);
+ } else {
+ return modelType;
+ }
+ }).cacheable(),
+
+ modelClassFor: function(manager) {
+ var modelClass, namespace, routeMatcher, identifiers, match, className;
+
+ // if an explicit modelType was specified, use that
+ if (modelClass = get(this, 'modelClass')) { return modelClass; }
+
+ // if the router has no lookup namespace, we won't be able to guess
+ // the modelType
+ namespace = get(manager, 'namespace');
+ if (!namespace) { return; }
+
+ // make sure this state is actually a routable state
+ routeMatcher = get(this, 'routeMatcher');
+ if (!routeMatcher) { return; }
+
+ // only guess modelType for states with a single dynamic segment
+ // (no more, no fewer)
+ identifiers = routeMatcher.identifiers;
+ if (identifiers.length !== 2) { return; }
+
+ // extract the `_id` from the end of the dynamic segment; if the
+ // dynamic segment does not end in `_id`, we can't guess the
+ // modelType
+ match = identifiers[1].match(/^(.*)_id$/);
+ if (!match) { return; }
+
+ // convert the underscored type into a class form and look it up
+ // on the router's namespace
+ className = Ember.String.classify(match[1]);
+ return get(namespace, className);
+ },
+
+ deserialize: function(manager, params) {
+ var modelClass, routeMatcher, param;
+
+ if (modelClass = this.modelClassFor(manager)) {
+ return modelClass.find(params[paramForClass(modelClass)]);
+ }
+
+ return params;
},
serialize: function(manager, context) {
+ var modelClass, routeMatcher, namespace, param, id;
+
+ if (modelClass = this.modelClassFor(manager)) {
+ param = paramForClass(modelClass);
+ id = get(context, 'id');
+ context = {};
+ context[param] = id;
+ }
+
return context;
},
@@ -88,7 +88,7 @@ Ember.State = Ember.Object.extend(Ember.Evented, {
return !get(this, 'childStates').length;
}).cacheable(),
- setupControllers: Ember.K,
+ connectOutlets: Ember.K,
enter: Ember.K,
exit: Ember.K
});
@@ -448,7 +448,7 @@ Ember.StateManager = Ember.State.extend(
// 1. Normalize arguments
// 2. Ensure that we are in the correct state
// 3. Map provided path to context objects and send
- // appropriate setupControllers events
+ // appropriate connectOutlets events
if (Ember.empty(name)) { return; }
@@ -527,9 +527,8 @@ Ember.StateManager = Ember.State.extend(
state = this.findStatesByRoute(state, path);
state = state[state.length-1];
- state.fire('setupControllers', this, context);
+ state.fire('connectOutlets', this, context);
}, this);
- //getPath(root, path).setupControllers(this, context);
},
getState: function(name) {
Oops, something went wrong.

1 comment on commit b71cae7

👍

Please sign in to comment.