Substitute for === (case equality operator) in ruby

Viewed 1214

I have a rubocop problem while doing my own ruby enumerables. I used a === and rubocop is asking me to change it. But every time I try to put something different my method stops working as desired.

module Enumerable
  def my_all?(arg = nil)
    return true if !block_given? && include?(nil) == false && include?(false) == false && arg.nil?
    return false unless block_given? || arg.nil? == false || empty?
    
    if block_given?
      my_each do |x|
        return false unless yield(x)
      end
    elsif arg.class == Regexp
      my_each do |x|
        return false unless x.match(arg)
      end
    elsif arg.class == Numeric
      my_each do |x|
        return false unless x.is_a? arg
      end
    else
      my_each do |x|
        return false unless arg === x
      end
    end
    true
  end
end

That last === is offering resistance to be changed. Thanks to the ones that are able to understand this and help!

2 Answers

The docs for Enumerable#all? specifically say that when a pattern is given:

[...] the method returns whether pattern === element for every collection member.

So in order to replicate the method you actually have to call ===. Trying to substitute it just to please Rubocop would likely result in a different behavior.

In your case, I'd disable the cop using an inline comment:

my_each do |x|
  return false unless arg === x # rubocop:disable Style/CaseEquality
end

The == method means is equivalent to, but === is a special purpose one that sometimes means that and more.

For Class the === method means is inherited from, not equivalent to, or in other words the two are the same:

  x === y
  x.is_a?(y)

Where you'll see how this plays out:

  String == String
  # => true
  String === String
  # => false

As String is a class, it is not a subclass of itself.

That being said, here's a Ruby idiomatic refactoring of your code:

module Enumerable
  def my_all?(arg = nil)
    return true if !block_given? && !include?(nil) && !include?(false) && arg.nil?
    return false unless block_given? || !arg.nil? || empty?
    
    if block_given?
      my_each do |x|
        return false unless yield(x)
      end
    else
      case arg
      when Regexp
        my_each do |x|
          return false unless x.match(arg)
        end
      when Numeric
        my_each do |x|
          return false unless x.is_a? arg
        end
      else
        my_each do |x|
          return false unless arg === x
        end
      end
    end

    true
  end
end

Where case internally uses the === method for comparisons so you can easily break out behaviours based on types.

Related