DEV Community

Cover image for 9 things that 🚨Rubocop🚨 don’t want you to use
Pimp My Ruby
Pimp My Ruby

Posted on

9 things that 🚨Rubocop🚨 don’t want you to use

One day, I got lost in the Rubocop documentation. I was struck by a realization: there are many Ruby features I didn't know existed because Rubocop tells us not to use them.

Today, I wanted to share with you the 9 discoveries that surprised me, which Rubocop recommends avoiding!

And / Or Operators (Style/AndOr)

Did you know it is possible to write:

ary = %w[hello world]
ary.size == 2 **and** ary.last == 'world'
=> true
Enter fullscreen mode Exit fullscreen mode

So why use && and || when you can directly write and and or?

The answer is quite simple, it's a matter of precedence compared to other operators.

The precedence table provided by the Ruby documentation shows us that the or and and operators are among the last to be evaluated in a Ruby expression.

In practice, this means we can form quite strange expressions, as indicated in the RubyStyle guide:

true or true and false # => false (it's effectively (true or true) and false)
true || true && false # => true (it's effectively true || (true && false)
false or true and false # => false (it's effectively (false or true) and false)
false || true && false # => false (it's effectively false || (true && false))
Enter fullscreen mode Exit fullscreen mode

And it is due to its rather misleading behavior that we favor using && and ||.


Then Keyword (Style/MultilineIfThen)

There is a keyword then, which is accepted by Rubocop for one-line if-elsif blocks.

a = rand(10)

if a < 3 then "Low"
elsif a < 6 then "Average"
elsif a < 8 then "High"
end
Enter fullscreen mode Exit fullscreen mode

It is an interesting feature! It somewhat resembles a case block but allows chaining different conditions easily.

Rubocop's rule recognizes that the following syntax is bad practice:

a = rand(10)

if a < 3 then
  "Low"
elsif a < 6 then
  "Average"
elsif a < 8 then
  "High"
end
Enter fullscreen mode Exit fullscreen mode

Which makes sense since then is completely unnecessary in this case.


BEGIN and END blocks (Style/BeginBlock)

This was also a real discovery. By using BEGIN, we can execute code when our Ruby script starts. END does the same, but when our script finishes:

# test.rb
BEGIN { pp 'Hello World!'}
END { pp 'Goodbye World!'}

pp "Running my script..."
---

$ ruby test.rb
"Hello World!"
"Running a script"
"Goodbye World!"
Enter fullscreen mode Exit fullscreen mode

I admit that in my daily life, I don't really think about using this syntax. I imagine it can be useful when designing CLI applications. To prepare and clean up a program.


?c → 'c' (Style/CharacterLiteral)

This one is very short. When you type ? followed by an ASCII character, it returns the ASCII character as a string:

$ irb
001> ?a
=> "a"
002> ?\t
=> "\t"
Enter fullscreen mode Exit fullscreen mode

What's the point? I don't really see either.

In fact, this feature was implemented to allow obtaining the ASCII code of the character. Before Ruby 1.9, you could do:

001> ?a
=> 97
002> ?\t
=> 9
Enter fullscreen mode Exit fullscreen mode

But since Ruby 1.9, it only returns the character. The ASCII code can still be found via the .ord method.

A good anecdote to tell at meetups!


There are a lot of aliases for collection methods (Style/CollectionMethods)

We are used to using .map, .select, or .include? when manipulating an Enumerable. But did you know there is an alias for these methods?

Here is the list of methods with an alias that should be used (according to Rubocop):

.collect => .map
.collect! => .map!
.collect_concat => .flat_map
.inject => .reduce
.detect => .find
.find_all => .select
.member? => .include?
.lenght => .size
Enter fullscreen mode Exit fullscreen mode

This Cop exists for one reason only: to ensure the use of a single method name throughout the project. In reality, I understand that it is impractical to have a project containing both .collect and .map. It can require a mental effort to remember that they are the same thing.

