What is eval()?
Eval is a common method in programming languages that allows the developer to do some Metaprogramming. I’m sure that answer actually raised more questions than it answered, so let’s take a step back and talk about how computers interpret our code.
Whether at compile or runtime, the programming language itself is responsible for translating human readable code into something the computer can do. What differs amongst languages is the grammar the human readable code takes.
Some languages are “highly dynamic” while others are … well, less dynamic. The how’s and what’s of defining “dynamic” are both a controversy in its own right and far beyond the pay grade of this blog post, so let me just speak about one of the banner features of dynamic languages: Metaprogramming.
Remember Inception? Like Inception, Metaprogramming is a bit of a mind bender, but the essence of Metaprogramming is that instead of writing code to solve one problem, developers instead write code that solves many problems; or, as I like to think of it – developers write code that writes code on the fly.
The idea behind Eval() is to have the compiler or interpreter of the language take a string of text and read and interpret it as if it were actually code. If you’re not a coder, you may still be waiting for the punch line; what makes this all very important is that, as coders, we can create that string programmatically, mixing in variables for class names, values, etc. This allows for highly dynamic software that, in effect is capable of writing itself.
variable1 eq '1' or AwsomeVar eq '1' or AwesomeSauce eq '1' or BowTiesAreCool eq '1' or theDoctor eq '1'
JEXL, which you can see in all its glory above, is basically a programming language unto itself. I would receive these JEXL statements from an API and I needed to evaluate the expressions for true or false. I knew I could pretty easily build a map of JEXL variable names to Apex variable names, and likewise replace the operands like eq into something like this:
variable1__c == true || AwsomeVar__c eq == true || AwesomeSauce__c == true || BowTiesAreCool__c == true || theDoctor__c == true
Wrap that in an IF() statement and we’re off to the races. Here is where Eval() comes in handy. With Eval() I can pass in that translated string, and evaluate it within an if statement. Using Eval() like this means that whenever the integrated API changes a validation JEXL string, my integration can automatically reflect that validation change.
So how do we create an Eval() method? Salesforce provides us with a REST based Tooling API that exposes the Execute Anonymous method. Utilizing the tooling API’s rest access to (securely) call Execute Anonymous allows us to pass a string of code in, and have it evaluated as if we were using the developer console’s Execute Anonymous window. Note, this means there are two requirements for Apex Eval() to work: API access (Sorry PE), and setting up Remote Site in your org that allows you to call out to your instance of Salesforce. I.e. na4.salesforce.com or cs3.salesforce.com. Once you’ve met those two requirements, we’ll utilize the excellent apex-toolingapi library for calling the tooling api. Because Apex is a typed language, our Eval methods will need to return a specific type. In my original use case, I wanted to know the Eval’d result of a Boolean expression. To do so, I created the Dynamic class, with the following method:[gist https://gist.github.com/noeticpenguin/cd457c5b969b48b1f28a]
I’m using an exception so that I can capture and return typed data from the exec anonymous call. This allows us to catch only a particular type of exception, in this case IntentionalException for success use cases, while still retaining the ability for our anonymous executed code to throw a different kind of exception if needed. I’ll leave it as an exercise for the reader to build out other types of Eval methods.
So there you have it Eval(), a.k.a. Execute Anonymous, within a typed generally non-dynamic language. Please use this for good, and remember you will incur rest api call cost when using this.