Why Doesn't Module.Method_Defined(:Method) Work Correctly

Why doesn't Module.method_defined?(:method) work correctly?

To know whether the module has a module method, you can use respond_to?
on the
module:

Something.respond_to?(another)
=> true

method_defined? will tell you whether INSTANCES of the class with the module included responds to the given method.

Namespacing module method definition

:: is the scope resolution operator. So self::connect resolves connect from self. Which means that its equivalent to self.connect. You can see how it works from this very contrived example:

class Foo; end

class Bar
def Foo::baz
"Hello World"
end
end

puts Foo.baz # "Hello World"

Of course we could also just use def Foo.baz to get the exact same result.

Using double colons for method definition is discouraged by the Ruby Style guide:

Do not use :: to define class methods.

# bad
class Foo
def self::some_method
end
end

# good
class Foo
def self.some_method
end
end

Its also not recommended for anything other than referencing constants and constructors:

Use :: only to reference constants (this includes classes and modules)
and constructors (like Array() or Nokogiri::HTML()). Do not use :: for
regular method invocation.

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

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.

Can I invoke an instance method on a Ruby module without including it?

If a method on a module is turned into a module function you can simply call it off of Mods as if it had been declared as

module Mods
def self.foo
puts "Mods.foo(self)"
end
end

The module_function approach below will avoid breaking any classes which include all of Mods.

module Mods
def foo
puts "Mods.foo"
end
end

class Includer
include Mods
end

Includer.new.foo

Mods.module_eval do
module_function(:foo)
public :foo
end

Includer.new.foo # this would break without public :foo above

class Thing
def bar
Mods.foo
end
end

Thing.new.bar

However, I'm curious why a set of unrelated functions are all contained within the same module in the first place?

Edited to show that includes still work if public :foo is called after module_function :foo

Access module method directly without operator ('.' or '::')

Try running it like this

module Example
def do_something
puts 'foo!'
end
end

include Example
do_something #=> foo!

Refer to a method with varying name

You can use the public_send method to call an arbitrary method on an object. For example, foo.public_send(:bar) is equivalent to foo.bar, and works exactly the same way.

Knowing this you can define an array of symbols, where each symbol is a method you want to call. So:

[:bar, :baz, :qorg].each {|method|
DailyQueries.public_send(method)
end

will work the same as:

DailyQueries.bar
DailyQueries.baz
DailyQueries.qorg

Here's some reading material if you'd like to learn more about the public_send method, and its less privacy-respecting cousin, the send method:

  • What is the difference between ruby send and ruby public_send method?
  • What does send() do in Ruby?
  • Object#public_send on RubyDoc

Changing ruby method context / calling a method with instance_exec

I think this is simpler than you might expect (and I realize that my answer is 2 years after you asked)

You can use instance methods from modules and bind them to any object.

module Providers
def facebook
@facebook ||= FacebookProvider
end

module FacebookProvider
def greet
"#{message} from facebook!"
end
end
end

class MyClass
include Providers
attr_accessor :message

def initialize(message="hello")
self.message = message
end

def greet(provider=nil)
if provider
provider.instance_method(:greet).bind(self).call
else
message
end
end
end

If your provider is a module, you can user instance_method to create an UnboundMethod and bind it to the current self.

This is delegation.
It's the basis for the casting gem which would work like this:

delegate(:greet, provider)

Or, if you opt-in to using method_missing from casting, your code could just look like this:

greet

But you'd need to set your delegate first:

class MyClass
include Providers
include Casting::Client
delegate_missing_methods
attr_accessor :message

def initialize(message="hello", provider=facebook)
cast_as(provider)
self.message = message
end
end

MyClass.new.greet # => "hello from facebook!"

I wrote about what delegation is and is not on my blog which is relevant to understanding DCI and what I wrote about in Clean Ruby

Ruby: Use module method inside a class method

By including the module, you make module_method is an instance method on TestClass, meaning you need to invoke it on an instance of the class, not the class itself.

If you want to make it a method on the class itself, you need to extend TestModule, not include it.

module TestModule
def module_method
"module"
end
end

class TestClass
extend TestModule # extend, not include

def self.testSelfMethod
str = module_method
puts str
end
TestClass.testSelfMethod # "method"
end

(In Ruby) allowing mixed-in class methods access to class constants

This seems to work:

#! /usr/bin/env ruby

module CommonMethods
def shout_my_constant
puts self::Const.upcase
end
end

class NonInstantiableClass
Const = "hello, world!"
class << self
include CommonMethods
end
end

NonInstantiableClass.shout_my_constant

HTH



Related Topics



Leave a reply



Submit