Setting up your next Rails project with RuboCop - a 101 guide
hix.dev
Posted on February 7, 2020
RuboCop is the most popular Ruby static code analyzer and code formatter, code linter in short. Linting is the process of running a program that analyzes the code for programmatic and stylistic errors.
Using it translates to a better code quality, clean code, and better experience for the readers.
In this tutorial I will explain how you can implement it in your next Rails project.
It's also available in a 1:1 format on our original site if you prefer to read from the source.
Let's get started.
What is RuboCop and Ruby Style Guide?
Out of the box, Rubocop enforces most of the guidelines outlined in the Ruby Style Guide maintained by the Ruby community. The guide groups its guidelines by related sections, which corresponds to RuboCop gems departments.
There are nine departments in the base RuboCop gem. Numbers might change in time and are given here only for the rough overview of what the gem offers.
- Layout - 87 cops
- Linting - 81 cops
- Metrics - 10 cops
- Migration - 1 cop
- Naming - 16 cops
- Security - 5 cops
- Style Cops - 170 cops
- Bundler - 4 cops
- Gemspec - 4 cops
In total, there is a little less than 400 rules applicable to anything written in Ruby, if we choose so.
But, it does not end here - let us take a quick look at accelerating the Ruby code by following extended RuboCop guidelines.
RuboCop Performance
There's an extension wisely encapsulated in its own Ruby gem called RuboCop Performance. Its job is pointing out Ruby code optimization opportunities.
All 25 rules of the RuboCop Performance gem are grouped in the single RuboCop department, called Performance.
It is a relatively young project and some of the cops included were originally placed in the main RuboCop gem.
If your Ruby program is really performance-dependent, you might want to additionally look at the fasterer gem, based on the open-sourced Github repository called Fast Ruby - the biggest collection of Ruby benchmarks.
Let us now take a look into other, framework-specific RuboCop extensions.
RuboCop Rails and Rails Style Guide
When RuboCop is a code linter of your choice, RuboCop Rails extension is the must-have for your Ruby on Rails projects.
RuboCop Rails is a RuboCop extension focused on enforcing Ruby on Rails coding conventions and best practices, neatly described in the Ruby on Rails Style Guide.
The style guide itself is split into 16 sections, covering things such as Ruby on Rails configuration, dealing with time, Models, Controllers, Mailers and more.
Whenever in doubt on how to approach Ruby on Rails related problem, try browsing the relevant style guide section, or go one step further and let your projects Continuous Integration point it out for you, by configuring RuboCop gem with all its extension on either GitlabCI or CircleCI.
RuboCop RSpec and RSpec Style Guide
If RSpec is your weapon of choice unit-testing-wise, setting up the RuboCop RSpec extension will help you a lot.
RuboCop RSpec is a RuboCop extension that provides RSpec, RSpec for Rails, FactoryBot and Capybara analysis for Ruby projects, which in total gives us another 76 rules to follow, for the sake of neat codebase.
All of the RuboCop RSpec cops - and then some - are extensively described in the RSpec Ruby Style Guide, providing examples of the code that follows the guideline paired with the one that does not.
It is my personal favorite style guide of all provided by the RubCop HeadQuarters, and I rely on it a lot during Code Reviews - my Github account saved replies are linking to it heavily.
Next to the gem itself, the guide gives an extremely useful overview of how to write unit tests of Ruby on Rails related parts, such as the MVC Models, Views, Controllers and also Mailers.
Unfortunately, some of the Rails related rules are not possible to enforce via the gem itself, hence the Github saved replies linking to the relevant guide sections.
It is still a great tool whenever you use RSpec in your Ruby and Ruby on Rails projects.
RuboCop MiniTest and MiniTest Style Guide
If you use MiniTest, the second most popular choice for testing the Ruby written programs, there's a RuboCop extension dedicated to it, called RuboCop MiniTest.
It enforces best practices outlined in the MiniTest Style Guide, yet another great read provided by RuboCop HeadQuarters.
It's a relatively young project comparing to the more popular RSpec choice. It describes a total of 15 rules for writing unit tests using the MiniTest framework.
RuboCop set up in a single Ruby on Rails project
Now that we went through most of the RuboCop extensions - there are two more, for Rake and Markdown - let us take a look at how to hook it all up in the Ruby on Rails application.
RuboCop and its extension configuration files are written in YAML. They are all placed in the same config directory, in each of the repositories respectively, assisted with a great RubyDoc documentations, which altogether makes it very easy to find and understand every rule.
When working on the Ruby on Rails project that uses either RSpec or MiniTest, we are going to install 4 RuboCop gems:
- RuboCop
- RuboCop Performance
- RuboCop Rails
- RuboCop RSpec or RuboCop MiniTest
Go ahead and edit your Gemfile
, adding the following to the development and test group.
group :development, :test do
gem 'rubocop'
gem 'rubocop-performance'
gem 'rubocop-rails'
gem 'rubocop-rspec' # or gem 'rubocop-minitest'
end
Next in your CLI, navigate to the directory path of your Ruby on Rails project and run the bundle install
command in order to update your Gemfile.lock
.
Now that all the gems are installed, let us prepare our custom RuboCop configuration file, called .rubocop.yml
.
It is responsible for two things:
- Importing installed extensions
- Defining which cops we do and do not want to enforce
By default, it uses the predefined RuboCop configuration with no other extensions installed, as it is designed to work with any Ruby language codebase.
In order to include the installed extensions, create the following RuboCop configuration file.
require:
- rubocop-performance
- rubocop-rails
- rubocop-rspec # or rubocop-minitest
Next, we are going to follow the example configuration for Ruby on Rails project presented in the RuboCop documentation - but in order to use the aforementioned extensions, we are going to skip some of it.
Add the following to your configuration file.
AllCops:
Exclude:
- 'db/**/*'
- 'script/**/*'
- 'bin/**/*'
It tells RuboCop to exclude given directories while analyzing the codebase - the readability does not matter there too much and is not worth bothering for the pleasing Ruby on Rails development.
Next, let us cut some additional slack while working with RuboCop. Those are the sane RuboCop rules to use with Ruby on Rails project.
Below the previous configuration, add the following.
Metrics/LineLength:
Max: 100
Metrics/BlockLength:
Exclude:
- config/**/*
- spec/**/*
Lint/AmbiguousBlockAssociation:
Exclude:
- spec/**/*
Style/Documentation:
Enabled: false
Most of those are pretty self-descriptive, but let's go through them quickly:
- Line length cop - that's the one you might want to change to your liking - it enforces a maximum amount of characters per single line of the code
- Block length cop is told to ignore the config and spec directories, as those blocks of code tend to grow like mad, and we don't want to discourage our contributors to write fewer tests, right?
- Ambiguous block association is turned off for the tests directory, as encouraged by the RuboCop HeadQuarters rockstar, bbatsov
- Documentation cop is turned off, as there are better ways to document the Ruby on Rails code
It is important to remember that nothing here is written in stone - if you ever feel like you waste more time than its worth fixing some "stupid RuboCop rules", you can always disable them in this file.
At this point, assuming that you've recently created your brand new Ruby on Rails project the RuboCop analysis will give you some headache, as Ruby on Rails does not follow its default guidelines.
But worry not - most of it can be easily fixed using the RuboCop command with the --safe-auto-correct flag
.
Let's not be naive, everything can crash, no matter if its name contains "safe" - create a backup of your recent changes to git first.
Then, navigate to your project directory and run the following.
bundle exec rubocop --safe-auto-correct
It will fix all RuboCop violations that are marked as auto-fixable in the default configuration file. Browse the changes and if it seems ok, add it to git.
There's also a big chance that you work on some much older Ruby on Rails codebase, and the quick-fix presented above might not be that effective, nor the best idea - some stuff might actually break.
Thankfully to the awesome RuboCop maintainers, there's a solution for legacy code implementations, the --auto-gen-config
command-line flag.
In the root directory of your legacy Ruby on Rails project after installing all the gems and creating the configuration file, run the following.
bundle exec rubocop --auto-gen-config
It does two things:
- Analyzes the codebase for RuboCop violations, writing the exclusions into the .rubocop_todo.yml file,
- Updates the .rubocop.yml file with an import of the exclusions.
Now, if you run the bare bundle exec rubocop
command again, it will show 0 offenses.
This is a great way for improving legacy Ruby on Rails applications code: just tell your team, that whenever anybody works on it, they should:
- Look it up and comment-out a path to it in the TODO file,
- Fix what's possible,
- Delete the commented-out paths exclusions for good.
That way the Ruby on Rails codebase quality is going to improve vastly over time, eventually allowing developers to remove the TODO list completely.
Sharing RuboCop configuration in multiple Ruby on Rails projects
It is easy to set the RuboCop up for a single Ruby on Rails project, although, following the current microservices trend, that's hardly enough. There's a big chance that wherever you work at, your team either:
- creates multiple Ruby on Rails projects monthly (like Software Houses do),
- maintains multiple Ruby on Rails projects
RuboCop evolves, all its extensions do too, and developers' preferences change. Maintenance of all those moving parts along with keeping sane is not a trivial task. Imagine that company you work for has 15 microservices, all written in Ruby on Rails, and consider one of the following:
- Your team decides that they don't like Department/Cop anymore
- RuboCop releases a newer version
- RuboCop Rails releases a newer version
- RuboCop RSpec releases a newer version
- RuboCop Performance releases a newer version
It's quite a copypaste task to keep it all up to date.
Percy, the QA SaaS, and Google APIs propose (and use themselves) a solution to that - creating your own gem with all the required extensions installed, a single source of the RuboCop configuration.
With your own gem, the "only" thing you need to do is updating it (and either backward-fixing or ignoring the "new" violations after the update) in every single project. There's automatically less room for error, as the single configuration per project would require you to update the core, every single extension and on top of that, the configuration file.
There's another option available, although I'm not a big fan of it, as in my opinion it is not controlled enough and might actually do some damage - with RuboCop, you might include the configuration from an URL address, so theoretically you would not need to manually update it in every single project.
What I don't like about it: if you lint the code in your Continuous Integration suite, it might suddenly cause your jobs to stop passing if new rules won't be backward compatible with the old code - and as a result, stop the development just to fix them.
Summing up, if your team maintains and/or creates a lot of Ruby on Rails applications, consider encapsulating your RuboCop configuration in the separated, shared gem.
Advanced RuboCop configuration
For all the standards nazis like myself, I'm going to mention yet another detail that I've recently discovered about RuboCop: it does not enable all its options by default.
See it for yourself.
- Browse the config/default.yml file in the core RuboCop gem repository,
- Search
CTRL + F
for the "Enabled: false" phrase.
At the time of writing this guide, there are 35 occurrences of the searched phrase, two of which are some explanatory comments.
It seems that there are 33 additional rules that one can potentially follow, improving the Ruby on Rails codebase even further!
Those are:
-
Bundler/GemComment
: Add a comment describing each gem. -
Layout/ClassStructure
: Enforces a configured order of definitions within a class body. -
Layout/FirstArrayElementLineBreak
: Checks for a line break before the first element in a multi-line array. -
Layout/FirstHashElementLineBreak
: Checks for a line break before the first element in a multi-line hash. -
Layout/FirstMethodArgumentLineBreak
: Checks for a line break before the first argument in a multi-line method call. -
Layout/FirstMethodParameterLineBreak
: Checks for a line break before the first parameter in a multi-line method parameter definition. -
Layout/HeredocArgumentClosingParenthesis
: Checks for the placement of the closing parenthesis in a method call that passes a HEREDOC string as an argument. -
Layout/MultilineArrayLineBreaks
: Checks that each item in a multi-line array literal starts on a separate line. -
Layout/MultilineAssignmentLayout
: Check for a newline after the assignment operator in multi-line assignments. -
Layout/MultilineHashKeyLineBreaks
: Checks that each item in a multi-line hash literal starts on a separate line. -
Layout/MultilineMethodArgumentLineBreaks
: Checks that each argument in a multi-line method call starts on a separate line. -
Lint/HeredocMethodCallPosition
: Checks for the ordering of a method call where the receiver of the call is a HEREDOC. -
Lint/NumberConversion
: Checks unsafe usage of number conversion methods. -
Migration/DepartmentName
: Check that cop names in rubocop:disable (etc) comments are given with department name. -
Style/AutoResourceCleanup
: Suggests the usage of an auto resource cleanup version of a method (if available). -
Style/CollectionMethods
: Preferred collection methods: map, find, select, reduce, size by default. -
Style/ConstantVisibility
: Check that class and module constants have visibility declarations. -
Style/Copyright
: Include a copyright notice in each file before any code. -
Style/DateTime
: Use Time over DateTime. -
Style/DocumentationMethod
: Checks for missing documentation comments for public methods. -
Style/ImplicitRuntimeError
: Use raise or fail with an explicit exception class and message, rather than just a message. -
Style/InlineComment
: Avoid trailing inline comments. -
Style/IpAddresses
: Don't include literal IP addresses in code. -
Style/MethodCallWithArgsParentheses
: Use parentheses for method calls with arguments. -
Style/MethodCalledOnDoEndBlock
: Avoid chaining a method call on a do...end block. -
Style/MissingElse
: Require if/case expressions to have an else branches. -
Style/MultilineMethodSignature
: Avoid multi-line method signatures. -
Style/OptionHash
: Don't use option hashes when you can use keyword arguments. -
Style/ReturnNil
: Use return instead of return nil. -
Style/Send
: Prefer Object#send or Object#public_send to send, as send may overlap with existing methods. -
Style/SingleLineBlockParams
: Enforces the names of some block params. -
Style/StringHashKeys
: Prefer symbols instead of strings as hash keys.
Let's check out one that I personally like the most, the ClassStructure cop residing in the Layout department.
As its description states, Layout/ClassStructure
enforces a configured order of definitions within a class body. There are multiple things that can be included in the Ruby class, and thanks to this cop we can make sure that all the classes written in the codebase define them in the custom or default order:
- include
- prepend
- constants
- initializer
- public, private and protected methods
Next to those, there are multiple other Layout
, Lint
and Style
cops, that put together remind me of some very wise words that I've read a long time ago.
Any codebase should look like it is written by a single person.
"Clean Code" by Robert Cecil Martin
That is the effect you might accomplish via the disabled by default RuboCop configuration.
Conclusion
RuboCop is a very powerful and the most popular code linting tool for the Ruby language.
Its configuration is not rocket science, so the amount of community knowledge on writing a clean, easily maintainable code comes with a very low price - all it takes is installing a few gems and writing a simple YAML file.
The only justification of not using it is knowing all those rules by heart - and I seriously doubt that there's a lot of people who do.
Using RuboCop when developing Ruby on Rails applications helps developers to avoid common mistakes, guards from the code formatting discrepancies and allows them to create a codebase that is well maintained and easy to understand.
Use RuboCop in your Ruby on Rails projects, period.
Posted on February 7, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.