Over the line

Rubocop to the rescue!

~5 min read in opensource

I decided to drop the GSoC related titles and focus on the things that I work during the week. That means I'll probably blog more often :p

This week I mostly focused on cleaning the code of fedoraruby and conforming to the ruby/rails community guidelines.

The gem that helps you do that is rubocop and is kind of the standard method in the ruby world.

RuboCop

Rubocop refers to each check as a cop. There are a bunch and you can see the supported ones by reading these [files][rubo-enabled].

After installing rubocop, call it with the rubocop command and it will check all Ruby source files in the current directory.

If working on a Rails project, you have to invoke it with the -R flag.

The first time I ran rubocop I was presented with no more or less 666 violations. Which meant if I wanted to clean up the code I'd had to manually edit all 666 of them. Luckily, as you may have imagined, rubocop provides the -a/--auto-correct flag which does what it says. In the documentation there is a note: Experimental - use with caution. What the heck, I had nothing to lose, I am under version control so I could go back any time. It worked like a charm and this brought the violations to about 150. Not bad at all.

So what about the rest? Well, you have to do it manually and so I began. If you run rubocop without any flags, it uses the default config that ships with the gem. If you want to use your config you can do so by defining it with the -c flag.

Now, there is another cool feature rubocop provides. It can create a config file for you containing all the violations found so far. Run rubocop with the --auto-gen-config flag and that will create .rubocop_todo_yml in the current dir. Then you can check against that file with rubocop -R -c .rubocop_todo_yml.

All cops in this yaml file are set to false, which means they won't be taken into account, not unless you explicitly set them to true. That way you can work your way up in fixing all violations by enabling one cop at a time. Basically what is included in this file, overrides the default values.

If you want to omit calling on .rubocop_todo_yml every single time, place this in .rubocop.yml:

inherit_from: .rubocop_todo.yml

Form now on you can just call it with rubocop -R.

To sum up, run rubocop -R see that there is no violation, edit .rubocop_todo_yml, set one of the cops to true, run rubocop again, fix the errors and work your way up until there is no violation.

Of course all of these are optional steps. Ruby's interpreter doesn't care about identation, it won't complain if you run a method 20 lines long and it won't throw an error if you have chain 16 methods spanning to 300 chars. All these are conventions among the Ruby community and you are not compelled to follow them. BUT, it provides much cleaner code and when you find yourself contributing to a project, all these will probably matter.

In my case, you can see through this commit what changed and in this gist you can see the difference from before/after running rubocop. Dropped to 73 violations from 666.

I've skipped some of them as I didn't see fit, like commenting above every class what it does. I'm not saying this isn't good to have it's just it also includes migrations and I'd like to avoid that. Also some code will be deprecated/rewritten any time soon so it doesn't make sense to fix the violations if I'm to remove the code afterwards.

HoundCI

Rubocop is good to test locally, but what about the code you host remotely? Enter Houndci.

Houndci is a web app written in Rails by Thoughtbot that integrates with your github account. It checks for violations every time a Pull Request is submited against your repository. It relies on the rubocop gem, but it may follow different approaches than rubocop.

I almost spent a day to find this out. I'll tell you what I mean since there was a particular error that made me search for many hours.

Let's start by saying that it is common practice to not have lines spanning on more that 80 characters. Python has it pinned to 79.

In rubocop, there is a cop that checks for method chaining. When the line is too long you should break it down, so this cop checks whether the dot(.) that chains two methods is placed before or after the methods. Here's an example to better visualize it:

def method_with_arguments(argument_one, argument_two)
  a_really_long_line_that_is_broken_up_over_multiple_lines_and.
  subsequent_lines_are_indented_and.
  each_method_lives_on_its_own_line
end

When I ran rubocop locally it complained with Place the . on the next line, together with the method name. Ok I did that and pushed. Then why was houndci told me otherwise?

Digging in rubocop's default config file I found that this particular cop was invoking an additional parameter: EnforcedStyle: leading. Interesting, so why houndci was telling me the opposite? Digging some more, this time in rubocop's source code, I found the responsible method. It seems rubocop gives you the option to decide which style fits you better and from what I've seen so far, houndci prefered the trailing dot. Ok let's fix that.

Reading the configuration guide, and since houndci uses rubocop, I copied .rubocop_todo_yml to .hound.yml. There, following the config file I appended

Style/DotPosition:
  EnforcedStyle: leading
  Enabled: true

in .hound.yml, pushed the change to my repo and created a test pull request to check if it worked. No... but whyyyy??

After some more more digging, this time at the issue tracker of houndci, I finally found the culprit. The latest version of rubocop changed the way cops are presented and that broke compatibility with houndci. Back to .hound.yml I removed Style/ and pushed to github. Finally, this time it was fixed.

Not much of a story, probably you already got bored or didn't make it this far, but anyway. Onto more interesting stuff, until next time it is.

comments powered by Disqus