It’s a perfect way to introduce the concept of a ‘default method’ or ‘default property’ into an object.
That was a little bit of a hack, so in this article I’ll show you a better way to do it using a proxy. At the end of this article, you’ll see there’s an Apps Script library that does most of the work for you, but I’ll go through how it works here.
What is a default method?
Let’s say you have an object with a couple of methods like this. It’s a very trivial example to get started with.
It’ll give results like this
It may be that we would like to give it a default behavior – i.e. what it would do if we didn’t specify any particular method. I’d like it to default to originalObject.countArgs(), but it’ll just fail because originalObject isn’t a function.
Sprinkling a little proxyDust on the originalObject sets it up.
Accessing an invalid method or property
Another possible requirement might be to either fail (or execute something) if a non-existent method (or property) is referenced. I’d like an error thrown like the one below
Using a proxy
Using my bmDuster library you can set a default method, and check for invalid references.
How does that work ?
Hooking up the original object to the default method
The first thing you might notice is that the proxy is being created, not for the original object as you’d expect – but for the defaultMethod.
- We intercept an attempt to access a property of the defaultMethod
- Instead of looking in the target object (i.e the defaultMethod which has no properties), we look in the originalObject
- We return the property from originalObject instead.
You’ll also notice that proxyDust takes a propAction function. This describes what to do when at attempt to access a property is made.
Usually the default will be fine. It simply returns the content of originalObject[prop], as described earlier, but it’s there in case you want to do some fancier thing.
There’s also a missingPropAction argument, which defines what to do if an attempt is made to access a non existent property.
The default is to throw a custom error (we’ll cover those too later in this article), but you may want to return undefined – which would mimic the unproxied behavior.
Hooking up the default method
In addition to property get, we can also intercept the running of a function at the ‘apply’ entry point in the proxy handler definition.
Since the defaultMethod is a function, that means whenever its invoked as a function (dust() rather than say, dust.countArgs()).
That gives us the place to simply execute the default function.
Using a foreign default method
Note that in this example so far, the default function is one that exists in the originalObject. It can of course be something completely different.
proxyDust also takes an applyAction which is what to do when the defaultFunction is executed. The default is of course just to execute it, but again, you may want to do something magnificent here instead – as we will indeed do later in the article when we start fiddling with Apps Script services.
Summary of proxyDust
It’s centered around the idea of creating a default method for an object containing methods, and detecting bad property references more elegantly. Each handler action is configurable via optional arguments but generally the defaults should be adequate.
Throwing a custom error
You just throw an instance of the error, passing the required arguments – for example
Logging a custom error
Just because it’s an ‘error’ it doesn’t mean you have to actually throw it. It can be useful for logging of errors too. This will log a formatted error, without actually throwing it
More complex example
Let’s look at a handier example now. Say you wanted a shorthand way of comparing 1 or more values passed as arguments, or arrays of arguments to see if they are equal.
In this case the ‘defaultMethod’ is where each comparison of arguments versus value is true. However there are other cases that need handled
- compare.every () – the default – all arguments must match
- compare.some () – at least 1 of the arguments must match
- compare.none () – no matches
- compare.partial () – at least 1 but not all of the arguments must match
- compare.list () – instead of a single match an array of true or false
First we’ll need a proxy to a base object with these methods implemented, and a default method of .every(). In this case I’ve chosen to flatten all arrays that come through as arguments as well.
You’ll notice that the default operation (how to compare 2 values) uses an equals function from bmDuster. This is a handy way of comparing equality that applies the === test, but it also handles Maps, Sets and so on as well. In particular it does a value equivalent test on objects.
I’ve exposed bmDuster.equals as it might be useful for you, even when not using proxies. For more on deep-eql see this package
For these series of tests, I’m using this simple checker to see if the expected values match the actual values
There’s a node version of all this, with many more (non-apps script) tests at https://github.com/brucemcpherson/bm-duster
Here are the tests
Fiddling with Apps Script objects
Let’s say you want to customize an Apps Script object.
Now let’s use proxyDust to create a simple customized version of UrlFetchApp with these characteristics
- Default method is fetch()
- A base url is added to each url reference and Oauth tokens are inserted into the header automatically
- Results are JSON parsed automatically
- Add caching to reduce the number of API fetches made
The test API
I’m using the People API for a test here. We’ll use the REST API for this test though. If you enable the Apps Script People advanced service (although we’re not going to actually use it) it will enable the API in your cloud project for you. As usual we’ll need to fiddle around a few scopes in your appsscript.json.
Here’s what your manifest file should contain.
Setting the default method
Let’s start by proxying UrlFetchApp and setting fetch as the default method
Modifying the fetch options
Enhance that by adding the fixed endpoint and the oauth token to all default requests
Parsing and organizing the reponse
Here we’ll further enhance by examining the response, parse it and create an object with it ready parsed. Note that it also creates a .throw() method which you can add if you want an error thrown if one is detected.
Finally, we’ll add automatic caching so it will go to cache first rather than the API
We’ve created a new version of UrlFetchApp with all the same characteristics as before – but now we have a default method which automatically does all the stuff we’d normally have to do when calling an API.
I’ve covered quite a bit in this article so thanks for sticking with it the whole way through. Proxies are a very powerful way of avoiding writing wrappers for existing objects, such as the the Apps Script services – or anything else you can think of.
As usual you can find all my libraries in my github repository at https://github.com/brucemcpherson
or in the IDE at
There’s a node version of all this, with many more tests at https://github.com/brucemcpherson/bm-duster