Garry Pettet
Garry Pettet

Reputation: 8290

How do I check if a module responds to a class method?

Using Ruby, not Rails.

My Ruby app provides the ability to extend its functionality by loading user-defined modules. For the sake of simplicity, below is the structure of a valid module:

module MyModule
  class MyClass < AppSuperClass
    def initialize
      puts "Hello"
    end
  end

  def self.create_class
    return MyClass.new
  end
end

As you can see, the module must implement a class that inherits from AppSuperClass and must implement a module method called create_class.

If the above module is in file mymodule.rb and I include it in another file like so:

require 'mymodule'

I need to check that MyModule responds to MyModule.create_class. For some reason, MyModule.method_defined?(create_class) and MyModule.method_defined(:create_class) both return false. What am I doing wrong?

Upvotes: 1

Views: 466

Answers (2)

BernardK
BernardK

Reputation: 3734

As posted, your code does not work :

$ ruby -w m.rb 
m.rb:5: warning: mismatched indentations at 'end' with 'def' at 3
m.rb:10: warning: mismatched indentations at 'end' with 'class' at 2
m.rb:10: syntax error, unexpected end-of-input, expecting keyword_end

$ ruby -w t.rb 
t.rb:3:in `<main>': undefined local variable or method `create_class' for main:Object (NameError)

After changing MyModule.method_defined?(create_class)

to MyModule.method_defined?(:create_class) :

$ ruby -w t.rb 
false
t.rb:4:in `<main>': undefined method `method_defined' for MyModule:Module (NoMethodError)

What you probably want isrespond_to?.

File m.rb

class AppSuperClass
end

module MyModule
  class MyClass < AppSuperClass
    def initialize
      puts "Hello"
    end
  end

  def self.create_class
    return MyClass.new
  end

  def mm
  end
end

File t.rb

require_relative 'm'

class MyClass
    include MyModule
end

puts "you can send create_class to MyModule because it's a singleton (class) method"
puts "MyModule.respond_to?(:create_class) => #{MyModule.respond_to?(:create_class)}"

puts 'mm is not a singleton method of MyModule'
puts "MyModule.respond_to?(:mm) => #{MyModule.respond_to?(:mm)}"

puts 'mm is an instance method of MyClass, cannot be sent to MyClass'
puts "MyClass.respond_to?(:mm) => #{MyClass.respond_to?(:mm)}"

puts 'mm is an instance method of MyClass, can be sent to an instance of MyClass'
puts "MyClass.new.respond_to?(:mm) => #{MyClass.new.respond_to?(:mm)}"

puts "instance methods of MyModule : #{MyModule.instance_methods}"

puts "MyModule.method_defined?(:create_class) => #{MyModule.method_defined?(:create_class)}"
puts "MyModule.method_defined?(:mm) => #{MyModule.method_defined?(:mm)}"

puts "singleton methods of MyModule : #{MyModule.singleton_methods}"

module MyModule
    class << self
        puts 'we are in the singleton class / eigenclass'
        puts "method_defined?(:create_class) => #{method_defined?(:create_class)}"
    end
end

Execution :

$ ruby -w t.rb 
you can send create_class to MyModule because it's a singleton (class) method
MyModule.respond_to?(:create_class) => true
mm is not a singleton method of MyModule
MyModule.respond_to?(:mm) => false
mm is an instance method of MyClass, cannot be sent to MyClass
MyClass.respond_to?(:mm) => false
mm is an instance method of MyClass, can be sent to an instance of MyClass
MyClass.new.respond_to?(:mm) => true
instance methods of MyModule : [:mm]
MyModule.method_defined?(:create_class) => false
MyModule.method_defined?(:mm) => true
singleton methods of MyModule : [:create_class]
we are in the singleton class / eigenclass
method_defined?(:create_class) => true

Upvotes: 2

max pleaner
max pleaner

Reputation: 26758

First of all, you're missing an end for the def initialize block so your create_class method is getting defined in the MyClass body - you should fix the indentation to make these kinds of errors more obvious.

After fixing this, you can use MyModule.respond_to?(:create_class). The reason method_defined? doesn't work as expected with class methods is explained here.

Upvotes: 1

Related Questions