1024px-Flatirons_Winter_Sunrise_edit_2

Object literals in JavaScript are both a blessing and a curse. In statically typed languages like C# or Java, objects have defined APIs which cannot be modified after compilation. Because JavaScript is a dynamically typed language, objects have no such restriction. I may add or remove properties and methods as I see fit during runtime. This gives me flexibility in that I can add or remove information as needed, but it also makes it very difficult to determine what the “shape” of an object should be at any point in time. This is especially true across API boundaries.

Recently I worked on a project where messages were delivered to application modules over a service bus. Each message had a sort of informal envelope/data schema that the module was expected to understand, unravel, and then pass the actual “data” part of the message to other application modules as necessary. The shape of this data was often arbitrary, or at best shaped according to undocumented conventions, so I frequently saw these kinds of test conditions:

Or the more clever cousin:

Dealing with literals

I had little control over the data being created, but I wanted a better means to test for, or access, properties on these object literal messages. Necessity, being the mother of invention, drove me to create the JavaScript library l33teral, a suite of methods that wrap an object literal to make working with its structure a less cumbersome experience. l33teral may be used in both node.js and desktop browser environments. For the examples above, I was able to reduce the number of test conditions to a single statement. (Assume leet represents the imported l33teral module.)

I could also avoid test conditions altogether by specifying default values for literal paths that did not exist.

Sometimes I found it necessary to modify a large object graph at a specific path but since I could not be sure of the literal’s structure at runtime, I needed to set up the graph if the given path did not exist.

With l33teral this becomes trivial because the plant method will recursively create the segments in a path you specify, if any do not exist.

l33teral has other utility methods as well, such as hasAllProperties() and hasAnyProperties(), which invoke an object’s hasOwnProperty() method for a varying number of arguments, returning true if all all present (in the case of the former) or if any are present (in the case of the latter).

You can pull data from various paths on an object literal into an array with collect(), or use extract() to load them into another object literal where the keys are the string representations of the paths and the values are those in the leet object at those paths.

Constructing and deconstructing data

While l33teral was born from the need to work with object literals over which I had little control (my code consumed them, it did not construct them), I recently found a powerful use case for l33teral’s ability to construct objects. In another, unrelated project I needed to work with data that, for specific reasons, had been divided into two different document collections in mongo. The records in these collections had schemas similar to these:

The customer objects are what you would typically expect: a single document per entity with a unique identifier. The customer data collection was structured so that the relationship to the customer collection was many-to-one with the memberID key. Each record in the customer data collection contained both a name and a value where the values were simple strings and the names were strings in a namespaced “path” format, e.g., ‘billingAddress.street1’.

Working with this customer data on the server is no big deal, but the overhead for clients consuming data via HTTP JSON endpoints would be tedious if they had to parse a collection of “customer data” each time they needed a piece of information like a phone number, zip code, or web page. Clients expect object graphs, not arrays of “records”. This seemed like an ideal candidate for l33teral.

To construct a single “customer” object during an HTTP GET, I fetched the customer and his data records asynchronously then built up a single object graph with l33teral.plant(). This was fairly painless.

In the examples below, I use mongoose models to fetch and persist data, and the async library to organize asynchronous control flow of operations. The express web server is also assumed, as each example is an HTTP endpoint.

I then returned this object graph to the client as a single JSON object, with all expected properties. So far so good. The second challenge was how to break up an object graph into its constituent data records on a POST or PUT.

There were several things that needed to happen. First, I needed to know which paths to extract from the customer literal. Because I was dynamically building these literals for client consumers, I kept these in our data store, but for this example, I will just assume they are in a simple array.

Next, I mapped each path to its corresponding point of data on the customer literal with l33teral’s extract() method. When I had this data, I used the purge() method to recursively remove these paths from the customer object, so that only the paths that were native to the customer object remained. This included metadata like timestamps, IDs, user role, etc. Once I’d separated the customer object from its many data points, I persisted all records back to the data store. I used the async library’s waterfall() method to chain these operations together, ensuring that the customer was saved before his data. I generated a 200 response when finished to indicate that the operation had been successful.

This solution, while involved, worked well for the needs that I had. l33teral made it much easier to build out and deconstruct my objects without having lots of recursive code scattered across my application (it’s all in l33teral instead!).

Limitations

l33teral does have some limitations, albeit sensical ones. As the library’s name implies, it is designed to work with object literals, not complex object graphs where certain parts of the graph override members of their prototypes.

Consider the following object graph, created with the node.js REPL:

The object graph corresponds to this model:

object graph

When I query o3.o2.foo in this example, I get the value ‘baz’, set explicitly as a property on object o2. l33teral would detect this property and report back that, yes, the path o3.o2.foo does exist.

If I were to use l33teral’s snip() method, however, to remove the property foo from the object o2, I would get unexpected results:

This happens because l33teral does not anticipate graph objects that override properties in their prototypes. We can simulate this in the node.js REPL as well.

Probing o3.o2.foo would also return false because object o2 no longer has its own property foo. A demonstration of this limitation can be viewed live in this jsbin snip.

Conclusion

For better or worse, JavaScript developers have to work with object literals. Understanding how they work and what you can and cannot do with them helps developers mature in knowledge and practice, but once you’ve gained this knowledge l33teral can give you shortcuts that will just make your life easier. Sometimes it is better to correct problems upstream before they get to us (explicit message schemas on your message bus, default values on object literals so you always know which properties will be present, etc.), but sometimes we have no control over these things and the best we can do is “[l]ive as brave men; and if fortune is adverse, front its blows with brave hearts.” (Cicero) So if you’re plagued by literals that you can’t control, grab a copy of l33teral and tame the beast. You’ll be happy you did.

Learn more about the l33teral API with this Blazon presentation!