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

[WIP] Rewrite to use native Proxy #627

Closed

Conversation

BryanCrotaz
Copy link

@BryanCrotaz BryanCrotaz commented Dec 7, 2021

Work in progress. Using existing test suite plus some more edge cases and preserving public API.

Enables developer to use normal JS semantics:

var output = changeset.gruffalos.lastObject.say('grrr');
changeset.people[1].name.first = 'Bryan';

Really useful for having nested components:

{{#let (changeset this.model) as |changeset|}}
  {{#each changeset.people as | person |}}
    <PersonForm @model={{person}}/>
  {{/each}}
{{/let}}

PersonForm doesn't have to know that this is a changeset. The save and rollback can be at a higher level.

export default class ChangesetArrayProxyHandler<T extends Input> implements IChangesetProxyHandler<T> {
constructor(source: T, _options?: ProxyOptions) {
if (source?.constructor?.name === 'ArrayProxy') {
this.__sourceProxy = source as ArrayProxy<any>;

Choose a reason for hiding this comment

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

these should either by symbol properties, or private properties, as they may conflict with folks data

Copy link
Author

Choose a reason for hiding this comment

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

you mean __sourceProxy etc?

Copy link
Author

Choose a reason for hiding this comment

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

I don't think these do conflict. Proxy is not a wrapper that falls back to the internals like Ember.ObjectProxy. It explicitly calls in with get and set and you do your own fallback. __sourceProxy is private api so it's not available through get and set, and if there was a name conflict it would fall through to the proxied object correctly.


public get(_target: T, key: string): any {
// extra stuff
switch (key) {

Choose a reason for hiding this comment

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

you can shorten this by doing if (typeof this.readArray[key] === 'function') return this.readArray[key];, etc

Copy link
Author

Choose a reason for hiding this comment

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

If you notice I have two different ways of doing it
readArray[key] returns the proxy or the source if there's no changes
writeArray[key] creates a proxy if it doesn't exist and returns it

I thought about using a map to choose which one to return. Otherwise readKeys.includes[key] etc

Thoughts?

Copy link
Author

Choose a reason for hiding this comment

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

typeof this.readArray[key] === 'function' doesn't work - typescript expects the index to be a number. Is there a way to iterate methods on an array without [key]?

Choose a reason for hiding this comment

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

I aw not sure

@action
public getValue(key: string | symbol) {
switch (key) {
// backwards compatible

Choose a reason for hiding this comment

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

yay back compat

@@ -0,0 +1,7 @@
export default function isUnchanged(a: any, b: any) {

Choose a reason for hiding this comment

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

I'm loving all these utilities

@NullVoxPopuli
Copy link

So, I like where this is going, but the real implementation is here: https://github.com/validated-changeset/validated-changeset
is there a reason you opted to only update this library? or is there a PR I'm missing? 🤔

@BryanCrotaz
Copy link
Author

I didn't realise. But looking at it, validated-changeset is supposed to be independent of Ember. So it shouldn't use Tracking for example. I don't think it's possible to pull tracking out of my design. I was aiming for a really clean and simple design that gets rid of a ton of cruft.

@NullVoxPopuli
Copy link

NullVoxPopuli commented Dec 7, 2021

validated-changeset is supposed to be independent of Ember

is true -- but it's also where most of the complexity comes in.

is the goal to eliminate the need for validated-changeset?

and fwiw, I think it is possible to a better native proxy design with validated-changeset and provide deep reactivity as well -- but, I want to make sure I know what your goals are?

@BryanCrotaz
Copy link
Author

My goals are to have ember-changes et working for deep nesting without get and set. If that means submitting to validated- changeset instead then that's fine by me

@NullVoxPopuli
Copy link

NullVoxPopuli commented Dec 7, 2021

Ah ok, we can probably do even less work then. Thanks for clarifying!

}

public get isValid(): boolean {
// TODO: validate this level

Choose a reason for hiding this comment

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

Todo!

// TODO: validate this level

// now look at all the nested proxies
for (var proxy of this.__nestedProxies.values()) {

Choose a reason for hiding this comment

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

Why reimplement these behaviors? Couldn't you return tte value from the underlying validated-changeset?

throw `changeset.${key} is readonly`;
}
// nested keys are separated by dots
var [localKey, subkey] = splitKey(key as string);

Choose a reason for hiding this comment

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

What about deeply nested keys? Maybe 5+ layers deep?

// have to use get() here because source might be an EmberProxy
const oldValue = get(this.__source, localKey);
const unchanged = isUnchanged(value, oldValue);
let changes = this.__changes;

Choose a reason for hiding this comment

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

Wait, does this PR remove and re-implement validated-changeset?

Sorry if obvious

@BryanCrotaz
Copy link
Author

closing in favour of adopted-ember-addons/validated-changeset#150

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.

2 participants