Abdo
Abdo

Reputation: 14051

Calling class methods from a module that is included by that class in Ruby

In the code below, I would like to call the class method done of the class that includes the module from inside self.hello

Explanation:

A::bonjour will call Mod::hello so will B::ciao

I would like to be able to detect the "calling class" (A or B) in Mod::hello in order to be able to call the A::done or B::done

module Mod
 def self.hello
  puts "saying hello..."
 end
end

class A
 include Mod

 def self.bonjour
  Mod::hello 
 end

 def self.done
  puts "fini"
 end
end

class B
 include Mod

 def self.ciao
  Mod::hello
 end

 def self.done
  puts "finitto"
 end
end

Upvotes: 2

Views: 7328

Answers (1)

Dave Newton
Dave Newton

Reputation: 160181

While (perhaps) not as clean as Niklas' answer, it's still easily doable, and IMO cleaner than the usage pattern shown in the OP which relies on knowing which module is mixed in.

(I prefer not having to pass an argument to mixin methods like this when other means exist.)

The output:

pry(main)> A::bonjour
saying hello...
fini
pry(main)> B::ciao
saying hello...
finitto

The guts:

module Mod
  module ClassMethods
    def hello
      puts "saying hello..."
      done
    end
  end

  def self.included(clazz)
    clazz.extend ClassMethods
  end
end

The modified class declarations, removing the explicit module reference:

class A
  include Mod

  def self.bonjour
    hello
  end

  def self.done
    puts "fini"
  end
end

class B
  include Mod

  def self.ciao
    hello
  end

  def self.done
    puts "finitto"
  end
end

You may also supply a default implementation of done:

module Mod
  module ModMethods
    def hello
      puts "saying hello..."
      done
    end

    def done
      throw "Missing implementation of 'done'"
    end
  end

  def self.included(clazz)
    clazz.extend ModMethods
  end
end

As a comment to this post points out, if the snippet in the OP is a faithful representation of the actual usecase, you might as well use extend (instead of include), leaving everything cleaner:

module Mod
  def hello
    puts "saying hello..."
    done
  end

  def done
    raise NotImplementError("Missing implementation of 'done'")
  end
end

And the classes using extend:

class A
  extend Mod

  def self.bonjour
    hello
  end

  def self.done
    puts "fini"
  end
end

Upvotes: 4

Related Questions