Hardscrabble 🍫

By Max Jacobson

See also: the archives and an RSS feed

Configuring ALE to only run rubocop in projects that use rubocop

March 6, 2023

I usually use rubocop to help maintain consistency and catch bugs in my Ruby codebases.

When I’m using Vim, I can see the feedback inline via Asynchronous Lint Engine, aka ALE, the terrific vim plugin.

Here’s what it looks like:

Screenshot of rubocop offenses showing up in vim via ALE

Nice!

I could run rubocop from the command line and see my offenses there, but it’s a really nice workflow to see the offense right away, in context, when I introduce it in the editor.

Before I used this vim plugin, I’d just run the rubocop CLI on the command line, which looks like this:

Screenshot of rubocop offense showing up on command line

It’s clear enough what’s happening, but you’d need to open the file to see the context, and there’s some noise, so I think the editor integration is a real improvement.

But! There’s one problem… not every ruby codebase uses rubocop.

For example, the RSS reader I use, Feedbin, is an open source rails app, and when I clone it and open it in vim, here’s what I see:

Screenshot of rubocop offense showing up in vim via ALE when you don't want them to

Yikes!!!

This codebase doesn’t even use rubocop, so of course there are a lot of “offenses”.

This is so annoying that it almost makes me want to disable ALE, but that would make my experience worse when I’m working on codebases that do use rubocop.

What to do?

Thankfully, after some poking around, I found out that it is possible to improve the situation with some ALE configuration. Here’s a bit about how I worked that out.

Here’s what I had in my .vimrc:

let g:ale_linters_explicit = 1

let g:ale_linters= {'ruby': ['rubocop']}

let g:ale_fixers = {'ruby': ['rubocop']}

let g:ale_ruby_rubocop_executable = "bundle"
let g:ale_fix_on_save = 1

I thought this would be sufficient. I ran :help ale-ruby and read through the docs there, and found this relevant bit:

g:ale_ruby_rubocop_executable                   *g:ale_ruby_rubocop_executable*
                                                *b:ale_ruby_rubocop_executable*
  Type: |String|
  Default: `'rubocop'`

  Override the invoked rubocop binary. Set this to `'bundle'` to invoke
  `'bundle` `exec` rubocop'.

So, because I configured that ale_ruby_rubocop_executable global variable to "bundle", that means that ALE is going to invoke bundle exec rubocop instead of simply rubocop.

That has a few nice benefits:

  1. You can feel confident that you’ll use the version of rubocop specified in your Gemfile.lock
  2. If rubocop is not included in your Gemfile.lock, the bundle exec rubocop command will fail, and therefore no offenses will be found

So… wait a second, why was I seeing any rubocop offenses in the feedbin codebase if feedbin doesn’t use rubocop?

Hm, let’s try searching the codebase with ripgrep for references to rubocop:

$ rg rubocop
Gemfile.lock
517:    rubocop (1.42.0)
524:      rubocop-ast (>= 1.24.1, < 2.0)
527:    rubocop-ast (1.24.1)
529:    rubocop-performance (1.15.2)
530:      rubocop (>= 1.7.0, < 2.0)
531:      rubocop-ast (>= 0.4.0)
572:      rubocop (= 1.42.0)
573:      rubocop-performance (= 1.15.2)

Hmm, what the heck, rubocop is in the bundle, even though feedbin doesn’t actually use it. How is it getting in there?

If bundler had a subcommand like npm explain, we could use that to find out exactly why it’s in our bundle. Alas, it doesn’t, so we must resort to reading through Gemfile.lock and figure it out. If we look closely, we’ll see this clue:

standard (1.22.1)
  language_server-protocol (~> 3.17.0.2)
  rubocop (= 1.42.0)
  rubocop-performance (= 1.15.2)

Aha! The feedbin codebase uses standard, which is kind of like an opinionated wrapper around rubocop.

So, because feedbin has this line its Gemfile:

gem "standard"

We get the standard gem and we also get its dependencies in the bundle, which includes rubocop. That means that when ALE tries running bundle exec rubocop, rubocop will run.

If we take a look at standard’s docs, we can find a page about integrating standard with vim which recommends this:

The recommended method for running standardrb within vim is with vim-ale.

To set Standard as your only linter and fixer for Ruby files and thereby preventing conflicts with RuboCop, add these lines to your .vimrc file:

let g:ale_linters = {'ruby': ['standardrb']}
let g:ale_fixers = {'ruby': ['standardrb']}

For automatically fixing on save, add this to your .vimrc:

let g:ale_fix_on_save = 1

Nice! ALE also supports standard, and can be configured to use standard instead of rubocop.

But that’s not exactly what I want…

To recap, here are my goals:

  1. When working in a codebase that uses rubocop, I want ALE to use rubocop to lint and fix
  2. When working in a codebase that uses standard, I want ALE to use standard to lint and fix, and not use rubocop directly
  3. When working in a codebase that uses neither, I want ALE to do nothing

Thankfully, this can be achieved.

Here’s what we can do:

let g:ale_linters_explicit = 1
let g:ale_linters= {'ruby': ['rubocop', 'standardrb']}

let g:ale_fixers = {'ruby': ['rubocop', 'standardrb']}

let g:ale_ruby_rubocop_executable = "bin/rubocop"
let g:ale_ruby_standardrb_executable = "bin/standardrb"
let g:ale_fix_on_save = 1

We’re no longer setting ale_ruby_rubocop_executable to "bundle", because it proved to be insuffient in codebases like feedbin.

Now we’re configuring it to point to the rubocop binstub1. And likewise, we’re configuring it to point to the standard binstub. If a codebase has a binstub for a tool, that’s a pretty good sign that it uses that tool.

