December 21, 2018

Basic Capybara-Gauge: Review the Ruby code with Rubocop

This is the seventh part of a series of blog posts. Care to go back to the beginning

Tinkering with Ruby only for the past year, Rubocop, a "Ruby static code analyzer and formatter, based on the community Ruby style guide" according to their Github site is both a blessing and a curse for someone new to the language. It is blessing that it is so detailed with output. The curse is that if you are new, it is going to take a lot of random Googling to figure out what all the errors thrown actual mean!

There is a lot of content in the Ruby Style Guide...

What is the Ruby Style Guide?

From the Ruby Style Guide README: "This Ruby style guide recommends best practices so that real-world Ruby programmers can write code that can be maintained by other real-world Ruby programmers. A style guide that reflects real-world usage gets used, while a style guide that holds to an ideal that has been rejected by the people it is supposed to help risks not getting used at all—no matter how good it is. [...]

"There are some areas in which there is no clear consensus in the Ruby community regarding a particular style (like string literal quoting, spacing inside hash literals, dot position in multi-line method chaining, etc.). In such scenarios all popular styles are acknowledged and it's up to you to pick one and apply it consistently".



The Ruby Style Guide gives examples of Ruby code -- both good and bad -- about:
  • Indentations should be two spaces
  • Semicolons should not appear at the end of a line of code
  • Using Curly braces? { Add Spaces Around Them }
  • Using parenthesis or square brackets? [Don't put spaces inside]
  • Quoting a variable in a string? Use 'the #{variable_name}'
  • Declaring a string? Use 'single quotes'.
Rubocop will hunt down every infringement on this style guide and make sure to tell you about it.

To add Rubocop to your working directory you can:
  • gem install rubocop
... Or you can add it to your Gemfile:
gem 'rubocop', '~> 0.61.1', require: false

To call it, just update bundler, go into the step_implementation folder where all your Ruby files are kept, and run Rubocop.
  • bundle update
  • rubocop
Note: You will need to create in the root directory of your project a .ruby_version file containing the Ruby version, such as "2.4.3", else Rubocop will be judging your code vs older versions of Ruby.  

Commonly Seen Errors and How To Fix Them

 Style/MutableConstant: Freeze mutable objects assigned to constants.

Ruby can be a weird language. You would think that declaring a CONSTANT in all caps, it would be immutable, unchangeable. Nope! There are two ways to fix it so it behaves like you would expect a constant to work:
  • Add: CONSTANT = value.freeze 
  • Add to the top of each Ruby file: # frozen_string_literal: true
But what is # frozen_string_literal: true?

"# frozen_string_literal: true is a magic comment, supported for the first time in Ruby 2.3, that tells Ruby that all string literals in the file are implicitly frozen, as if #freeze had been called on each of them. That is, if a string literal is defined in a file with this comment, and you call a method on that string which modifies it, such as <<, you'll get RuntimeError: can't modify frozen String.
"The comment must be on the first line of the file.
"In Ruby 2.3, you can use this magic comment to prepare for frozen string literals being the default in Ruby 3". -- StackOverflow

Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.

  • When declaring Strings, do 'this' not "that".
Layout/IndentationWidth: Use 2 (not 3) spaces for indentation.

  • Ruby uses two spaces, not three as in Java. Ruby uses two spaces, not three as in Java. Ruby uses two spaces, not three as in Java. I keep on forgetting!

Style/GuardClause: Use a guard clause instead of wrapping the code inside a conditional expression

Taking an example from Martin Fowler's new edition of Refactoring, examine the following code:

 function getPayAmount() {  
  let result;  
  if (isDead)  
   result = deadAmount();  
  else {  
   if (isSeparated)  
    result = separatedAmount();  
   else {  
    if (isRetired)  
     result = retiredAmount();  
    else  
     result = normalPayAmount();  
   }  
  }  
  return result;  
 }  

This is very hard to read! It would be easier writing it this way:

 function getPayAmount() {  
  if (isDead) return deadAmount();  
  if (isSeparated) return separatedAmount();  
  if (isRetired) return retiredAmount();  
  return normalPayAmount();  
 }  

Many more examples of Guard Clauses and how to fix them at the Rubocop Style Guide entry of Guard Clauses at https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/GuardClause

Rubocop also checks that:
  • All Gems in the Gemfile are listed in alphabetical order
  • That there aren't any extra blank lines or extra spaces
  • That there isn't inconstant indentation
  • Each modules should have a top-level module documentation comment.
  • Each line is only 80 characters long
  • You are using spaces and not tabs to indent
With Ruby, use only two spaces to indent code:

"Indent your code with two spaces per logical level. Really, it's that simple. Unlike in some other communities, there is no contention in the Ruby community over whether to use two, four or eight spaces. Everyone is happy to use just two. Furthermore, never use tabs, which includes the practice of mixing tabs with spaces" - From Caliban.org Ruby Guide.


... Well, that's it for this project! I hope you had as much fun reading this blog series as I had writing it!

Happy Testing!


-T.J. Maher
Sr. QA Engineer, Software Engineer in Test
Meetup Organizer, Ministry of Testing - Boston

Twitter | YouTubeLinkedIn | Articles

No comments: