DEV Community

Cover image for Mastering Linters : A Code Quality Assurance Comprehensive Guide using Ruby on Rails
Pimp My Ruby
Pimp My Ruby

Posted on

Mastering Linters : A Code Quality Assurance Comprehensive Guide using Ruby on Rails

As developers, we all come from different backgrounds, each with our own coding style. When working on personal projects, we have the freedom to express ourselves in any way we like—using functions with approximate names, methods with 40 lines of code... However, when collaborating on a collective project, conflicts often arise due to varying coding styles. This is where we introduce coding conventions, and our best friend for enforcing these conventions is the linter.

In this article, we'll dive into the world of linters, explaining their essential role in every development project. We'll also explore a powerful tool called Lefthook, designed to simplify the management of linters and other code quality-related tasks in our workflows. Finally, I'll introduce a selection of linters that I use daily, which have a significant impact on the quality of our code.

What Exactly Is a Linter?

A linter is a tool designed to help developers maintain clean and consistent code. It does this by statically analyzing a project's source code to identify issues. These issues can range from simple syntax errors to subtle logic errors. Additionally, linters check if the code adheres to the defined project standards.

The strength of linters lies in their customizability. If a rule doesn't suit your needs, you can choose to override it.

Linters are versatile tools capable of detecting a wide range of minor issues. Examples of common formatting issues include extra spaces, lines that are too long, unused variables, and unknown references.

As a result, linters provide peace of mind to developers by helping them avoid common errors. You'll no longer have to spend three hours searching for the missing semicolon!

My List of Linters for Rails Projects

In all my Rails projects, I aim to establish consistency in code formatting. Let's explore the linters I use on a daily basis!

Rubocop

Rubocop is THE tool for Ruby. It focuses on adhering to the conventions established by the Ruby community. It checks elements like indentation, line length, the use of undefined variables, and more. Its goal is to make Ruby code as readable and consistent as possible.

To use it in your project, it's as simple as:

bin/bundle exec rubocop .
Enter fullscreen mode Exit fullscreen mode

Image description

Rubocop is powerful because it also acts as a code formatter. By using the -A option, it will automatically correct simple issues like excessive line breaks.


i18n-tasks

In projects that use I18n, I introduce i18n-tasks. It identifies untranslated strings in translation files and unused translations.

This tool allows me to identify translation gaps in my applications, making it incredibly useful when managing multiple languages in your applications.

To use it, it's as straightforward as:

bin/bundle i18n-tasks health
Enter fullscreen mode Exit fullscreen mode

Image description

But i18n-tasks does much more:

  • i18n-tasks find helps find all usages of a translation key in your code.
  • i18n-tasks add-missing fills in missing translation keys in your translation files with a placeholder.
  • i18n-tasks translate-missing takes it a step further by creating and translating all missing keys, with the option to use Deepl, Google Translate, and others.

i18n-tasks offers numerous features to simplify your translation key management. It's a gem that's worth exploring!


Reek

Reek is your best friend for detecting what we call "code smells." Code smells are a set of indications that show your code is tightly coupled. I'll write a more in-depth article on this, but the key takeaway is that tightly coupled code is challenging to maintain and evolve.

Once installed, you can simply use it like this:

bin/bundle exec reek app
Enter fullscreen mode Exit fullscreen mode

Image description

Let's explore some examples of errors that reek can detect:

  • Duplicate Method Call: When you call the same method multiple times with the same arguments.
  • Boolean Parameter: When you pass a boolean argument, implying there are two execution paths in your method. This is a strong indicator of coupling.
  • Missing Safe Method: Every "dangerous" method (ending with !) should have a corresponding "safe" method.

Brakeman

brakeman is a security linter specifically designed for Ruby on Rails. It searches for potential security vulnerabilities in the code, such as Cross-Site Scripting (XSS) or SQL injection vulnerabilities. Using Brakeman is essential for maintaining a Rails application that is less vulnerable to security issues.

Once installed, you can run brakeman with:

bin/bundle exec brakeman -z -q
Enter fullscreen mode Exit fullscreen mode

Image description

Here are some errors that brakeman can identify:

  • Cross-Site Scripting: When you execute code that can be manipulated by the user, for example, through unfiltered params.
  • Dangerous Send: Using .send can be dangerous if you don't validate the data passed as a parameter.
  • Unscoped Find: When you search for a record directly through the model instead of the object that owns it.

Rails Best Practices

Rails Best Practices is a tool that focuses on Ruby on Rails projects. It identifies violations of Rails-specific best practices, contributing to project consistency and helping avoid common pitfalls.

To use it:

