Dequoter - Plugins
I’m trialing the addition of plugins in Dequoter, so users (i.e. me) can define new filters without having to rebuild the app. These plugins are written in UCL, for two reasons. One, because the interpreter is already embedded; and two, because I think the language works well for such a use case.
There is a new package for the Dequoter builtins, called d. The builtin for defining a text transformation is d:filter. This takes a name, which will appear in the palette, and a block that receives the text to transform. An example of such a filter is this one that calls OpenSSL to decode an x509 certificate encoded as a PEM block, something I always forget how to do despite needing to do so surprisingly often:
d:filter "Plugin: Decode Certificate" { |in|
try {
os:exec "/bin/bash" "-c" "openssl x509 -noout -text -in /dev/stdin" -in $in
} catch { |e|
error "Failed to run openssl: $e"
}
}
Any block or function that transforms a string could be used here, including builtins. So a plugin version of the “Strings: To Upper Case” filter can be implemented simply as this:
d:filter "Plugin: To Upper" strs:to-upper
This is also an opportunity to refine some of the UCL primitives. For example, it’s a little unwieldily to use os:exec like I did, what with the explicit reference to Bash and passing stdin like this. Nor does it work with UCL pipes, where the piped value is supplied as the first argument. I had a go at improving this with the os:! builtin, called “os bang”. When called with a single argument it will invoke the command using the shell. Call it with two arguments and the first one will be treated as stdin, making the piping of command I/O trivial:
d:filter "Plugin: Date Hash" {
os:! "date" | os:! "sha256" | os:! "base64"
}
And yeah, this is a contrived example, but UCL’s standard library is not that substantive just yet, and being able to “shell out” like this provides some useful relief.
One other function was d:map-lines, which is a simple map function that operates over lines of the input. This is just to standardise this form of operation. It’s essentially “map”, but it also filters out lines when the block returns nil:
d:filter "Plugin: Line Length Above 10" { |in|
d:map-lines $in { |l|
ll = len $l
if (le $ll 10) { () } else { $ll }
}
}
I say “when the block returns nil”, but returning anything at all is not possible with this form of block. Wrapping it in a lambda should help with that:
d:filter "Plugin: Line Length Above 10" { |in|
d:map-lines $in (proc { |l|
ll = len $l
if (le $ll 10) {
return ()
}
return $ll
})
}
So, that’s the current state of plugins in Dequoter.