In my web-app, a form will be posted to create a new user. It seems like Spec would be a good fit to make sure the form submitted has all the things required by the function. How do you employ spec for such a purpose as this, or is it a wrong use-case?
Ya, spec is good for validation.
What you do is that you need to gather the form data into a Clojure/Script structure. So no JSON, or HTML. You need to convert it to say a Clojure Map, Vector, Strings, Lists, Ints, Numbers, etc.
You create a spec for that structure.
Then you call valid? and explain and it will validate, and tell you what failed validation if it did not.
Spec is very versatile. I have used it for:
Parsing the arguments to defmacro. See for instance def-set-method in https://github.com/jonasseglare/bluebell-utils/blob/master/src/bluebell/utils/setdispatch.clj
As an general text parser. If you have a string, you can just convert it to a vector of characters using the vec and then parse that vector using spec. Probably there are already good text-parsers in Clojure, but if you only know Spec, why learn something else if performance is not an issue?
Validating inputs to functions (which is maybe one of the most common uses).
Bringing back structure to a linear log history. For instance, if you always print “begin” to the log whenever you enter a function and “end” whenever you exit a function, then you can bring back the depth of the callstack to your logs using Spec.
I would use it for (but haven’t yet):
- Extracting data from scraped HTML-data from webpages. HTML-data is just a nested datastructure and Spec would probably be perfect for that. I once wrote a bot in Clojure using Selenium Webdriver to monitor the number of members in Facebook groups. If I would write that bot today, I would use Spec to extract all kinds of interesting data.
We use Spec heavily outside of tests. We define specs for each endpoint of our REST API and use it to validate and conform inbound string arguments to the data types we want them to be (long, double, Boolean, keyword, etc).
We also use Spec to describe our persistence schema, and then we pull the list of keys – columns – from the specs so we can use the specs both to validate data we’re working on as well as pull out just the keys we can persist, so additional keys in the map don’t cause problems when we insert data to the database.
And, yes, we also use Spec in tests. Most commonly, we use Spec to define test data and use
exercise to generate test data, especially for sequences of commands we want to run our admin systems through in order to validate behavior paths.
I have used spec for:
- validation obviously
- the more general parsing case (conformance) which I would call pattern recognition. Together with multimethods this lets you dispatch on the content or shape of the data. https://feierabendprojekte.wordpress.com/2016/09/11/generating-ui-for-arbitrarily-nested-data-structures/
You could call conform on the data received by the endpoint. This validates and parses the data at the same time. If you have a spec that contains choices like “or” or “cat” the conformed data will be labeled. Thus you could even distinguish between different kinds of requests based on the data.
I experimented with that idea a couple of weeks ago. I don’t know how fast or powerful the resulting parser can get but it didn’t prove limiting in my context. What’s far more interesting though are test.check generators of token streams you get for free from your specs.