Rubocop: The Sheriff of Your Codebase
Published: 2023-02-24 13:26:29

## Introduction

I have to admit it sometimes, it may happens that I use print or prett-print in my code to debug. Even though byebug is really useful (use it really..), there no day I do not use it personnally, sometimes printing your variables is useful.


Nonetheless despite the usefulness of print statements for debugging, they can also have serious drawbacks. In fact, printing can really be evil in some situation. And it may happen that you forgot a print and push it in your feature branch. 

It is why I have decided to create my own Cop for Rubocop so I am sure that I always remember to remove my debug print. 

So In this article, I'll be sharing my journey of creating and integrating my own custom Rubocop Cop, with the goal of improving my code and avoiding the pitfalls of unnecessary print statements. 
## Rubocop

I will make a quick intro about RuboCop, as I think this is one of the most popular ruby gem. And most of rubyist know about it and have or will use it at some point. 
RuboCop is a linter and formatter for you ruby's app. 
It is extendable and you can create your own formatting rules, this what we are going to do in the following part ! 

Let's see RuboCop in action: 

## Create my own Cop

Like I said before sometimes I push evil pretty-print on my branch. Of course it does not go to production since our code is systematically reviewed by coworkers.

But I was a little bit annoyed to have to often go back to my code and remove the print command, and make a new commit only for this. So I decided to make a custom Cop and improve my code quality thanks to it.

It is quite easy by the way. I will show you the whole process I followed in order to create it : 

I've created a `print_cop.rb` file, in a folder called custom_cop that I've createad in lib folder. 

We end up with `./lib/custom_cop/print_cop.rb`

Then I had to dig in the [RuboCop Documentation](https://docs.rubocop.org/rubocop/1.45/development.html) to know how to create a cop and how to test it. This doc is really great and you will learn a lot of

Once this done I could write my Cop. 
```
module RuboCopCustom
  class PrintCop < RuboCop::Cop::Cop
    def_node_matcher :print_cop, <<-PATTERN
      (send nil? :pp ...)
    PATTERN


    MSG = 'Do not forget to remove the print'.freeze

    def on_send(node)
      print_cop(node) do
        add_offense(node,message: MSG)
      end
    end

  end
end
```

Let's break this into smaller piece :

### Create our Pattern
The first part :
```
    def_node_matcher :print_cop, <<-PATTERN
      (send nil? :pp ...)
    PATTERN
```

Thanks to the macro def_node_matcher we can define the pattern that we want to catch and declare as an offense in our Ruby code. 
You should have noticed the `...` after the pp, it tells RuboCop that we do not care about what comes next. 
But how can you know this pattern? 
I will reassure you, you do not have to know the AST by heart. 
RuboCop uses the parser gem that you can install and run : 

```
$ gem install parser
$ ruby-parser -e 'pp "salut"'
  (send nil :pp
    (str "salut"))
```


### Adding the offense

The next important part is the following one where you the definition of the offense : 
```
def on_send(node)
  print_cop(node) do
    add_offense(node,message: MSG)
  end
end
```

The on_send callback is use for all the send ast node it find in your ruby code. 
Then it pass through the print_cop helper if it matches the pattern it will add the offense here. 

That's it our custom cop is good now. I could add a autocorrect of the offense, but maybe in some case we really want to push the pretty-print so I did not do it. 

But the work is not over ! Now we have to test it !

## Testing 

Now we have to test our custom Cop. We can use the custom expect_offense helper for this. Personally I had to go to the [Rubocop repository](https://github.com/rubocop/rubocop) and I went in their config specs. 

I get this from it and add it to my spec_helper

```
require 'rubocop'
require 'rubocop/rspec/support'
RSpec.configure do |config|
  config.include RuboCop::RSpec::ExpectOffense
  ##some other configuration
end
```

And then you can use the custom expect_offense like this :

```
require_relative '../../../lib/custom_cops/print_cop'

RSpec.describe RuboCopCustom::PrintCop, :config do
  it 'registers an offense when using pretty print' do
    expect_offense(<<~RUBY)
      pp 'hello'
      ^^^^^^^^^^ Do not forget to remove the pint
    RUBY
  end
end
```

And eventually you can run your test with the custom helpers for your cop. 


## Conclusion

Rubocop is a powerful tool that can greatly improve the quality and consistency of your code. Whether you're working on a small project or a large-scale application, Rubocop is definitely worth considering as a part of your development toolkit.

If you have any feedback or questions about Rubocop, feel free to leave a comment or reach out. And always remember to... Thanks Rubocop for keeping your production environment safe and secure!

## Reference
If you want to see a excellent article about that check the [EvilMartians one](https://evilmartians.com/chronicles/custom-cops-for-rubocop-an-emergency-service-for-your-codebase)