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

Further refine the error messages around allowing normal JavaScript property gets #16148

Closed
wycats opened this issue Jan 18, 2018 · 14 comments
Closed
Assignees

Comments

@wycats
Copy link
Member

wycats commented Jan 18, 2018

This issue is tracking problems that come up due to allowing normal JavaScript property lookup to work with computed properties.

Context

We are in the process of landing a change that will allow you to use normal property lookups to get properties defined as computed properties in Ember.

In practice, what this means is that, in most cases (more later) you will be able to drop .get('key') and replace it with .key.

However, there are a handful of small historical issues that could make the transition a little more messy. If you landed on this issue, chances are you encountered a warning from Ember that you encountered one of these historical issues.

Computed Properties used to be directly on the prototype

Until Ember 3.0, internal implementation details of Ember caused computed properties to be directly present on objects, so if you didn't use .get(), you would see the internal "Ember descriptor" representing the computed property (a private API).

This internal implementation detail was never intended to be a public (or even intimate) API.

This internal implementation detail has now changed and the (still private) "descriptor" object has been relocated to the object's "meta" (also a private API). This allows us to make normal property lookup work, and in general, very few people were relying on the presence of this internal descriptor.

If you encountered an error related to this problem, you are probably using an addon that relies on this now-defunct private implementation detail, and your best bet is to follow the instructions in the error message (and ask for help in #dev-ember).

Ember Proxies are tricky

Secondly, since the new behavior relies on using defineProperty, it does not allow users of Ember proxies to stop using .get().

Previously, since all Ember objects required you to use .get() to access properties, you could uniformly use .get() in your code and never need to worry about whether an object was an Ember proxy.

However, now that you can use normal property access for computed properties, Ember proxies are special. If an object is implemented using an Ember proxy, it should advertise itself as requiring .get() to access its proxied properties.

This is analogous to other libraries in the JavaScript ecosystem, such as immutable.js, that expose an API that is different from normal JS property access patterns.

Since proxies are relatively rare in Ember compared to property access on other Ember objects, emberjs/rfcs#281 (now merged) felt that the cost of losing consistent property access (everyone types .get() everywhere, so you don't need to know when an object is "special") was worth the benefit of normally not needing to use .get().

During the transition, we are worried that people will accidentally migrate some .get()s on Ember proxies to regular property access, so we added an assertion to the code that fires if:

  • you use a normal property get on an Ember proxy
  • the property is not present on the object itself
  • unknownProperty returns a value other than undefined

(this development-mode assertion is implemented using ES2015 proxies, which are not yet present on all supported browsers, but will cover the browsers people typically use for development)

For the most part, this avoids the vast majority of mistakes but it does introduce a possible problem with code that "probes" objects to discover whether they implement protocols.

Here's what I mean by "probing" code:

if (obj.toJSON) {
  return obj.toJSON();
}

A more resilient way to write this code would be:

if ('toJSON' in obj && typeof obj.toJSON === 'function') {
  return obj.toJSON();
}

Unfortunately, apps are not always in control of the probing code (which could come from test frameworks, generic serialization logic, etc.).

Notably, "probing" code is only a problem if:

  • an Ember proxy finds its way to the probing code
  • the property is not present on the object itself (not true about toString, which is usually a real property of the Ember proxy itself)
  • and this is the important part: unknownProperty returns something other than undefined

It's definitely possible that an Ember proxy could return some object for every possible property, and that some probing code checks for a protocol that's not usually present on the wrapper object (like toString), which would trigger the assertion inappropriately.

This problem comes up now because probing code previously would only look at properties of the wrapper proxy object, but now, in order to provide good errors for people accidentally removing .get() that they shouldn't have, normal property accesses on Ember proxies become sensitive to the behavior of unknownProperty.

Again, the correct mental model going forward is that these objects (most notably the values returned from relationships in Ember Data) are now special objects and require a non-standard API, but the process of migration introduces this ambiguity.

If you got this error and didn't expect it, please reply to this issue with information about what happened so we have help improve the error message with more further advice and workarounds.

@mani-mishra
Copy link
Contributor

An example of the non resilient probing code is JSON.stringify implementation, which I encountered at #16323

@Dhaulagiri
Copy link
Contributor

Dhaulagiri commented Apr 11, 2018

I'm seeing this error get triggered by code we don't control in chai.js

// Chai.js code

if (value && typeof value.inspect === 'function' &&

// Error

You attempted to access the `inspect` property (of <DS.Errors:ember460>)

This is the line of test code that triggers this, where component.get('visible') in this case is an array of ember data records returned from a computed.uniq filter:

(0, _chai.expect)(component.get('visible')).to.contain(emberDataRecord);

@rwjblue
Copy link
Member

rwjblue commented Apr 11, 2018

@Dhaulagiri - That specific issue should have been addressed in emberjs/data#5340 (which was released in ember-data@3.1.0).

However, I do still believe there are some changes that we could make to reduce (and avoid the DS.Errors like issue). IMHO we should update the assertion system to allow null or undefined here:

let value = target.unknownProperty.call(receiver, property);
assert(messageFor(receiver, property), value === undefined);

@Dhaulagiri
Copy link
Contributor

@rwjblue sure enough, using ED 3.1 seems to fix it. Sadly we have been trapped on ED 2.12 for a long time so that fix won't be of immediate help to us as we try to upgrade ember itself, so I may explore your second suggestion.

@rwjblue
Copy link
Member

rwjblue commented Apr 11, 2018

@Dhaulagiri - FWIW, many/most of the ember-data upgrade blocking issues that happened in the 2.12 era have been addressed in ember-data@3.2.0-beta.2. Might be worth a try...

@denzo
Copy link

denzo commented Apr 12, 2018

@cibernox selected.then throws an actual error in Ember 3.1.0

https://github.com/cibernox/ember-power-select/blob/master/addon/components/power-select.js#L141

@rwjblue
Copy link
Member

rwjblue commented Apr 12, 2018

@denzo - Seems like a bug report should be filed in ember-power-select, no?

@huberts
Copy link

huberts commented Apr 17, 2018

@denzo - Have you reported this as a bug in ember-power-select? Maybe @cibernox is not aware of the problem. I have the same issue wtih power select in my app.

@cibernox
Copy link
Contributor

cibernox commented Apr 17, 2018

@huberts I was not. What is wrong exactly with typeof selected.then === 'function'? Seems like a pretty reasonable pattern. What is a safe way of checking if an object is a thenable?

@cibernox
Copy link
Contributor

@huberts @denzo The bug should be fixed in 2.0.0-beta.4 of ember-power-select.

@rwjblue
Copy link
Member

rwjblue commented Apr 21, 2018

There is an open PR in the works (over in #16560) to prevent the proxy assertion from applying to functions. This fixes a large number of the existing reported issues (e.g. the ember-power-select probing for selected.then), and still provides nice guidance for folks that may accidentally attempt to remove .get when retrieving CP values on a proxy.

@akshaisarma
Copy link

Just to clarify: with this change, are we still supposed to be able to use ember-metal functions on Proxy objects? I am getting this assertion triggered in testing when I call isEmpty on a Proxy object:

https://github.com/emberjs/ember.js/blob/master/packages/ember-metal/lib/is_empty.js#L45

  if (typeof obj.size === 'number') {
    return !obj.size;
  }

marclundgren pushed a commit to marclundgren/ember-power-select that referenced this issue Aug 27, 2018
…2>).

Since Ember 3.1, this is usually fine as you no longer need to use `.get()`
to access computed properties. However, in this case, the object in question
is a special kind of Ember object (a proxy). Therefore, it is still necessary
to use `.get('length')` in this case.

If you encountered this error because of third-party code that you don't control,
there is more information at emberjs/ember.js#16148, and
you can help us improve this error message by telling us more about what happened in
this situation."
@pixelhandler
Copy link
Contributor

pixelhandler commented Sep 21, 2018

@wycats do you know if any effort has been done to further refine the errors?

Sounds like we need to provide a good warning message for the scenario where dot access was attempted for a proxy object ?

Since Ember 3.1, this is usually fine as you no longer need to use .get() to access computed properties. However, in this case, the object in question is a special kind of Ember object (a proxy). Therefore, it is still necessary to use .get('length') in this case.

Also need to be sure this is well understood ^

Thanks @yininge for pointing out we need to revisit this one :)

Perhaps instead of You attempted to access the inspect property (of <DS.Errors:ember460>) we should identify that a proxy object was involved and explain that .get() is required ?

Per the 3.1 blog post we have instruction on when to use .get().

If you are calling get with a chained path. For example in this.get('a.b.c') if b is undefined the return value is undefined. Converting this to this.a.b.c when b is undefined would instead raise an exception.

If your object is using unknownProperty you must continue to use get. Using an ES5 getter on an object with unknownProperty will cause an assertion failure in development.

Ember Data returns promise proxy objects when you read an async relationship and from other API. Ember proxy objects, including promise proxies, still require that you call get to read values.

My best guess is that the last item for a proxy describes what needs more detail in an error message.

marclundgren pushed a commit to marclundgren/ember-power-select that referenced this issue Oct 23, 2018
…2>).

