Why Style Matters
Freedom of expression is a good thing, and we all have our own style of coding. One of the great aspects of Ruby is it gives you a lot of liberty with the syntax. Some people always leave out parentheses, unless they’re required, while others always include them, even when they are superfluous.
Picture by Man Alive!
When you work together with others on a project, the team should agree upon a coding standard and strictly adhere to it. Exactly what is in the standard is less important, but everyone agreeing to it and following it is crucial.
Opening up files in a project to find a different style in each file subtracts a lot from the readability, which is a key thing when collaborating with others. You are likely going to spend more time reading other peoples’ code than your own, so cringing every time you open up a file is not going to be fun in the long run…
Be Uniform
Picture by Michael Nutt
Consistency is key. I’ve seen code written by a single author who managed to be inconsistent with himself! Different levels of indentation, different spacing, different variable naming, e.g.
def foo(var_a,var_b, var_c, varD )
fooBar = "a(#{a}) "+ 'b(' +b+') cd(' +bar( var_c,varD)+")"
return fooBar
end
(code changed to protect the guilty)
Compare this to the below:
def foo(a, b, c, d)
"a(#{a}) b(#{b}) cd(#{bar(c, d)})"
end
Quite a difference, right?
Ruby Style Guide
In Ruby, there is more than one way to do the same thing. For instance,
- Change the load path by using either
$:
or$LOAD_PATH
- Create a proc using
proc
orProc.new
- Use a
module
withmodule_function :foo
or aclass
withdef self.foo
for an object which only has class methods.
Then, there are some things which are a matter of taste. For example, how you indent, ending lines with a semicolon, if you use parenthesis for a method without arguments, spacing after a comma, etc. What you choose is up to you, but there are good reasons for picking one over the other.
There are a number of Ruby style guides available, and which one you select is up to you and your team:
- https://github.com/bbatsov/ruby-style-guide
- https://rails.lighthouseapp.com/projects/8994/source-style
- https://github.com/chneukirchen/styleguide/
- https://www.caliban.org/ruby/rubyguide.shtml
This is far from a complete list. In the end, an important consideration is if you can automate style compliance validation, as manual inspection is slow and faulty.
Automatic Style Checking
To enforce the chosen style, you should deploy an automatic style checker. For some languages, like go, this is really easy, as the language itself comes with with it (see the go fmt
command).
In Ruby, you have to rely on external tools.
Rubocop
I prefer to use rubocop
as it is fast, highly configurable, and comes with sensible defaults.
Continuous Integration
To prevent badly formatted code from making it into the repo, you could use a pre-commit hook (if you are using git). You should also include an automated check in your continuous integration server. With Rubocop, it is really easy, as there is a rake task that does it for you:
require 'rspec/core/rake_task'
require 'rubocop/rake_task'
task default: [:rubocop, :spec]
RSpec::Core::RakeTask.new
Rubocop::RakeTask.new
This will now run style checking before it runs the (rspec
) tests.
Similarly, if you are running guard, adding Rubocop to the Guardfile is simple using a gem like guard-rubocop. The point is, working automated style checking into your routine is simple, and the payoff is well worth it.
Sample Output
Running the horrific piece of code I showed you at the beginning through rubocop
yields the following list of violations:
$ rubocop foo.rb
Inspecting 1 file
C
Offences:
foo.rb:1:14: C: Space missing after comma.
def foo(var_a,var_b, var_c, varD )
^
foo.rb:1:33: C: Space inside parentheses detected.
def foo(var_a,var_b, var_c, varD )
^
foo.rb:1:35: C: Trailing whitespace detected.
def foo(var_a,var_b, var_c, varD )
^
foo.rb:2:1: C: Use 2 (not 3) spaces for indentation.
fooBar = "a(#{a}) "+ 'b(' +b+') cd(' +bar( var_c,varD)+")"
^^^
foo.rb:2:4: C: Use snake_case for variables.
fooBar = "a(#{a}) "+ 'b(' +b+') cd(' +bar( var_c,varD)+")"
^^^^^^
foo.rb:2:23: C: Surrounding space missing for operator '+'.
fooBar = "a(#{a}) "+ 'b(' +b+') cd(' +bar( var_c,varD)+")"
^
foo.rb:2:30: C: Surrounding space missing for operator '+'.
fooBar = "a(#{a}) "+ 'b(' +b+') cd(' +bar( var_c,varD)+")"
^
foo.rb:2:32: C: Surrounding space missing for operator '+'.
fooBar = "a(#{a}) "+ 'b(' +b+') cd(' +bar( var_c,varD)+")"
^
foo.rb:2:41: C: Surrounding space missing for operator '+'.
fooBar = "a(#{a}) "+ 'b(' +b+') cd(' +bar( var_c,varD)+")"
^
foo.rb:2:46: C: Space inside parentheses detected.
fooBar = "a(#{a}) "+ 'b(' +b+') cd(' +bar( var_c,varD)+")"
^
foo.rb:2:52: C: Space missing after comma.
fooBar = "a(#{a}) "+ 'b(' +b+') cd(' +bar( var_c,varD)+")"
^
foo.rb:2:58: C: Surrounding space missing for operator '+'.
fooBar = "a(#{a}) "+ 'b(' +b+') cd(' +bar( var_c,varD)+")"
^
foo.rb:2:59: C: Prefer single-quoted strings when you don't need string interpolation or special symbols.
fooBar = "a(#{a}) "+ 'b(' +b+') cd(' +bar( var_c,varD)+")"
^^^
foo.rb:3:1: C: Inconsistent indentation detected.
return fooBar
^^
foo.rb:3:3: C: Redundant `return` detected.
return fooBar
^^^^^^
foo.rb:5:1: C: 1 trailing blank lines detected.
1 file inspected, 16 offences detected
Wrapping Up
Whenever you start a new project, be sure to include automatic style checking at the very beginning, or it will take a major effort to get all files into compliance.
If you inherit an existing project with divergent styles, you can exclude the checks which generate the most failures so you slowly can bring them in order.
However you do it, enforce your style. You’ll be very glad you did.