[FEATURE factory-for] Implement factoryFor#14360
Conversation
fed86bf to
ef5301e
Compare
|
/cc @mixonic |
|
Haven't had a chance to review in detail, but I did notice that this still needs feature flagging. |
c89e3fd to
6d1d689
Compare
|
@stefanpenner I think this is good for first round of review. Currently no feature flagging has been done because these are effectively internal changes with no user-land implications. One outstanding thing is if we need to inject the deprecated container in this new public API. |
6d1d689 to
b732ef5
Compare
O_o, isn't the whole point of the RFC to expose this as public API? |
I'm not quite sure what this means, the container is not deprecated. Will review here. |
|
☔ The latest upstream changes (presumably #14355) made this pull request unmergeable. Please resolve the merge conflicts. |
0d270ce to
d649c52
Compare
|
☔ The latest upstream changes (presumably #14428) made this pull request unmergeable. Please resolve the merge conflicts. |
c46fd47 to
f3ecc62
Compare
mixonic
left a comment
There was a problem hiding this comment.
Thanks so much for getting this going @chadhietala. The big item is that lookup should still be using double extend. If you want to add a second feature flag (ideally in a followup) that experimentally switches lookup to the non-double-extended factory, that would definitely be welcome as a distinct feature flag.
| _nameToClass(type) { | ||
| if (typeof type === 'string') { | ||
| type = getOwner(this)._lookupFactory(`model:${type}`); | ||
| type = getOwner(this).factoryFor(`model:${type}`).class; |
There was a problem hiding this comment.
There is a logic change happening here- Where the old version was returning the double-extended class now the registered class is being returned. How does that impact the logic?
There was a problem hiding this comment.
Hm seems like backwards-compat for pre-1.13 per this hook implementation. Super unfortunate that any of the class stuff is still used that way. Seems like it needs a deprecation cycle in ember-data.
There was a problem hiding this comment.
It seems there is some use of the types in Ember-Data. For example here attributes are read off the class. Removing the double-extend means the attributes CP would be called and memoized on the base class, and thus shared between test runs etc.
This is definitely a change, described in the design section.
Some other examples of libraries this would impact are listed in the drawbacks section.
| @@ -75,7 +75,8 @@ export default class Environment extends GlimmerEnvironment { | |||
|
|
|||
| this._definitionCache = new Cache(2000, ({ name, source, owner }) => { | |||
| let { component: ComponentClass, layout } = lookupComponent(owner, name, { source }); | |||
There was a problem hiding this comment.
ComponentClass seems a poor name now. componentFactory if you intended to pass the whole thing through instead of just the class?
packages/container/lib/container.js
Outdated
|
|
||
| let manager = { | ||
| class: factory, | ||
| create(options = {}) { |
There was a problem hiding this comment.
I expected the implementation here to be
let factory = this._lookupFactory(name);
return factory.create(options);for the initial pass of factoryFor create per https://github.com/emberjs/rfcs/blob/master/text/0000-factoryFor.md#detailed-design. Additionally https://github.com/emberjs/rfcs/blob/master/text/0000-factoryFor.md#development-mode-proxy. In practice literally implementing as _lookupFactory(name).create(props) might not work, but it seems like it should and would preserve the double-extend behavior during instance creation. We should decide if this is really needed, will raise it in some conversations.
There was a problem hiding this comment.
Not sure if I follow. Can we not just transition all the internal stuff over to factoryFor?
There was a problem hiding this comment.
The intent was to encourage libs/apps to use the new API and drop their current reliance on the double-extended class, then change over internals and any other small breaking changes that will happen after most apps have refactored toward factoryFor and stopped expecting a double-extend.
packages/container/lib/container.js
Outdated
| if (isSingleton(container, fullName) && options.singleton !== false) { | ||
| container.cache[fullName] = value; | ||
| function isFactoryClass(container, fullname, { instantiate, singleton }) { | ||
| return (!isSingleton(container, fullname) || singleton === false) && (!shouldInstantiate(container, fullname) && instantiate === false); |
There was a problem hiding this comment.
putting the faster checks first seems ideal:
function isFactoryClass(container, fullname, { instantiate, singleton }) {
return (
(singleton === false || !isSingleton(container, fullname)) &&
(instantiate === false || !shouldInstantiate(container, fullname))
);
}thus avoiding the function calls in many cases.
packages/container/lib/container.js
Outdated
| deprecate('Using "_lookupFactory" is deprecated. Please use container.factoryFor instead.', false, { id: 'container-lookupFactory', until: '2.12.0', url: 'TODO' }); | ||
| } | ||
|
|
||
| return factoryFor(this, this.registry.normalize(fullName), options); |
There was a problem hiding this comment.
can this function be renamed now, since it is not the factoryFor one would normally be looking for here.
packages/container/lib/container.js
Outdated
| } | ||
|
|
||
| let value = instantiate(container, fullName); | ||
| return instantiateFactory(container, fullName, options); |
There was a problem hiding this comment.
So this meanscontainer.lookup is now based on the the non-double-extended factory? This seems like the behavior we didn't intend to change until _lookupFactory is removed.
There was a problem hiding this comment.
yes, we should only have 1 behavior in the system at any point in time, once we cut over it should be all no double extend.
packages/container/lib/container.js
Outdated
| assert('fullName must be a proper full name', this.registry.validateFullName(fullName)); | ||
|
|
||
| if (isFeatureEnabled('container-factoryFor')) { | ||
| deprecate('Using "_lookupFactory" is deprecated. Please use container.factoryFor instead.', false, { id: 'container-lookupFactory', until: '2.12.0', url: 'TODO' }); |
There was a problem hiding this comment.
Must be until: '2.13'. 2.12 is an LTS release and must contain this deprecation.
| function lookupFactory(name, container, options) { | ||
| let factory; | ||
| if (isFeatureEnabled('container-factoryFor')) { | ||
| expectDeprecation(() => { |
There was a problem hiding this comment.
There is also an ignoreDeprecation which might be appropriate/useful here. Example:
|
|
||
| QUnit.test('A deprecated `container` property is appended to every object instantiated from a non-extendable factory, and a fake container is available during instantiation.', function() { | ||
| // This is testing that container was passed as an option | ||
| QUnit.skip('A deprecated `container` property is appended to every object instantiated from a non-extendable factory, and a fake container is available during instantiation.', function() { |
There was a problem hiding this comment.
This test shows why we need to keep the "double extend" the default path for lookup until at least 2.13.
packages/container/lib/container.js
Outdated
| throw new Error(`You attempted to set "${prop}" on a factory manager created by container#factoryFor. A factory manager is a read-only construct.`); | ||
| } | ||
| }; | ||
| manager = new Proxy(manager, validator); |
There was a problem hiding this comment.
Would like to have tests included that:
- getting a property that is not
classorcreatethrows an error - setting a property throws an error
- calling
instanceofwith the return value fromfactoryForthrows an error
Per: https://github.com/emberjs/rfcs/blob/master/text/0000-factoryFor.md#development-mode-proxy
|
☔ The latest upstream changes (presumably #14487) made this pull request unmergeable. Please resolve the merge conflicts. |
c993e4b to
f6dfba0
Compare
|
I made some changes to this, but likely needs further guidance. |
|
☔ The latest upstream changes (presumably #14545) made this pull request unmergeable. Please resolve the merge conflicts. |
3a63382 to
71c9815
Compare
|
Quick profiles from Travis-Web. This is consistent across runs. Looked at the homepage and build status pages. Benefits are bound to number of objects created. Before: (Prod / Feature Flags Off) After: (Prod / Feature Flags On) |
The public factoryFor API should always return `class` as the no-double-extend version. However internal APIs already refactored to use FACTORY_FOR should use the double extended version if the no-double-extend feature flag is false.
71c9815 to
bac3a08
Compare
|
@chadhietala - I assume the |
| this.inject('route', '_environment', '-environment:main'); | ||
| }, | ||
|
|
||
| [FACTORY_FOR](fullName, options) { |
There was a problem hiding this comment.
Lets make this part of the ContainerProxyMixin, this would simplify issues in ember-test-helpers land and actually keep the logic in the correct place (currently .lookup and ._lookupFactory are defined in ContainerProxyMixin so that seems like the logical place).
|
|
||
| if (isFeatureEnabled('ember-factory-for')) { | ||
| EngineInstance.reopen({ | ||
| factoryFor(fullName, options) { |
There was a problem hiding this comment.
This can be moved to the mixin also...
packages/container/lib/container.js
Outdated
| import { assert, deprecate, runInDebug, isFeatureEnabled } from 'ember-metal'; | ||
|
|
||
| const CONTAINER_OVERRIDE = symbol('CONTAINER_OVERRIDE'); | ||
| const HAS_PROXY = typeof Proxy === 'function'; |
There was a problem hiding this comment.
Can we move this into ember-utils and export as HAS_NATIVE_PROXY? Very similar to what was done in #14649 for packages/ember-utils/lib/weak-map-utils.js.
tests/node/helpers/build-owner.js
Outdated
| @@ -1,5 +1,14 @@ | |||
| module.exports = function buildOwner(Ember, resolver) { | |||
| var Owner = Ember.Object.extend(Ember._RegistryProxyMixin, Ember._ContainerProxyMixin); | |||
| var FACTORY_FOR = Ember.Container.__FACTORY_FOR__; | |||
There was a problem hiding this comment.
See comments in #14360 (review) and emberjs/ember-test-helpers#191, we should be able to remove this.
155e807 to
aefb6ed
Compare
I've made Robert's final requested change
|
I've done some benchmarks against todomvc. I would expect the impact on a larger app to be much more significant (as @chadhietala's profiling shows). This PR may have a minor performance regression: The However the I think we can continue to explore issues around backwards compatibility, performance, and the migration path to @chadhietala Thanks much for your patience and push on this gnarly, complex, cross-cutting part of Ember's API. The payoff (especially in big apps) is going to be really notable. |
|
👍 |
|
@chadhietala Awesome! Nice to be able to move into a public API for this + great performance improvements! ⛵️ |






Implements
lookup.factoryForto avoid double extending all objects.See RFC