Add `Proxy.advise` (and maybe `Proxy.on`?) #108

Closed
briancavalier opened this Issue Jun 7, 2013 · 8 comments

Projects

None yet

3 participants

@briancavalier
Member

If proxies provided an api for advising, then they could implement their own custom way of advising things that we can't currently advise with meld. wire/aop could simply rely on Proxy.advise, and wire's base proxy could use meld to implement Proxy.advise.

For example, we could advise "non-standard" objects like jQuery widgets if the jQuery widget proxy implemented a custom advise method.

We could do something similar with Proxy.on for easier integration with other framework's custom event systems (for which none of our existing facets will work).

In short, this may help us integrate other libraries and frameworks more easily without having to write custom facets. It puts more burden on Proxy implementations, but allows facets (which are the cujoJS developers interface to wire) to be much more generic.

@unscriptable
Member

sounds +1-ish :)

@briancavalier
Member

I've started this over in the proxy-advise branch.

@briancavalier briancavalier added a commit that referenced this issue Jul 2, 2013
@briancavalier briancavalier See #108. Implement proxy.advise for base object proxies, port connec…
…tion facets to use proxy.advise where appropriate
6953041
@briancavalier
Member

Proxy.advise just landed in dev. I'm still debating Proxy.on, and may hold off until we run into concrete use cases for it (although being able to abstract event connections for jQuery widgets and Backbone models/collections seems compelling)

@skiadas
Contributor
skiadas commented Jul 10, 2013

Can you elaborate a bit how this might work in practice, both the Proxy.advice part and the Proxy.on part? Will it no longer be necessary to include the wire/aop plugin for example? This might be a bit too abstract for me as it stands. Would plugins typically be expected to implement their own proxies? Maybe I just need to read plugins.md more carefully.

@briancavalier
Member

@skiadas Proxies are a very powerful part of wire. They basically wrap a particular type of component, such as a DOM node, in a standard interface so that wire plugins can perform operations on them via that standard interface. This allows plugins to be written against the proxy interface, making them much more broadly applicable.

For example, the properties facet can work on anything because it simply relies on the proxy.set(name, value) API. The plain JS Object proxy implements that by doing simple property assignment, but the DOM proxy implements it by doing either setAttribute or setting a property on the node (it uses some heuristics to determine which is appropriate). The jQuery widget proxy also implements a custom proxy.set to do the right thing for jQuery widgets. So, developers can just use properties and expect it to do the right thing on nearly anything.

Plugins are not required to implement a proxy. A plugin will typically implement a proxy when the plugin is intended to deal with a particular type, like DOM nodes or Backbone Models/Collections, jQuery widgets, etc. As long as some plugin provides a proxy for the type in question. E.g. wire/dom provides a DOM node proxy, so no other plugin needs to provide that.

The idea with Proxy.advise and Proxy.on is similar. For example, meld.js cannot actually advise things like jQuery widgets because they don't work like regular JavaScript objects, or have bizarre or hidden APIs. If we modify the wire/aop plugin such that, instead of using meld.js directly, it simply relies on proxy.advise, then it will be able to advise any component that has a proxy that implements proxy.advise. Again, it makes plugins much more widely applicable.

FWIW, Proxy.advise is already implemented in dev: The base object proxy implements it using meld.js, and wire/aop simply relies on proxy.advise.

Proxy.on would be similar, but I'm envisioning that proxy.on would be responsible for interacting with whatever event system the proxied component supports/prefers. For example, for plain objects, the base proxy could implement synthetic "events" by using meld.js. The DOM node proxy could implement proxy.on using DOM events. A Backbone Model/Collection proxy could implement proxy.on using backbone events. That'd allow wire/on and wire/connect to be merged into one plugin, call it wire/on, and it could simply rely on proxy.on.

Proxy.on does have some slight complications. We typically think of an on()-style method having 2 parameters: 1) an "event name" or "event matcher", 2) a function to call when events matching the name or matcher are emitted. The "event name" could be vastly different across proxy types. For example, the current DOM events-based wire/on allows DOM event names and css selectors for high-in-the-dom event handling. That is, an "event name" may look like: 'click:.button-container .submit-button'. However, a Backbone event is typically just a simple name, like 'change'.

That's not hard to represent in the API. Proxy.on can probably just have the signature: on(eventSpec:String, handler:Function) and that'll cover all bases. However, the acceptable values of eventSpec will vary from one proxy type to another.

Most of the time that won't cause problems, but it may occasionally trip people up.

@skiadas
Contributor
skiadas commented Jul 11, 2013

Backbone's Views do have an events section's that pretty close to dom, they look like this:

{
  'mousedown .title':  'edit',
  'click .button':     'save'
  'click .open':       function(e) { ... }
}

And I think those are referenced relative to the view's "dom element", in theory a proxy could do that connection I suppose. jQuery also supports I think selectors based on an element, so "high-in-the-dom-but-starting-from-an-element" so to speak. It might be nice to identify that element, but maybe that can be delegated to the proxy. The default proxy could be returning "document" while a Backbone.View proxy or a jQuery widget proxy could be returning the element they correspond to. Maybe the jQuery proxy already does that.

Maybe the proxies could also support a similar "trigger" abstraction, for triggering events? Or is the trigger function's format across frameworks consistent enough to not need an abstraction?

It definitely feels like this is the right place to do event handling.

@briancavalier
Member

Right, yeah, backbone views deal with DOM events, which will look similar to things you can pass to jQuery.on (since it likely just delegates to that), and to wire/on (and friends). Backbone models and collections have their own custom event system, which uses event names like "change", "add", "remove", etc. Once we have Proxy.on support, we can support all of the above!

Hmmm, trigger() is interesting. I hadn't thought about that. To be honest, I've not had a need for it, but I can see it being useful for testing in some situations. Although, you can always directly access proxy.target to get a handle to the proxied object and use it's methods directly in testing situations if necessary.

Seems like we should move forward with Proxy.on, while continuing to think about and discuss a corresponding trigger() method.

@briancavalier
Member

Proxy.advise is in dev. Closing this and opening a new issue to discuss Proxy.on.

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