Many programming languages have a powerful feature - multimethods or method overloading, but Ruby doesn’t have that feature. But in the Ruby it’s still possible to do it, but as extra code.

Ov the special gem to do multimethods.

Basic Usage

require 'ov'

class MyClass
  include Ov                       # add and use
  
  let :test, String  do |str| 
    p "string is received"
  end   

  let :test, Array do |arr|
    p "array is recevied"
  end
end

my_class = MyClass.new

my_class.test("string") #=> "string is received" 
my_class.test([])       #=> "array is recevied"

And, of course, we can create constructors:

class MyArray 
  include Ov
  attr_accessor :arr

  let :initialize do
    @arr = []
  end

  let :initialize, Fixnum do |fx|
    @arr = fx.times.map { nil }
  end 

  let :initialize, Fixnum, Any do |fx, any|
    @arr = fx.times.map { any }
  end
 
end

p MyArray.new()             #=> #<MyArray:0x9d39290 @arr=[]>
p MyArray.new(3)            #=> #<MyArray:0x9d3904c @arr=[nil, nil, nil]>
p MyArray.new(3, true)      #=> #<MyArray:0x9d38e08 @arr=[true, true, true]>

Moar

Create your own pattern-matching.

# magic 

module Matching
  def match(*args, &block)
    z = Module.new do 
      include Ov
      extend self
      def try(*args, &block)
        let :anon_method, *args, &block
      end
      def otherwise(&block)
        let :otherwise, &block
      end
      instance_eval &block
    end
    begin
      z.anon_method(*args)
    rescue NotImplementError => e 
      z.otherwise
    end  
  end
end

We create a module with the match method, that takes several *args these arguments will be passed to the block. And in this match method, we defined an anonymous module with two methods: try - it’s the same as when, otherwise - it’s else'. When the *args do not match with the types in try then call otherwise`. Example:

include Matching # I'm using it in the Main

match("String", [123]) do 
  try(String, Array) {|str, arr| p "#{str} #{arr}" }
  try(String) {|str| p "#{str}"  }
  otherwise { p "none" }
end 

# => "String [123]"

Another example: get resource:

require "net/http"

match(Net::HTTP.get_response(URI("https://httpbin.org/status/200"))) do
  try(Net::HTTPOK) {|r| p r.header }
  try(Net::HTTPMovedPermanently) {|r| p r.header }
  otherwise { p "error" }
end

Depending on the results, different blocks will be executed.

References