Skip to content
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

Container / Registry Reform #11440

Merged
merged 12 commits into from
Jul 23, 2015
Merged

Container / Registry Reform #11440

merged 12 commits into from
Jul 23, 2015

Conversation

dgeb
Copy link
Member

@dgeb dgeb commented Jun 13, 2015

This is an implementation of RFC 46. The goal is to fully encapsulate and privatize the Container and Registry classes by exposing a select subset of public methods on Application and ApplicationInstance.

This is achieved by introducing two new proxy mixins: RegistryProxy and ContainerProxy.

Application now mixes in RegistryProxy.

ApplicationInstance mixes in both RegistryProxy and ContainerProxy.

RegistryProxy

RegistryProxy maintains the registry as a private __registry__ property.

The following registry methods are exposed publicly via RegistryProxy:

  • resolveRegistration - alias for resolve
  • register
  • unregister
  • hasRegistration - alias for has
  • registerOption - alias for option
  • registerOptions - alias for options
  • registeredOptions - alias for getOptions
  • registerOptionsForType - alias for optionsForType
  • registeredOptionsForType - alias for getOptionsForType
  • inject - alias for injection

ContainerProxy

ContainerProxy maintains the container as a private __container__ property.

The following container methods are exposed publicly via ContainerProxy:

  • lookup
  • lookupFactory

Privatization of Container and Registry

The introduction of these interfaces allows for the complete privatization of the Container and Registry classes. This allows for their architecture and documentation to be cleaned up. For instance, a Container can freely reference its associated Registry as registry rather than _registry, as it can be assumed that only framework developers will reference this property.

Simplification of Initializers

Application initializers now receive a single argument to initialize: application. Receiving two arguments is still allowed, but deprecated.

Likewise, ApplicationInstance initializers still receive a single argument to initialize: applicationInstance.

Allow Application Instance's Registrations to take Precedence over the Resolver

This work also addresses #11174 by only associating a resolver with the Application's registry and not with ApplicationInstance's registry.

This has a couple benefits:

  • It allows registrations made in the app instance's registry to be resolved before falling back to the app's registry, rather than hitting the resolver beforehand. This is important for scenarios such as testing in which custom registrations need to take precedence over the resolver.
  • It is also more efficient in the case when the app's registry's registrations are used to resolve, because it avoids calling the same resolver twice before finally getting the registration.

Remaining Action Items:

@tomdale
Copy link
Member

tomdale commented Jun 15, 2015

This looks great. 👍

@dgeb
Copy link
Member Author

dgeb commented Jun 18, 2015

@stefanpenner ping re: details on your proposal to replace lookupFactory. If it looks good, I'll include an implementation in this PR so we can get this merged.

@dgeb
Copy link
Member Author

dgeb commented Jun 29, 2015

@stefanpenner ping ping (the dreaded double ping, best employed on Monday mornings).

ref(registry, App);

// TODO2.0 remove deprecated case in which initialize takes two args
if (ref.length === 2) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is not going to land in a 1.13.x release, this can be removed I think.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is the question. I'd hate to land this post-2.0 and carry forward all these deprecations until 3.0.

@dgeb
Copy link
Member Author

dgeb commented Jul 8, 2015

The lack of movement on this issue concerns me. If we do nothing, the following will need to be carried forward into all Ember 2.x releases:

  • Application initializers that accept two arguments: application and registry. This effectively makes Registry public, whether it's recognized as such in the docs. It also leaves ambiguity for developers as to whether to call register and inject on application or registry.
  • Application instance initializers that accept two arguments: applicationInstance and container. This effectively makes Container public as well. It doesn't provide a clear public path to access the instance-level registry. Some developers go through container._registry, others through applicationInstance.registry - both are marked private.
  • Treatment of Registry and Container as "de facto" public classes. This means that we need to read the tea leaves of how they're being accessed by developers in practice whenever they are changed.

I'm trying to get attention to this issue now because deprecating the above is messy, and those messy deprecations will need to be carried forward through all 2.x releases. I'd like to avoid a second long-lasting wave of container / registry deprecation warnings like the one experienced after the SSR refactor.

On the other hand, if we establish clear public interfaces for 2.0, we can demystify Ember's DI approach through docs and guides. Even if this approach is tweaked during 2.x (e.g. access to lookupFactory), we can more surgically deprecate the old behaviors because the public interfaces will be clear.

I know time is short now, but please consider this low hanging fruit. Just say the word and I'll rebase ASAP.

Note: this PR also contains a fix to #11174 which should probably be merged regardless of the bigger picture changes.

@stefanpenner
Copy link
Member

I would like to give this some time. But my plate of awfully full. I'll try to find time yet this week. But my queue is quite saturated

@tomdale
Copy link
Member

tomdale commented Jul 8, 2015

@stefanpenner If we cannot get your feedback on this, can we get this merged in? We are quickly running out of time, @dgeb has put a ton of effort getting it backwards compatible, and both me and @wycats are 👍 on it.

@stefanpenner
Copy link
Member

@stefanpenner If we cannot get your feedback on this, can we get this merged in?

Sure, but I reserve the right to complain violently down the road :trollface:

@rwjblue
Copy link
Member

rwjblue commented Jul 8, 2015

Sure, but I reserve the right to complain violently down the road :trollface:

Have no fear, I even complain violently about the PR's that I made.

@dgeb
Copy link
Member Author

dgeb commented Jul 16, 2015

