A Standard Way to Lint your Views

Connor McQuillan

Why Standard?

In our Ruby guide we recommend using Standard. The gem describes itself as a Ruby style guide, linter, and formatter. It helps avoid the never-ending debates around code style by making decisions for us (I’m looking at you single vs double quotes!). It’s easy to use, consistent, avoids configuration and helps us write better styled and more consistent Ruby code.

Introducing erb-lint

As developers, we often write HTML when working with Ruby. It can be useful to lint our .erb templates to ensure consistency and catch errors. The erb-lint gem from Shopify is a tool to lint our views. The linters enabled by default are outlined in the documentation.

We can run both tools as part of CI. Here’s how to do that in GitHub Actions.

# .github/workflows/lint.yml

name: Lint
on: push
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true
      - name: Run Standard
        run: bundle exec standardrb
      - name: Run erb-lint
        run: bundle exec erblint --lint-all

Erb-lint can also run Rubocop to lint the Ruby code inside our views. The documentation demonstrates how to setup Rubocop linting with some basic configuration. But what about if we want to use Standard to lint the Ruby code in our views instead?

Running Standard with erb-lint

Standard documentation demonstrates how it can be used via Rubocop. We can add this to our erb-lint config and use Standard via Rubocop to lint the Ruby code in our views.

At the time of writing, there is a minor open issue with Rubocop that means all cops aren’t disabled by default. But we can override that setting manually, for now.

# .erb-lint.yml
---
EnableDefaultLinters: true
linters:
  Rubocop:
    enabled: true
    rubocop_config:
      require: standard
      inherit_gem:
        standard: config/base.yml
      AllCops:
        DisabledByDefault: true

One caveat of how erb-lint uses Rubocop is that the tool considers all Ruby code in isolation (i.e as though it were an individual Ruby file). This means we need to disable some of the Standard config in order to obtain sensible results.

To do this, we can inherit from another config file in addition to the Standard gem. We disable 3 rules that make little or no sense when linting inline Ruby code in an .erb file.

  1. Layout/TrailingEmptyLines: we don’t need trailing new lines when the Ruby code is inline.
  2. Layout/InitialIndentation: the initial indentation might not be the start of the line when we write Ruby code in our views.
  3. Lint/UselessAssignment: erb-lint cannot distinguish between useless and useful assignment, because it doesn’t retain any context. It can’t know whether or not a variable is subsequently used.
# .erb-lint.yml
---
EnableDefaultLinters: true
linters:
  Rubocop:
    enabled: true
    rubocop_config:
      require: standard
      inherit_gem:
        standard: config/base.yml
+     inherit_from: .erb-lint_rubocop.yml
      AllCops:
        DisabledByDefault: true
# .erb-lint_rubocop.yml

Layout/TrailingEmptyLines:
  Enabled: false

Layout/InitialIndentation:
  Enabled: false

Lint/UselessAssignment:
  Enabled: false

Now we can lint our views including the Ruby code they contain using Standard - helping us to write better styled and more consistent code in our views!