Ruby: If over unless

All the time.

Unless you are new to the world of ruby you're probably familiar with unless. It's the inverse of if. The code is executed when the condition is not true.

cry unless happy?

Is equivalent to

cry if !happy?

This is a preferred usage where I work as well as in the Ruby Style Guide.

I like to disagree.

if_over_unless.jpeg

My main issue with it is that I always have to negate the condition in my head. If the condition has multiple parts the cognitive load is even higher.

There is literally no benefit to it. The only argument I've heard so far in favour of unless, is that it makes the code more readable. Maybe, but the code needs to be understandable, if readability is at the expense of understandability, that is a bad thing.

Also there is a very easy way to remove the exclamation mark. Define a negated method:

def sad?
  !happy?
end

Or we can do some meta programming:

module Negator
  def negate(method_name, negated = nil)
    negated_name = negated || "not_" + method_name.to_s
    define_method(negated_name) { !send(method_name) }
  end
end

With this we can set up a negated method with either the default name being a not_ prefix to the positive method name, or a custom one.

Let's set up a simple class to test it.

class Mood
  extend Negator

  attr_reader :happy

  negate :happy, :sad

  def initialize(happy: true)
    @happy = happy
  end

  def sing
    puts 'Don\'t worry, be happy' if happy?
  end

  def cry
    puts 'cry a lot' if sad?
  end
end

We got a Mood class with a happy boolean argument and 2 public methods, singing when happy, and crying when sad using the negate method.

3.0.0 :114 > mood = Mood.new(happy: false)
 => #<Mood:0x00007fd01314deb8 @happy=false>
3.0.0 :116 > mood.cry
cry a lot

So we went from using

  def cry
    puts 'cry a lot' unless happy?
  end

to

  def cry
    puts 'cry a lot' if sad?
  end

Have a great day and don't forget to be awesome!