I made significant progress with a rebase last night, but still have a couple failing builds: https://travis-ci.org/emberjs/ember.js/builds/71200614

Will do another pass of rebasing/fixing today ...

@rwjblue
Copy link
Member

rwjblue commented Jul 16, 2015

The old-jquery build was a spurious failure, I restarted and it is passing now.

The node-tests build is a legit failure, you need to update https://github.com/emberjs/ember.js/blob/master/tests/node/app-boot-test.js#L97 to use app.register instead of app.registry.register (I think).

@dgeb
Copy link
Member Author

dgeb commented Jul 16, 2015

Thanks for the tip @rwjblue - all green now!

I don't know whether you want to merge this or wait until @wycats and I have a chance to revisit lookupFactory.

@dgeb dgeb force-pushed the registry-reform branch 4 times, most recently from 49c95fd to ba47f70 Compare July 21, 2015 15:47
@rwjblue
Copy link
Member

rwjblue commented Jul 21, 2015

@dgeb and I just discussed at length the path forward for this feature. The following is the rough result:

This PR does quite a few things, but the vast majority of the changes are additive and not breaking. This means we can add those features in the 2.x cycle at any time, behind a feature flag.

The primary areas that we identified that were potentially breaking were:

  1. Application initializers will receive a single argument (the application instance with the container proxy mixed in). This PR already includes an arity check for initializer.initialize function. If two arguments are expected, they will be provided properly.
  2. The ApplicationInstance currently exposes the container publicly. This will need a little bit of work, but a potential solution for non-breaking changes would be:
Object.defineProperty(ApplicationInstance, 'container', {
    configurable: true,
    enumerable: false,
    get() {
      var instance = this;
      return {
        lookup() { 
          Ember.deprecate('Using `ApplicationInstance.container.lookup` is deprecated. Please use `ApplicationIntance.lookup` instead.');
          instance.lookup(...arguments);
      }
    }
  });

Both of those scenarios can be completely mitigated when the new feature is enabled.


Remaining action items (also added to description above):

@dgeb dgeb force-pushed the registry-reform branch 2 times, most recently from a56866c to e180c04 Compare July 22, 2015 13:44
dgeb added 12 commits July 22, 2015 23:49
Mix in to both `Application` and `ApplicationInstance` classes to
provide consistent public access to their internally maintained
registries.
Pass the related Application, which the instance can inspect to obtain
the initialization data that it needs.

This eliminates the need to set `applicationRegistry` on the instance,
which was always an anti-pattern.
The `RegistryProxy` mixin now maintains the registry instance as
`__registry__`.

As appropriate, the equivalent `RegistryProxy` methods are called
instead of directly accessing the `registry`.
Registry access must go through the Application now.
Now that container will be fully privatized, we can relax naming of
members like `registry` that won't be accessed outside of the
framework.
Mix in to `ApplicationInstance` class to provide public access to
a few select methods on the encapsulated container.
Instead, allow the application's resolver to be invoked only as part of
the fallback strategy for the application instance's registry.

This has a couple benefits:

* It allows registrations made in the app instance's registry to be
  resolved before falling back to the app's registry, rather than
  hitting the resolver beforehand. This is important for scenarios such
  as testing in which custom registrations need to take precedence
  over the resolver.

* It is also more efficient in the case when the app's registry's
  registrations are used to resolve, because it avoids calling the same
  resolver twice before finally getting the registration.

[Closes emberjs#11174]
* Ensure that `Container#_registry` remains supported for now.
  Can be deleted once cont/reg reform is enabled by default.

* Continue to expose `registry` and `container` on `ApplicationInstance`.
  These can be removed once cont/reg reform is enabled, but we’ll still
  need to expose `ApplicationInstance.container.lookup` until 3.0.

* Flag deprecation of Application initializer `initialize` arguments.
@dgeb
Copy link
Member Author

dgeb commented Jul 23, 2015

@rwjblue I've completed the tasks. This has been rebased, feature-flagged, and is ready for review.

rwjblue added a commit that referenced this pull request Jul 23, 2015
@rwjblue rwjblue merged commit 1d7b833 into emberjs:master Jul 23, 2015
@rwjblue
Copy link
Member

rwjblue commented Jul 23, 2015

Thanks @dgeb!

@krisselden
Copy link
Contributor

@dgeb if this is supposed to privatize things, did you miss this one? https://github.com/dgeb/ember.js/blob/c353c383b666474e5fb2c2d6fa17934946dcc9ca/packages/container/lib/container.js#L240

should that not be __container__ too?

@rwjblue
Copy link
Member

rwjblue commented Jul 23, 2015

@krisselden - This was affecting a public API to be used by initializers and instanceInitializers, but does not change access from within an object looked up from the container. Changing this.container.lookup to this.__container__.lookup would be a pretty big breaking change (and this RFC / PR does not provide an alternative for that use case at all).

I'm totally in favor of exposing some sort of limited public API for use here, but we must tread carefully and ensure no breakage...

@dgeb
Copy link
Member Author

dgeb commented Jul 23, 2015

@krisselden I personally am in favor of deprecating container in that case too. My preference would be to provide a single path for all lookups - through the app instance. I briefly floated the idea of injecting the app instance as an alternative to container, but response was lukewarm so I didn't pursue in the initial RFC / PR. Now that this PR has been merged, it's probably a good time to reopen this discussion.

Of course, I also agree with @rwjblue that we must tread carefully and ensure no breakage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants