How to add a custom cop to Rubocop

Dmytro Vasin
2 min readFeb 27, 2019

Задача: In the project, as part of refactoring, we need to change and reduce values to one type in attributes (in tests).

For example, such RSpec methods as “describe”, “context”, “it”, “its”, “it_behaves_like can be called with an optional attributes “freezed_date” or “freezed_time” with a Data value. These attributes should be replaced with the representation of the same date, only as a string in the format “iso8601”

Part one: Finding a solution.

Method # 1: Brute-force

Use CTRL + F to find all hits and replace them.

The problem with this approach is that we have a little less than a thousand of such replacements and dates are practically all different and have a different call format starting from “Time.new”, “Date.today” to “Date.today.midnight”.

Moreover, the work on the project did not freeze — and at the time of rewriting, developers can add more tests with such attributes.

Method #2: RegExp

Write a regular expression that covers all methods, and then apply it in auto-correction.

Even if we imagine that this is possible, the downside is that in a month it will be unrealistic to understand and reuse this code — since one God will know how it works. (How much time do you need to understand the following regular exp )

Method #3: Ruby AST

Ruby 2.6. allows a great opportunity to parse AST tree.

After sitting for an hour or two, I wrote a simple algorithm that could parse and find the code I needed. But I didn’t realize how to combine its built-in methods, but I didn’t really like working with the code as text in the file.

Method #4: Rubocop with a custom cop

Working with AST reminds me of how RuboСop work: search by code and auto-replacement. After that, I decided to write “cop” which will emphasize the hits I need.

Part two: Solution.

Write a cop that is looking for method calls “describe”, “context”, “it”, “its”, “it_behaves_like”. When calling these methods, the second (optional) attribute is hash. Such hash can have key-value pair with key: “freezed_date” or “freezed_time”, and value: Method call ( “Time.new”, “Time.utc” is a method call construction )

In order to run this cop — you need:

  1. Require it
  2. Launch it separately from other cops

After launch we will see:

Now it remains to add the auto-correct “auto-correct” feature:

I was pleasantly surprised that Rubocop allows me to make such changes so simple and easy!

--

--