Tuesday, July 14, 2020

rm != rg

I use ripgrep, rg. It's like recursive grep, only 100x better.

Today, I had this session:

$ rm pattern
rm: pattern: No such file or directory
$ rg pattern

See that? I meant to type "rg pattern" the first time, but typed "rm pattern". Luckily no harm was done in this case. But what if I had typed "rm tod*", wanting to search for the string "to" followed by zero or more "d"? Well, I might just blow away my todo file, since it matches the glob tod*. And then I'd be sad.

I'm not sure if the ergomomics of rg as a command name have come up before, but this scary close encounter has me thinking. Maybe I should do this sly manuever, as suggested by a colleague: alias ag=rg.

I don't know. I think I'm tempted more so to make rm with its dangerous side effects harder to invoke. Like so:

alias remove=rm
rm() {
  echo "If you want to destroy a file, use remove."
  echo "If you want to find a file, use rg."

What do you think?

Tuesday, October 30, 2018

Friday, September 8, 2017

Choosing the first available program from list of options

GNU tar accepts an external program to perform compression, via the option --use-compress-program. I'd normally want pigz if it's available, but if not, fallback to gzip. Is there a compact way to get represent this? Yes!
which --skip-alias --skip-functions pigz gzip 2>/dev/null | head -1
GNU which accepts multiple arguments, printing out the resolution for each as they're found or an error if not. GNU which also allows finding only full-fledged binaries, not aliases or functions. This is exactly what we want: list the paths to these programs, in the order I gave, then pluck the first one.

Monday, March 27, 2017

What is a @dataProvider?

I'm asked about data providers almost every time I introduce a developer to PHPUnit. Once you understand them, they're quite clear, but on first pass they seem to give developers pause.

So what are they? Practically, a data provider is any static method that produces an array of arrays. The outer array defines the iterations of the test loop, while the inner arrays are the arguments to pass to each iteration. Let's look at an example. First, annotate a test method's docblock:

 * @dataProvider provides_foo_and_bar
public function test_frobnicator($foo, $bar) { /* ... */ }

Then define the data provider:

public static function provides_foo_and_bar() {
    return [
        [ 'FOO', 'BAR' ],
        [ 'BAZ', 'QUUX' ],

PHPUnit will call provides_foo_and_bar twice. The first time it will pass test_frobnicator with "FOO" and "BAR". The second time it will call test_frobnicator with "BAZ" and "QUUX". Note that the data provider is both public and static: PHPUnit requires that.

Pro-top: by default, phpunit runs all data sets. But, you can select specific data sets to run easily: phpunit FrobnicateModel.php test_frobnicate#1 runs only one loop, with the 1-index elements "BAZ" and "QUUX".