Since Ember 3.1, this is usually fine as you no longer need to use `.get()`
to access computed properties. However, in this case, the object in question
is a special kind of Ember object (a proxy). Therefore, it is still necessary
to use `.get('length')` in this case.

If you encountered this error because of third-party code that you don't control,
there is more information at emberjs/ember.js#16148, and
you can help us improve this error message by telling us more about what happened in
this situation."
marclundgren pushed a commit to marclundgren/ember-power-select that referenced this issue Nov 27, 2018
…2>).

Since Ember 3.1, this is usually fine as you no longer need to use `.get()`
to access computed properties. However, in this case, the object in question
is a special kind of Ember object (a proxy). Therefore, it is still necessary
to use `.get('length')` in this case.

If you encountered this error because of third-party code that you don't control,
there is more information at emberjs/ember.js#16148, and
you can help us improve this error message by telling us more about what happened in
this situation."
cibernox pushed a commit to cibernox/ember-power-select that referenced this issue Jan 2, 2019
…2>). (#1148)

Since Ember 3.1, this is usually fine as you no longer need to use `.get()`
to access computed properties. However, in this case, the object in question
is a special kind of Ember object (a proxy). Therefore, it is still necessary
to use `.get('length')` in this case.

If you encountered this error because of third-party code that you don't control,
there is more information at emberjs/ember.js#16148, and
you can help us improve this error message by telling us more about what happened in
this situation."
kswilster added a commit to salsify/ember-power-select that referenced this issue Jan 3, 2019
* Resolves typo. (cibernox#1055)

* Added another addon: https://github.com/IliasDeros/ember-data-power-select

* v2.0.0-beta.1

* Fix but by bumping minimum version of ember-basic-dropdown

* selectedItemComponent into beforeOptionsComponent (cibernox#1060)

Make `selectedItemComponent` available in `beforeOptionsComponent`. This will allow for a persistent header showing the overall selected items inside of `power-select-multiple`. This matters in the case where the list ends up scrolling.

* v2.0.0-beta.2

* Fix sandboxing

* Add extra params for trigger (cibernox#1062)

* Add factoryFor in 2.10 ember-try scenario (cibernox#1063)

* Improve test-helpers import path (cibernox#1067)

* Improve test-helpers import path

* Still use ember-native-dom-helpers for find and findAll

* Document new import path

* v2.0.0-beta.3

* Fix bad code sample

* `horizontalPosition` property api reference update (cibernox#1071)

* Update how-to-use-it.hbs (cibernox#1080)

typo

* Update installation.hbs (cibernox#1078)

small typo

* Update installation.hbs (cibernox#1079)

this might be a typo as well

* Update action-handling.hbs (cibernox#1081)

Sorry to submit several PRs. I create them as I encounter typos.
I tried compile only one for action-handling.hbs this time tough ;-)

* stop click propagation added to the docs (cibernox#1087)

* stop click propagation added to the docs

* added more info about stop click propagation in the docs

* Update addon to 3.1 (cibernox#1088)

* Update addon to 3.1

* Smarter way to decide wether or not we want to load jquery

* Modernize test suite part 1 (cibernox#1089)

* Modernize test suite part 2 (cibernox#1090)

* Modernize test suite part 3 (cibernox#1091)

* Modernize test suite part 4 (cibernox#1092)

* Modernize test suite part 5 (cibernox#1093)

* Remove more usages of ember-native-dom-helpers

* Add more qunit-dom

* fixes cibernox#1094 (cibernox#1097)

* qunit-dom is almost everywhere (cibernox#1096)

* v2.0.0-beta.4

* Simplify tests a bit more

* Penultimate test refactor (cibernox#1098)

* Remove all traces of find/findAll from ember-native-dom-helpers

* Changed self.window to window and self.document to document (cibernox#1099)

* v2.0.0-beta.5

* Pre-render docs site at build time (cibernox#1102)

This PR enables Fastboot pre-rendering of the entire docs site using [prember[(https://github.com/ef4/prember). For URL discovery, it's using the newly-published [prember-crawler](https://github.com/ef4/prember-crawler)

I tested the `_redirects` on netlify to confirm it will work, and it does. By default, prember outputs an `_empty.html` file that contains your original (empty) index.html content, since your pre-rendered index.html file will have actual content in it.

* Making helpers more async (cibernox#1100)

* Fix last set of tests

* Add two more tests

* Improve component's behavior in beta (cibernox#1104)

* Fix typo

* Remove native dom helpers (cibernox#1105)

* Remove dependency on ember-native-dom-helpers

* Not used any more

* Update changelog

* v2.0.0

* Update ember-concurrency to fix bug in beta and canary (cibernox#1106)

* Drop node 4

* Add extra params for `selectedItemComponent` on trigger (power-select-multiple) (cibernox#1111)

* Fix: add extra hash to power-select-multiple selectedItemComponent

* Test: Add a couple missing tests for power-select-multiple with custom components

* Test: Update to use new `qunit-dom` test syntax

* v2.0.1

* Remove direct access to then (cibernox#1116)

* v2.0.2

* Remove console-log from test helper (cibernox#1119)

* typo (cibernox#1118)

* Remove console.log from test support (cibernox#1108)

* v2.0.3

* Bind title to the trigger (cibernox#1126)

* Fix tests failing due to new assertion in triggerKeyEvent (cibernox#1129)

* Replaced return value of maybePlaceholder for IE to undefined (cibernox#1128)

* v2.0.4

* Update to ember-cli 3.2 (cibernox#1132)

* Update to 3.3 (cibernox#1137)

* 2.X is not in beta

* guard against destroying hook when calling deactivate (cibernox#1144)

* 2.0.5

* Update to ember-cli 3.4 (cibernox#1149)

* Update to ember-cli 3.4

* Fix template-lint errors

* Update ember-data to 3.4 (cibernox#1150)

* Test and possible fix for cibernox#1147 (cibernox#1151)

* v2.0.6

* Only load polyfill when not in FastBoot environment (cibernox#1155)

fixes cibernox#1154

* v2.0.7

* v2.0.7

* fix: typeof returns string, broke polyfill when necessary (cibernox#1157)

* v2.0.8

* Fix typo in api-reference.hbs (cibernox#1158)

* use .set() to avoid assertion failure under Ember 3.4 (cibernox#1162)

* Add animationEnabled to be passed down to ember-basic-dropdown (cibernox#1165)

* Add highlightOnHoverEnabled to be used in options.js (cibernox#1166)

* Add highlightOnHoverEnabled to be used in options.js

When false, will disable automatically highlighting an option
when hovered over with the mouse. This will prevent unintentionally
selecting a new value when tabbing through a form with an EPS field
while the user's mouse is where the list will appear when the EPS
gains and then looses focus.

* Renamed highlightOnHoverEnabled to highlightOnHover

* v2.0.9

* Change slack for discord (cibernox#1169)

* close -> clone (cibernox#1176)

* close -> clone

'clone' is the intended word in the context, and the spelling error 'close' may cause some confusion to the reader

* spelling error fixes

close -> clone
debounding -> debouncing

just two small mistakes, can be confusing in context for the reader

* Fix build error (cibernox#1178)

* User active-voice instead of passive-voice

* Document using a computed property for the selected property

* Fix build error due to missing rel='noopener'

* Improve documentation (cibernox#1177)

* User active-voice instead of passive-voice

* Document using a computed property for the selected property

* Fix build error due to missing rel='noopener'

* minor typos

* Changed npm and bower for yarn

Changed instructions for contributing from `npm install` and `bower install` to `yarn install` as per cibernox#1179 to reflect updated build tools.

* Fix typo

* Allow select to open with click (cibernox#1181)

* v2.0.10

* v2.0.11

* Fix inconsistent focus of the input in single selects (cibernox#1180)

* v2.0.12

* v2.0.13

* v2.0.14

* v2.0.15

* Fix npm audit warnings (1 critical, 2 high) (cibernox#1183)

* Allow direct imports from node_modules (cibernox#1182)

* v2.1.0

* v2.2.0 (cibernox#1185)

* [BUGFIX] Allow test helpers to work with EBD >=1.0.5 (cibernox#1190)

In EBD 1.0.5 a fix for better a11y was introduced. It makes aria-owns to only be present on the trigger when
the dropdown is open. Test helpers in EPS relied in that property being there all the time, but now
they work both ways.

* v2.2.1

* Specify that only strings are searchable

* Update all dependencies December 2018 (cibernox#1194)

* Update all dependencies December 2018

* Fix sass complaint

* Fix tests

* Replace ember-cli-eslint with plain eslint (cibernox#1195)

* "You attempted to access the `length` property (of <(unknown):ember712>). (cibernox#1148)

Since Ember 3.1, this is usually fine as you no longer need to use `.get()`
to access computed properties. However, in this case, the object in question
is a special kind of Ember object (a proxy). Therefore, it is still necessary
to use `.get('length')` in this case.

If you encountered this error because of third-party code that you don't control,
there is more information at emberjs/ember.js#16148, and
you can help us improve this error message by telling us more about what happened in
this situation."
@rwjblue
Copy link
Member

rwjblue commented Jun 3, 2019

Closing this for now. Happy to reopen, but these messages have been around for quite a long time now and we aren't working on changing them.

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

No branches or pull requests

9 participants