Sunday, September 12, 2010

Learning to Open-Source via Proxy

The Internet is a relentless, fast-paced, consumer of content, and I'm a bit of a recluse. My engagements had been brief and trifling; irc chatting, a blog comment here, a forum post there. Then, as a test use of - a popular code-sharing service - I published Proxy. It was my debut in the open-source JavaScript world1, and I was determined to see it through to the end. However, in the reverse time-warp that is the Internet, I've learned that the end never comes...

So here you are, now, reading about code I'd rather move past. "Now" could be the day I posted this article, but it's more likely some months or years later. Admittedly, I promised to discuss Proxy, and my efforts to document it, in an earlier post. Now felt like a good time.

Introducing Proxy

Proxy allows you - the JavaScript developer - to share and use privileged objects easily. (A privileged object has functions that execute within a scope different from the current one.) Proxy began within a larger project that has a public API of privileged objects. Each privileged object uses getter and setter styled methods to manipulate it's private-object counterpart. As I optimized and refactored that code, a pattern emerged for defining these getter/setter methods, which is now employed by the Proxy constructor.

Before I continue, it's important to note the differences between Proxy and native getters and setters. First, the native option uses a recent JavaScript construct - supported by all modern browsers except Internet Explorer (natch). Syntax aside, they also have different applications. The native method is designed to mask an object's individual properties, while Proxy is designed to mask the entire object. With a compliant browser, you can do the latter with the former, but not vice-versa. Ultimately, given their distinct application, I won't compare these two in this article.

To be clear, Proxy is nothing new or unique. But that's because creating versions of objects - or proxy-like objects - is also not new or unique. If you were tasked to create proxy-like objects in code, it might go something like this. Define a function that makes an object literal that has methods to get and set properties of some source object. Here's an example, below.

Simple enough, and there is already a perceptible degree of repetition in the defined methods. If you had to do this for more than one property, you might refactor it like below.

Proxy takes this one step further, by identifying three phases of our getter and setter functions: get, vet, and set or GVS. The get and set phases isolate when a getter/setter retrieves or assigns a value. The vet phase isolates when a setter function is validating a value, before assigning it to the source object's property. With this abstraction, we can describe each phase of our getter/setter function with an object. Proxy uses an indexed array, called a GVS array, where each index corresponds to each phase - the first is the "get-index", and so on. Proxy accepts and compiles GVS arrays, so they execute like traditional getter/setter methods.

Each index can be a string, function, or any other truthy or falsy value. When the get and set indexes are strings, they reference the source object property to retrieve and assign, respectively. When the vet-index is a string, it specifies the object type required in order to set a value. [The next version of Proxy will accept an array of object types.] For the sake of comparison, below is a traditional proxy-like object, with a same-name setter/getter function that retrieves property "x" and accepts a number for property "y", and the same function described as a GVS array in a Proxy constructor.

There are many possibilities for defining simple to complex getter/setter functions via a Proxy GVS array. (The Proxy wiki will be updated to explain all of them.) Of course, Proxy doesn't limit you to GVS arrays, and can accept regular functions as well. However, the efficiency of the GVS pattern is the primary reason I use it, instead of defining proxy-like objects traditionally.

Learning to do it right (next time)

Open-sourcing takes work and a greater commitment than open-source advocates proselytize. Documenting Proxy, improving it, defending it, naming and explaining concepts like "GVS pattern" have destroyed my productivity. Before announcing Proxy, I spent a weeks documenting it (i.e., less than two-hundred lines of commented code). Now, a month later, I've finally come around to blogging about it directly, if not completely.

This post doesn't mark the end of anything regarding Proxy. I've resigned myself to the fact that this code is my baby and always will be. I've begun to integrate Proxy updates into the larger project it spawned from. I also have plans to improve it's documentation and packaging - which will finally include unit tests. I've filed tickets against myself (a highly recommended practice), and plan to add a roadmap to the wiki.

Though Proxy is more than I expected, it's just what I asked for: a trial run at open-sourcing my work. I've de-romanticized the notion of shared code equating to community-supported code. That kind of interest and support comes way down the line, after useful criticism and outright derision. By dragging Proxy (and myself) to higher levels of recognition and professionalism, I'm discovering what it might take to push larger projects, in the future.

1 Actually, my first JavaScript routine was posted to in 2002. A message-board is not a code-sharing service per se, yet my code remains archived and accessible to this day.


No comments:

Post a Comment