-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simplify and modularize app/router initialization #10256
Conversation
The CLI resolver always treats type keys with the name of `main` as top-level objects. For example, `router:main` resolves to `app/router.js` instead of `app/routers/main.js`. This change updates the default resolver to always look for `foo-bar:main` as `App.FooBar`. This is in anticipation of application init/boot cleanup that we are doing as part of the FastBoot effort.
} | ||
} | ||
|
||
function logLibraryVersions() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While we are refactoring, it might be better to declare these as
var logLibraryVersions;
// later
Ember.runInDebug(function(){
logLibraryVersions = function(){
};
});
// init Function
Ember.runInDebug(function(){
logLibraryVersions();
});
and use Ember.runInDebug
. Right now, emberjs-build is configured to strip out Ember.debug
statements. So, if this code ultimately becomes a no op (Ember.debug
is the only output here, no other side effects), it'd be nice if it could be stripped out at build time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fivetanley - I agree in principle, but having Ember.debug
nested inside of Ember.runInDebug
would throw errors (because defeatureify doesn't like nested stripped blocks). Some more finagling would be required to deal with that (still totally doable though).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am happy to do whatever here, we just extracted these as-is to make the init
method easier to read.
Instead of storing a `__container__` on the Application, this commit stores an ApplicationInstance, which manages the per-instance lifecycle. The next step will be allowing multiple `ApplicationInstance`s to exist at once, enabling a single app to serve multiple FastBoot requests at a time. This is a conceptual improvement that brings application “reset” in line with the destruction infrastructure. As part of this commit, we eliminated an ad-hoc call to router.reset(), allowing that logic to happen as a natural consequence of destruction of the `ApplicationInstance`. In general, the goal of this commit is to move all responsibility for application state into an object that manages it.
@@ -253,6 +253,8 @@ var Application = Namespace.extend(DeferredMixin, { | |||
customEvents: null, | |||
|
|||
init: function() { | |||
this._super(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this._super.apply(this, arguments);
looks good, interested in seeing how this refactoring plan progresses. |
@return {Ember.Container} the configured container | ||
*/ | ||
buildContainer: function() { | ||
buildInstance: function() { | ||
var container = this.__container__ = this.__registry__.container(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we maintain __container__
on the Application
as well as the ApplicationInstance
? Removing it from the Application
might not be worth it until 2.0, since it is so frequently accessed despite being "private" (e.g. from the Ember Inspector). Perhaps we should flag it for 2.0 removal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dgeb - Ember.Application.create().__container__
is not removed here (AFAICT). Are you suggesting that ApplicationInstance.create().__container__
should be used instead of ApplicationInstance.create().container
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, @wycats and I discussed this a bit and felt that, as much as this fact might pain us, the __container__
API is de facto public and removing it would be very annoying to a great many people. Given that it is not particularly onerous to continue supporting it, at least until 2.0, we thought it best to preserve it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you suggesting that
ApplicationInstance.create().__container__
should be used instead ofApplicationInstance.create().container
?
@rwjblue I wasn't suggesting this, but I'd recommend doing so IF ApplicationInstance
becomes public itself. Since that doesn't appear to be the case in this PR, I'd hold off until then.
Given that it is not particularly onerous to continue supporting it, at least until 2.0, we thought it best to preserve it.
@tomdale Those are my thoughts as well. Perhaps tag it with the 2.0TODO
flag mentioned above?
Looks like a good incremental refactor 👍 I still have some questions about our next steps (re: per-instance registries and the upcoming changes to initializers) but those can be dealt with down the line. |
Based on feedback from @stefanpenner, we moved responsibility for creating the default container from the `Application` to the `ApplicationInstance`. Instead, the instance points back at the application’s registry, using it as the basis for a new container. This commit also adds initial documentation to the application instance.
We also introduced a deprecation guide for the recent registry/container split, and moved that deprecation behind a feature flag. Once this PR is merged, we should make sure that emberjs/website#1951 is merged along with it so that the deprecation URL works correctly. |
This commit introduces a new (feature flagged) API for adding instance initializers. Instance initializers differ from normal initializers in that they are passed the app instance rather than a registry, and therefore can access instances from the container in a safe way. This design not only allows us to avoid expensive app setup for each FastBoot request, it also minimizes the amount of work required between acceptance test runs (once the testing infrastructure is updated to take advantage of it). This commit also removes a previously introduced deprecation that was not behind a feature flag. That deprecation (when emitted with the feature flag enabled) now points to a comprehensive deprecation guide.
@tomdale - I realize that you are not changing the fundamentals here, but I'd love to see a test with |
@@ -368,6 +370,11 @@ export default EmberObject.extend({ | |||
if (factory) { return factory; } | |||
}, | |||
|
|||
resolveMain: function(parsedName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add doc to this new method and annotate it public/private
Simplify and modularize app/router initialization
Will instance initializers always run after all normal initializers? |
How to migrate this initializer, which needs to access a service, waits for a resolving promise and dynamically sets up routes? import Router from '../router';
export function initialize(registry, application) {
var session = registry.lookup('service:session');
application.deferReadiness();
session.load()
.then(function(config) {
if (config.fancyRoute) {
Router.map(function() {
this.route('fancyRoute');
});
}
application.advanceReadiness();
})
.catch(function(e) {
Ember.Logger.error('[session-initializer]', e);
});
}
export default {
name: 'session',
initialize: initialize
}; |
I support the question by @fpauser Could you suggest how to migrate such cases @tomdale @rwjblue ? Thanks. I tried to use deferReadiness in the instance initializer http://emberjs.jsbin.com/xutebutoma/1/edit?html,css,js,console,output But it seems it's not possible or I am doing it wrong. In my case, we have custom dependency management solution for initializers https://github.com/n-fuse/ember-initializer-dm/blob/master/main.js. It assumes that each of the initializers returns a Promise and defines its dependencies via |
@OrKoN Yes, instance initializers should always run after app initializers. If this is not happening for you please file an issue. |
@fpauser Since your initializer is changing state based on a service, and services can change between app instances, you should move that code to an instance initializer. (Instantiating services before the app is booted could definitely lead to some undefined/strange behavior, for what it's worth.) I'm not sure if we support mutating the route map after the app instance has booted, but we should expose APIs for doing so if not. |
There is an open issue for this: #10979. It worked in 1.10 but not 1.11+ (possibly due to changes here)... |
I have created a jsbin http://emberjs.jsbin.com/zixivojiyi/1/edit?js,console,output to demonstrate the use case. It's not working because I don't know how to make the instance initializer wait for the promise outcome. |
@OrKoN It's not possible to defer app readiness in an instance initializer, since by definition instance initializers are only run after the app has finished booting. Sidebar on the semantics of application booting: "App readiness" (as in, To restate, the lifecycle of an Ember application running in the browser is:
If you want to delay showing the UI because there is some runtime setup you need to do (for example, you want to open a WebSocket before the app starts running), the correct solution is to use the Using |
@tomdale this is awesome description and I wish I could find it somewhere in the official documentation! |
^^^ 👍 would be great to add it to ember guides |
@tomdale: :+5: Thanks for that! |
Ember.Application
's initialization has evolved organically a number of times to support the transition from no router, to a globals-based router, and now the Ember CLI/ES6 modules world. Over time, this initialization code has accumulated some cruft that makes it difficult to reason about its behavior.This carefully constructed series of targeted refactors preserves the previous semantics as closely as possible (with one exception that we may want to roll back depending on real-world impact; see below). This refactoring is in anticipation of our work to support multiple sessions of a single
Ember.Application
instance for the FastBoot effort.The one exception to semantics-preservation is a small tweak to the globals resolver to bring it more in line with the Ember CLI resolver.
In the Ember CLI resolver, full paths named
something:main
would resolve toappname/something.js
–themain
suffix is special-cased to become a top-level lookup. In order to achieve something similar in the globals resolver, there are hardcoded registrations (registry.register('foo:main', Foo)
) forrouter
andstore
.We changed the globals strategy to be more like the Ember CLI strategy: any
something:main
lookup in globals mode gets resolved toApp.Something
.This is a slight change in semantics because the process of going from a full name to a factory first tries to go through the resolver before using any explicitly registered full names.
In order for this to become a problem:
foo:main
in an initializer with a hardcoded value that did not live atApp.Foo
(e.g. registeringweb-socket:main
with a hardcoded class other than App.WebSocket).App.WebSocket
model in their applicationNote that this problem does not exist for
App.Router
orApp.Store
, since they have always been special-cased by Ember and Ember Data respectively.If necessary, we could restrict the new behavior to
router:main
, but if we can get away with this minor semantics change, we think this would more closely align with how people already expect this to work, and how it does work in Ember CLI.