Skip to content

Commit 6a2cffc

Browse files
authored
Add owner to the list of properties of an object (#2680)
2 parents 821b9fb + 26cc76e commit 6a2cffc

File tree

7 files changed

+133
-21
lines changed

7 files changed

+133
-21
lines changed

app/components/object-inspector/properties-base.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ export default class PropertiesBase extends Component {
99
const data = {
1010
objectId: this.args.model.objectId,
1111
};
12+
1213
if (name !== '...') {
1314
data.property = name;
1415
}
16+
1517
this.port.send('objectInspector:sendToConsole', data);
1618
}
1719

app/components/object-inspector/property.hbs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,34 +30,36 @@
3030
<span class="mixin-property-icon mixin-property-icon-{{this.iconInfo.type}} inline-block rounded font-mono text-md text-white text-center select-none"></span>
3131
</span>
3232

33-
{{!-- Property Name --}}
34-
<span
35-
class="mixin-property-name text-base14 truncate"
36-
data-test-object-property-name
37-
>
38-
{{#if this.isService}}
39-
<span
40-
title="service"
41-
class="mixin-property--group"
42-
data-test-property-name-service
43-
>
44-
{{@model.name}}
45-
</span>
46-
{{else}}
47-
{{#if this.hasDependentKeys}}
33+
{{#unless this.isOwner}}
34+
{{!-- Property Name --}}
35+
<span
36+
class="mixin-property-name text-base14 truncate"
37+
data-test-object-property-name
38+
>
39+
{{#if this.isService}}
4840
<span
49-
title="computed"
41+
title="service"
5042
class="mixin-property--group"
43+
data-test-property-name-service
5144
>
5245
{{@model.name}}
5346
</span>
5447
{{else}}
55-
{{@model.name}}
48+
{{#if this.hasDependentKeys}}
49+
<span
50+
title="computed"
51+
class="mixin-property--group"
52+
>
53+
{{@model.name}}
54+
</span>
55+
{{else}}
56+
{{@model.name}}
57+
{{/if}}
5658
{{/if}}
57-
{{/if}}
58-
</span>
59+
</span>
5960

60-
<span class="mr-1">: </span>
61+
<span class="mr-1">: </span>
62+
{{/unless}}
6163

6264
{{#if this.isEdit}}
6365
{{#if this.isDate}}

app/components/object-inspector/property.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ export default class ObjectInspectorProperty extends Component<ObjectInspectorPr
4747
@equal('args.model.value.type', 'type-string')
4848
isString!: boolean;
4949

50+
get isOwner() {
51+
return this.args.model?.value?.type === 'type-owner';
52+
}
53+
5054
get isService() {
5155
return this.args.model?.isService;
5256
}
@@ -116,6 +120,10 @@ export default class ObjectInspectorProperty extends Component<ObjectInspectorPr
116120
return { type: 'getter', title: 'Getter' };
117121
}
118122

123+
if (this.isOwner) {
124+
return { type: 'owner', title: 'Owner' };
125+
}
126+
119127
return { type: 'n/a', title: 'N/A' };
120128
}
121129

app/styles/object_inspector.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,11 @@
120120
content: "T";
121121
}
122122
}
123+
124+
.mixin-property-icon-owner {
125+
background-color: var(--spec02);
126+
127+
&::before {
128+
content: "O";
129+
}
130+
}

ember_debug/object-inspector.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
GlimmerComponent,
2222
GlimmerReference,
2323
GlimmerValidator,
24+
getOwner,
2425
} from './utils/ember';
2526
import { cacheFor, guidFor } from './utils/ember/object/internals';
2627
import { _backburner, join } from './utils/ember/runloop';
@@ -31,6 +32,8 @@ let tagValue, tagValidate, track, tagForProperty;
3132

3233
const GlimmerDebugComponent = (() => GlimmerComponent?.default)();
3334

35+
const OWNER_SYMBOL = '__owner__'; // can't use actual symbol because it can't be cloned
36+
3437
// Try to use the most recent library (GlimmerValidator), else
3538
// fallback on the previous implementation (GlimmerReference).
3639
if (GlimmerValidator) {
@@ -491,6 +494,8 @@ export default class extends DebugPort {
491494

492495
if (prop === null || prop === undefined) {
493496
value = this.sentObjects[objectId];
497+
} else if (prop === OWNER_SYMBOL) {
498+
value = getOwner(this.sentObjects[objectId]);
494499
} else {
495500
value = calculateCP(object, { name: prop }, {});
496501
}
@@ -615,6 +620,7 @@ export default class extends DebugPort {
615620
* - Bar
616621
* - Foo
617622
* - EmberObject
623+
* - Owner (Container)
618624
* ```
619625
*
620626
* The "mixins" returned by this function directly represent these things too.
@@ -734,6 +740,27 @@ export default class extends DebugPort {
734740
tracked,
735741
);
736742

743+
const owner = getOwner(object);
744+
const ownerId = guidFor(owner);
745+
746+
if (owner && !mixinDetails.find((mixin) => mixin.id === ownerId)) {
747+
mixinDetails.push({
748+
name: 'Container',
749+
id: ownerId,
750+
expand: false,
751+
properties: [
752+
{
753+
name: OWNER_SYMBOL,
754+
value: {
755+
inspect: `<Owner:${ownerId}>`,
756+
type: 'type-owner',
757+
objectId: ownerId,
758+
},
759+
},
760+
],
761+
});
762+
}
763+
737764
this.currentObject = { object, mixinDetails, objectId };
738765

739766
let errors = errorsToSend(errorsForObject);
@@ -1284,6 +1311,7 @@ function calculateCP(object, item, errorsForObject) {
12841311
if (object instanceof ArrayProxy && property == parseInt(property)) {
12851312
return object.at(property);
12861313
}
1314+
12871315
return item.isGetter || property.includes?.('.')
12881316
? object[property]
12891317
: object.get?.(property) || object[property]; // need to use `get` to be able to detect tracked props

ember_debug/utils/ember.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ let ArrayProxy,
5252
computed,
5353
EmberObject,
5454
captureRenderTree,
55-
getEnv;
55+
getEnv,
56+
getOwner;
5657

5758
let Debug = emberSafeRequire('@ember/debug');
5859
let InternalsMetal = emberSafeRequire('@ember/-internals/metal');
@@ -72,6 +73,8 @@ let GlimmerReference = emberSafeRequire('@glimmer/reference');
7273
let GlimmerRuntime = emberSafeRequire('@glimmer/runtime');
7374
let GlimmerUtil = emberSafeRequire('@glimmer/util');
7475
let GlimmerValidator = emberSafeRequire('@glimmer/validator');
76+
let EmberOwner = emberSafeRequire('@ember/owner');
77+
let EmberApplication = emberSafeRequire('@ember/application');
7578

7679
let inspect = Debug?.inspect || InternalsUtils?.inspect;
7780
let subscribe = Instrumentation?.subscribe;
@@ -142,6 +145,12 @@ if (Ember) {
142145
get = emberSafeRequire('@ember/object')?.get;
143146
}
144147

148+
if (EmberOwner) {
149+
getOwner = EmberOwner.getOwner;
150+
} else {
151+
getOwner = EmberApplication.getOwner;
152+
}
153+
145154
export {
146155
Runloop,
147156
Debug,
@@ -188,4 +197,5 @@ export {
188197
GlimmerRuntime,
189198
GlimmerUtil,
190199
GlimmerValidator,
200+
getOwner,
191201
};

tests/ember_debug/object-inspector-test.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import EmberDebug from 'ember-debug/main';
2323
import setupEmberDebugTest from '../helpers/setup-ember-debug-test';
2424
import EmberRoute from '@ember/routing/route';
2525
import Controller from '@ember/controller';
26+
import { setOwner } from '@ember/application';
2627

2728
const GlimmerComponent = (function () {
2829
try {
@@ -1224,6 +1225,59 @@ module('Ember Debug - Object Inspector', function (hooks) {
12241225
'inspector: updateProperty',
12251226
]);
12261227
});
1228+
1229+
test('Inspected objects pass along their owner they have one', async function (assert) {
1230+
class Owner {}
1231+
class Foo {
1232+
bar = 'baz';
1233+
}
1234+
1235+
let owner = new Owner();
1236+
let inspected = new Foo();
1237+
let message = await inspectObject(inspected);
1238+
let ownerId = guidFor(owner);
1239+
1240+
assert.false(
1241+
message.details.some(({ name }) => name === 'Container'),
1242+
"Objects without an owner don't report an undefined owner in their last details object",
1243+
);
1244+
assert.strictEqual(message.details.length, 2);
1245+
1246+
setOwner(inspected, owner);
1247+
1248+
message = await inspectObject(inspected);
1249+
1250+
assert.strictEqual(
1251+
message.details.length,
1252+
3,
1253+
'Object with owner have an additional details object',
1254+
);
1255+
1256+
assert.strictEqual(
1257+
message.details.filter(({ name }) => name === 'Container').length,
1258+
1,
1259+
'Objects with an owner have exactly one container item in their details',
1260+
);
1261+
assert.deepEqual(
1262+
message.details.at(-1),
1263+
{
1264+
id: ownerId,
1265+
name: 'Container',
1266+
expand: false,
1267+
properties: [
1268+
{
1269+
name: '__owner__',
1270+
value: {
1271+
inspect: `<Owner:${ownerId}>`,
1272+
objectId: ownerId,
1273+
type: 'type-owner',
1274+
},
1275+
},
1276+
],
1277+
},
1278+
'the container is the last mixin in the list',
1279+
);
1280+
});
12271281
}
12281282

12291283
// @glimmer/component 1.0 doesn't seem to support 3.4, even though it has the manager API

0 commit comments

Comments
 (0)