bin/bundle exec rails_best_practices .
Enter fullscreen mode Exit fullscreen mode

Image description

All the rules of this linter are available on https://rails-bestpractices.com. While many of these rules are old, dating back to 2013, the majority of them are still applicable and relevant today.

Here are examples of rules that rails_best_practices checks:

  • default_scope is Evil: Avoid using default_scope as it cannot be overridden.
  • Tell, don’t Ask: This well-known programming principle encourages providing instructions or expressing actions directly rather than querying an object about its state to make decisions.
  • Restrict auto-generated routes: When you use resources for your routes, Rails generates all CRUD routes by default. However, many times, not all of these generated routes are used.

Haml-lint / Erb-lint / Slim-lint

Your views also need to be consistent! It's essential to have a linter for any template syntax. Here, we'll focus on haml-lint.

Let's see how haml-lint works:

bin/bundle exec haml-lint app/views
Enter fullscreen mode Exit fullscreen mode

Image description

The rules enforced by this linter ensure that your views are consistent and uniform.

Here are examples of rules that haml-lint checks:

  • Implicit Div: Avoid creating unnecessary div elements.
  • Alt Text: All images must have the alt tag.
  • Repeated Id: Ensure that an ID is not used multiple times in the view.

Scss-lint / Css-lint

Scss-lint is specifically designed for the Sass/SCSS language. It identifies syntax, indentation, and style issues in your stylesheets, helping maintain clean and organized CSS code.

Once installed, you can run it with:

bin/bundle exec scss-lint app/assets/stylesheets/
Enter fullscreen mode Exit fullscreen mode

Image description

Here are examples of rules enforced by scss-lint:

  • Zero Unit: Do not use units after a 0.
  • BEM Depth: Ensure compliance with the BEM (Block Element Modifier) naming convention.
  • Property Units: Verify the use of the correct measurement units for attributes.

Setting Up Git Hooks with Lefthook

Having all these linters is great, but it places a significant mental burden on developers to run them manually every time. Fortunately, git hooks exist!

In practice, a git hook is an action that executes when you use Git commands. For example, you can say, "Run this linter before I commit." This is incredibly powerful!

The problem? Git hook syntax is not very developer-friendly. However, for every problem, there's a solution, and let me introduce you to Lefthook.

Lefthook is a tool that manages your Git hooks, exposing a YAML file to orchestrate your Git hooks. It's available as a Ruby gem and an NPM package.

Let's see how to easily set up Lefthook in your Rails project.

Once you've installed the gem with bundle add lefthook, you'll create a lefthook.yml file at the root of your project. In this file, you'll specify all the linters you want to run with every Git action.

Here's a simple example of a lefthook.yml that sets up rubocop:

pre-commit:
  commands:
    rubocop-linter:
      glob: "{*.rb,*.rake,*.jbuilder,*.builder,*.ru,Capfile,Gemfile,Rakefile}"
      files: git diff --name-only master
      run: bundle exec rubocop --force-exclusion {files} | grep "no offenses"
      skip:
        - merge
        - rebase
Enter fullscreen mode Exit fullscreen mode

The list of instructions I provide to lefthook is straightforward. Before each commit, I run rubocop on all the files I've edited on my local branch. If there's an issue with rubocop, the commit fails.

To enable the Git hook, simply install it on your machine with the following command:

bin/bundle exec lefthook install
Enter fullscreen mode Exit fullscreen mode

And there you go! The Git hooks are installed on your machine!

Image description

Keep in mind that the example we've seen is simple. In larger projects, you'll want to run all the linters we discussed earlier.

Conclusion

Linters are more than just static code analysis tools. They are the silent guardians of code quality, contributing to more stable, readable, and efficient projects. By combining linters with Lefthook, you can automate code quality monitoring in your project while reducing human errors.

Clean code is not a luxury but a necessity. Linters and automation tools like Lefthook are essential parts of the modern developer's toolkit. So, don't hesitate to integrate these tools into your development workflow and reap the benefits they offer for code quality and project robustness.

Top comments (3)

Collapse
 
starswan profile image
Stephen Dicks • Edited

In ruby, guard is a nice alternative to git hooks. The checks are run as files are changed so e.g. rubocop can be set to autocorrect every file as you go along. Also pronto is another nice solution to the same issue - it can run all your checks in one go, and has pull request integration so the checks can be done visually, even if a tiny bit later in the flow

Collapse
 
pimp_my_ruby profile image
Pimp My Ruby

Interesting, thanks for the recommendation!

Collapse
 
brdnicolas profile image
Nicolas B.

Damn! i18n-tasks is very interesting!

Thank's for this article :)