This way, ALE will use rubocop in a codebase that has a rubocop binstub and it will use standard in a codebase that has a standard binstub. There should never be a codebase that has both binstubs2, so you don’t need to worry about activating both of them. Just run bundle binstubs rubocop or bundle binstubs standard and commit the generated file and you’re good to go.

  1. Here’s a nice primer on binstubs for the unfamiliar. 

  2. Of course it’s possible that both will be there if you run bundle binstubs --all and commit them all, but… please do not do that! 

Patrick Willems - How To Analyze Movies

February 22, 2023

This analysis of Home Alone is a tour de force:

It covers quite a lot of ground, from formalist film theory to Auteur Theory demonstrating how they can enrich your experience of filmgoing.

It’s pretty long, but is broken up into nice chapters. I watched it over several days.

NewJeans - OMG

February 19, 2023

I am a sucker for a catchy pop song and a high concept music video and this has both.

(If you don’t speak Korean, turn on the closed captions. The dialogue is funny.)

NewJeans is a new K-pop girl group and I am going to assume that I’m hip for having heard of them.

REJECTED with commentary

February 10, 2023

You may or may not be familiar with REJECTED, the classic animated short film by living genius Don Hertzfeldt. If not, go watch it and let it burn itself into your brain and make you laugh very hard every time you rewatch it for the rest of your life. Then seek out his longer works like It’s such a beautiful day and World of Tomorrow which are much more serious while still being deeply silly…

For everyone else…

Hertzfeldt did a very funny “text commentary” version, which I just stumbled on. Here it is:

I learned a lot, but also laughed a lot. It’s so dumb lmao.

Enjoy!

Out of Sight

February 8, 2023

I loved this animated short film by Yu Ya-Ting, which visualizes what it’s like for a young blind girl to get around with and without her companion dog.

How wondrous.

It is kind of bold to name a movie Out of Sight when that’s already a great Soderbergh movie, though.

Via kottke.

Whole of the moon

February 2, 2023

Check out this cover of Whole of the Moon by Fiona Apple:

(I particularly like when the wandering camera lingers on her boot, stomping to the beat)

I wasn’t familiar with the original song by The Waterboys from 1985 (I never even heard of The Waterboys).

I wandered out in the world for years

While you just stayed in your room

I saw the crescent

But you saw the whole of the moon

This reminds me of that Pinegrove song with a very similar title, Size of the Moon:

That one has these lines:

I got caught

you got those caravaggio moves

We had some good ideas but we never left that fucking room

And later:

I remember that too

In your living room, right?

When we began to fight but then we both got confused

Then we were laughing & crying in awe of the size of the moon

In both songs we’re comparing ourselves to others, we’re trapped in rooms, we’re thinking about the moon.

I love when songs are in conversation with each other and I didn’t even know it. Finally hearing that other part of the conversation after only hearing one side for a while expands something about both.

Something about being in a room when the moon is up there is profane, maybe?

Via Merlin Mann on twitter.

Natasha Lyonne on Directing

January 29, 2023

I’m really liking the new show Poker Face, and I adored this quote from an interview Natasha Lyonne did to promote it:

(She’s the star of the show, but the question was about directing; she directed one episode of the show, and several other episodes of other shows)

I’ve been obsessed with movies, like — OK, this is weird, but if you actually, for reasons unknown to me, slowly took all the skin off my body, and you’re curious what I was made of on the inside, you’d just see a bunch of images, clung together from all these classic films. And that’s actually how I’m still walking, despite all those cigarettes, is that the images, they don’t experience the downside of nicotine. And so the tar keeps them glued together. That’s what celluloid means. And I love problem-solving. And I love improvising. And despite my seemingly maybe wacky nature, I’m a deeply obsessive workaholic, who’s a very precise perfectionist. So I love heavy preparation and exactitude. I like very clear parameters, and then filling the frame within it with as much information as possible. But I don’t actually like lawless chaos. If you’ve been to my house, my bed is always made. I don’t know how to describe it. I just have big hair — that’s the best way I can put it. And I think it’s potentially misleading to people. But I really, really love directing. All my heroes are really, directors. I love acting. The thing that I love with acting, though, is, it’s almost like being a musician. I like the idea that Rian’s a composer, and he’s letting me know what part of the song I can play in service to his album. And I want to do it as best I can for him, and it makes me happy to do it. But with writing and producing and the way it all kind of comes together with directing now, it’s very fun to have pages on set and be correcting them in real time with the actors, as things are adjusting. I also love looking at the schedule obsessively and figuring it out. I love being in the edit, and the feeling of when a song or score lands correctly over a sequence to tie it together is such a joyful experience. I feel very alive when it’s all happening. And I like to not feel dead inside.

Read the rest here: https://www.rollingstone.com/tv-movies/tv-movie-features/natasha-lyonne-poker-face-accent-tripping-acid-new-york-city-rian-johnson-1234659240/

Being Alive

January 26, 2023

Here’s a video of Raul Esparza performing Being Alive from Company.

One of those interpretations that really wrings the meaning from every word.

Via the replies to this tweet from TV critic Emily Nussbaum on twitter.

Water walk

January 25, 2023

Check out this experimental John Cage performance from 1960:

This is some wackadoo shit, and I had a great time watching it.

Via this cool Kyle Chayka thread about minimalism (not sure how minimalist this thing is tbh).

Morning Announcements

January 25, 2023

This isn’t officially a Derry Girls parody but it gets at the same joy. Aubrey Plaza is so great.

I feel like this is a weird time of life to become an SNL fan but why shouldn’t I be? Not to damn it with faint praise, but some of it’s funny.