We have another justification for this choice in the RubyStyle documentation:

“The rhyming methods are inherited from Smalltalk and are not common in other programming languages. The reason the use of select is encouraged over find_all is that it goes together nicely with reject and its name is pretty self-explanatory.”


% behaves like sprintf (Style/FormatString)

I discovered that we can use % like sprintf:

001> sprintf("%5d", 10)
=> "   10"
002> "%5d" % 01
=> "   10"
Enter fullscreen mode Exit fullscreen mode

Basically, it's a shortcut but does exactly the same thing as sprintf.

We can pass arguments to it in the form of an Array if using multiple arguments during formatting:

001>  "Name: %s, Age: %d" % ["V", 23]
=> "Name: V, Age: 23"
002> "Name: %{name}, Age: %{age}" % { name: "V", age: 23 }
=> "Name: V, Age: 23"
Enter fullscreen mode Exit fullscreen mode

Rubocop's rule is also there to ensure the use of a single method between sprintf, format, and %.


%w[] is not only a story of

In Ruby, we are fortunate to have %w[Hello World] notation to create an array of strings. But did you know it is possible to use almost any non-alphanumeric character as a delimiter?

%w{Hello World}
%w/Hello World/
%w@Hello World@
%w-Hello World-
%w*Hello World*
%w&Hello World&
# <> behaves like [] or ()
%w<Hello World>
...
Enter fullscreen mode Exit fullscreen mode

So why do we use []? The answer is given in the RubyStyle doc:

We most often use [] because it aligns with the array syntax [0, 1, 2]. It's as simple as that!

But there are a few small exceptions like %q which uses only parentheses (). But also regexes that use %r{} because parentheses () and square brackets [] are frequently used in regexes.


FlipFlop Operator (Lint/FlipFlop)

This is a rather interesting find! The FlipFlop Operator. Let's see an example right away, and I'll explain the details:

# test.rb
text = <<~EOF
  random line 1
  start
  interesting line 1
  interesting line 2
  end
  random line 2
EOF

text.each_line do |line|
  puts line if (line.chomp == 'start'..line.chomp == 'end')
end
---

$ ruby test.rb
start
interesting line 1
interesting line 2
end
Enter fullscreen mode Exit fullscreen mode

The FlipFlop operator works in two phases, "flip" and "flop".

The operator "flips" (becomes true) when the first condition is true, and "flops" (becomes false again) when the second condition is true. Between these two points, it remains true, even if the first condition becomes false.

Honestly, I quite like this syntax.

Rubocop advises against using the FlipFlop operator in Ruby because it complicates code readability, making its behavior opaque to those unfamiliar with the operator. It encourages the use of clearer and more explicit alternatives such as loops and conditions to improve code maintainability.


There are a lot of Perl-style global variables (Style/SpecificGlobalVars)

I'm not going to list them all, but Ruby exposes many so-called “Perl-style” variables. You can find a complete list here.

Basically, these are variables that start with $ and can be found in two forms: the short form, or the long form in English.

Here's a small example of variables I liked:

# Print file name
puts $0
puts $PROGAM_NAME

# Print required files
puts $:
puts $LOAD_PATH

# Print error backtrace and error information
begin
  File.read('non_existant_file.txt')
rescue
  puts $ERROR_POSITION
  puts $@

  puts $ERROR_INFO
  puts $!
end
Enter fullscreen mode Exit fullscreen mode

There are many others, most of them having their equivalent in short/long versions.


Conclusion

I am delighted to see the diversity and richness of Ruby's features we explored today, even if Rubocop sometimes advises us to avoid them.

This variety demonstrates the flexibility and power of the language, offering us multiple ways to write and structure our code. Discovering these less common aspects can inspire us to experiment further and deepen our understanding of Ruby.

I would be very curious to know what the latest Rubocop Cop that taught you about our favorite language.

Feel free to subscribe so you don't miss my next breakdown on Ruby/Rails topic.

Top comments (0)