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

Use Proxy.revokable and add revokeProxy to API #4

Merged
merged 11 commits into from
May 25, 2017

Conversation

alshakero
Copy link
Collaborator

@alshakero alshakero commented May 9, 2017

I'm implementing the needed specs right now.

Implements: Palindrom/Palindrom#134

@@ -8,6 +8,7 @@
/** Class representing a JS Object observer */
var JSONPatcherProxy = (function() {
function JSONPatcherProxy(root) {
this.proxifiedObjectsMap = [];
Copy link
Contributor

Choose a reason for hiding this comment

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

A WeakMap here would be more memory-friendly.

return proxy;
/* keeping track of all the proxies to be able to revoke them later */
this.proxifiedObjectsMap.push(proxy);
return proxy.proxy;
};
//grab tree's leaves one by one, encapsulate them into a proxy and return
JSONPatcherProxy.prototype._proxifyObjectTreeRecursively = function(
Copy link
Contributor

Choose a reason for hiding this comment

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

Could _proxifyObjectTreeRecursively automatically revoke proxy of when a child object is removed?

Copy link
Collaborator Author

@alshakero alshakero May 9, 2017

Choose a reason for hiding this comment

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

@warpech I started with this yesterday but figured that would render the child object useless even if it was cached earlier on purpose for later usages, like in this demo. Doing it in Palindrom made more sense since Palindrom.obj is ours and we give it to the user for use, so we have the right to destroy parts of it.

But the observed object in JSONPatcherProxies is not ours and I guess we should leave it, and its children, usable.

What do you think?

Copy link
Collaborator Author

@alshakero alshakero May 9, 2017

Choose a reason for hiding this comment

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

I see my mistake here. Children will emit patches to the same callback of the parent object even after they are deleted, which is problematic. I think disabling the traps inside _proxifyObjectTreeRecursively would be the safest option.

Copy link
Contributor

Choose a reason for hiding this comment

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

Great, then let's do it like you say.

Copy link
Member

Choose a reason for hiding this comment

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

@alshakero The jsbin throws 504 to me :| could you send it once again?

Forgive me if I get something wrong. I still don't get why can we revoke on deleteProperty by default.

But the observed object in JSONPatcherProxies is not ours and I guess we should leave it, and its children, usable.

If child proxy object "was cached earlier on purpose for later usages". It's a proxy object, so it's ours and we can destroy it. If user wants to cache his/hers object for later usages after it's unobserved by us, he/she can use originalObject or the object returned by delete/splice - I believe it's the original one https://github.com/Palindrom/JSONPatcherProxy/pull/4/files?w=1#diff-3bfa726203c6cb018f2321c18ca9298eR94 . I'm not sure, @alshakero is it?

Copy link
Collaborator Author

@alshakero alshakero May 10, 2017

Choose a reason for hiding this comment

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

@tomalec now I implemented the ability to disable traps for a given proxy, and this happens automatically in deleteProperty trap. It's more fail-proof. And I regret the revoking proposal, it's redundant now. I'll explain:

If an element (say an HTML input field) is bound to a sub-object of the state tree using Polymer binding. And a patch comes to delete that sub-object from the server, editing the field will throw a TypeError (in the time window of Polymer binding async update), and this error will be to the user in production, not to the developer. Any race condition will cause this issue and it will be very hard to reproduce.

WDYT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@tomalec the whole JSBin is down 🤒

Copy link
Member

Choose a reason for hiding this comment

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

Could you use jsfiddle.net?

Once delete patch is applied binding should be removed as well.
If you are talking about race conditions between browser-to-server and server-to-browser messages, OT should solve it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@tomalec I don't see any objective argument for revoking against disabling traps. And since I implemented disabling traps, I naturally lean to less redundant work. Do you have any reason to prefer throwing an error rather than covering the edge-case by simply disabling the proxy?

Copy link
Member

Choose a reason for hiding this comment

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

Could we setup a HO call, I feel I miss something in here.

*/
JSONPatcherProxy.prototype.revokeProxy = function(proxy) {
let deletedIndex;
this.proxifiedObjectsMap.forEach(function(el, index) {
Copy link
Contributor

Choose a reason for hiding this comment

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

With WeakMap this becomes simpler: this.proxifiedObjectsMap.get(proxy).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Totally forgot about its whole existence! Great idea.

@tomalec
Copy link
Member

tomalec commented May 10, 2017

@alshakero
Copy link
Collaborator Author

@tomalec here is my response in case it was hidden: #4 (comment)

- Modify TS typings accordingly
- Use `Map` instead of `WeakMap` (Itertable)
- Add specs for all new functions and checking for detached warnings
- Disable UglifyJS for now (No ES6 Support)
@alshakero
Copy link
Collaborator Author

@tomalec I think this is ready for review.

README.md Outdated

#### JSONPatcherProxy#switchCallbackOff () : void
Enables patches omitting (to both callback and patches array). Starting from the moment you call it.
Copy link
Member

Choose a reason for hiding this comment

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

I would think resume would resume observing/notifying changes, so it would "Disable patches omitting".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What an unlucky typo. I mean emitting.

return obj;
}
var instance = this;
var trapsInstance = Object.assign({}, traps, {path: path, instance: instance});
Copy link
Member

Choose a reason for hiding this comment

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

isn't

trapsInstance.path = path;
trapsInstance.instance = instance;

cheaper?

Or even just define traps in here, without a need to create 3 new objects?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's right. I thought not defining the functions (set, get etc..) again and just referencing them would be better. But this would be slower with every issued patch (this.instance.defaultCallback is slow).

Moving them inside could be a little slower but for one time only. Faster with every patch.

I moved it inside.

@alshakero
Copy link
Collaborator Author

@tomalec is this good to merge?

README.md Outdated

Turns the proxified object into a forward-proxy object; doesn't emit any patches anymore, like a normal object..
Copy link
Member

Choose a reason for hiding this comment

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

Does .. stands for ellipsis, or it's just one dot too much?

/**
* Creates an instance of JSONPatcherProxy around your object of interest `root`.
* @param {Object|Array} root - the object you want to wrap
* @param {Boolean} showDetachedWarning - whether to log a warning when a detached sub-object is modified @see {@link https://github.com/Palindrom/JSONPatcherProxy#detached-objects}
Copy link
Member

Choose a reason for hiding this comment

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

WDYT about adding optional parameter info and default values in jsdoc? http://usejsdoc.org/tags-param.html#optional-parameters-and-default-values

* Disables all proxies' traps, turning the observed object into a forward-proxy object, like a normal object that you can modify silently.
*/
JSONPatcherProxy.prototype.disableTraps = function() {
proxifiedObjectsMap.forEach(disableTrapsForProxy.bind(this));
Copy link
Member

Choose a reason for hiding this comment

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

Why don't we make disableTrapsForProxy instance method as we are always calling it on instance - it results in creating new function of every call, here and on delete? Do you want to keep it private?

});

observedObj.firstName = 'Malvin';
expect(called).toReallyEqual(1);
it('should omit patches when unobserved then observed', function () {
Copy link
Member

Choose a reason for hiding this comment

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

We are testing pause and resume not "unobservered" then "observed"

});
it('should unobserve then observe again (deep value)', function () {
Copy link
Member

Choose a reason for hiding this comment

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

and we have unobserve again, It's quite confusing to me to mix pause and unobserve test together, under describe('unobserving', function(){

@tomalec
Copy link
Member

tomalec commented May 19, 2017

Overall I think it's just cosmetics, but unobserve suite in test is quite confusing to me, wouldn't it be cleaner to have separate section for pause?

@alshakero
Copy link
Collaborator Author

@tomalec we have done all kinds of breaking changes, why should we keep unobserve in its current utterly useless case. I removed it and stuck with pause, resume, and disableTraps. And cleaned up the tests.

});

describe('pausing and resuming', function() {
it("shouldn't omit patches when paused", function() {
Copy link
Member

Choose a reason for hiding this comment

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

I think it should be: "Should omit patches when paused"

expect(called).toReallyEqual(2);
});

it('should omit patches when paused then resumed', function() {
Copy link
Member

Choose a reason for hiding this comment

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

I'd say: "Should re-start emitting patches when paused then resumed"

@alshakero
Copy link
Collaborator Author

alshakero commented May 23, 2017

Is this good to go?

@alshakero alshakero merged commit f1d8e91 into master May 25, 2017
@warpech warpech deleted the Use-Proxy.revokable branch June 5, 2019 18:50
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.

3 participants