How to Invoke an Instance Method on a Ruby Module Without Including It

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

How to call any instance method in ruby object without instantiating it?

Despite the fact method_missing would do the job, I'd probably avoid it in this case in favor of a more explicit delegation. Simple and dirty example:

module InstanceDelegator
def delegate_to_instance(*methods)
methods.each do |method_name|
define_singleton_method method_name do |*args|
new(*args).public_send(method_name)
end
end
end
end

class Foo
extend InstanceDelegator

delegate_to_instance :bar # <- define explicitly which instance methods
# should be mirrored by the class ones

def bar
puts "bar is called"
end

def baz
puts "baz is called"
end
end

# and then

Foo.bar # => bar is called
Foo.baz # NoMethodError ...

# reopening works too
class Foo
delegate_to_instance :baz
end

Foo.baz # baz is called

Pros:

  1. you don't need to redefine method_missing (less magic -> less pain when you debug the code)
  2. you control precisely which instance methods to be wrapped with the class level "shorthand" (fewer chances to delegate something you don't want to - more robust code)
  3. (minor) no need to raise NoMethodError explicitly - you can fully rely on the core dispatching as it is...

Calling a method on a Ruby module

Simple method:

module MyModule
def self.my_method(*args)
MyModule::MyClass.my_method(*args)
end
end

Harder method:

Use metaprogramming to write a function for all cases (like attr_accessor).

How to call a method from a module of an other ruby file

Require needs the absolute path to the file unless the file is located in one of Ruby's load paths. You can view the default load paths with puts $:. It is common to do one of the following to load a file:

Add the main file's directory to the load path and then use relative paths with require:

$: << File.dirname(__FILE__)
require "my_module"

Ruby 1.8 code that only loads a single file will often contain a one-liner like:

require File.expand_path("../my_module", __FILE__)

Ruby 1.9 added require_relative:

require_relative "my_module"

In the module you will need to define the methods as class methods, or use Module#module_function:

module MyModule
def self.method1 ary
...
end

def method2
...
end
module_function :method2
end

a = [1,2,3,4]
MyModule.method1(a)

How do I access a module from an instance method in Ruby?

Usually, in a module, it's a good practice to have class-methods and instance-methods separate, like this:

module M
def bar
puts "BAR"
self.class.foo
end

module ClassMethods
def foo
puts "FOO"
end
end
end

Now, in a class, I would ideally want to include this module M in a way that I get A.foo as class method and A.new.bar as instance method. The trick for that is Module.included.

module M
def bar
puts "BAR"
self.class.foo
end

module ClassMethods
def foo
puts "FOO"
end
end

# when module is included, extend the class with ClassMethods
def self.included(base)
base.extend ClassMethods
end
end

class A
include M
end

A.singleton_methods #=> [:foo]

A.new.foo
#=> BAR
#=> FOO

With this approach, you can refer the class method with self.class and it will automagically work.

Hope it helps.

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

Can I invoke a class method from an ActiveRecord::Concern without mixing it into another class?

No, you can't because the methods defined in the block of class_methods are actually defined in the module Foo::ClassMethods (Foo is your concern). Here is the relevant source code of ActiveSupport::Concern

module ActiveSupport
# ...
module Concern
# ...
def class_methods(&class_methods_module_definition)
mod = const_defined?(:ClassMethods, false) ?
const_get(:ClassMethods) :
const_set(:ClassMethods, Module.new)

mod.module_eval(&class_methods_module_definition)
end
end
end

You can see that class_methods just creates the module ClassMethods for you if it is not defined by yourself. The methods you defined are just instance methods in that module, so you can't call it at the module level.

Later on, the module ClassMethods will be extended by the class that includes your concern. Here is the relevant source code:

module ActiveSupport
# ...
module Concern
def append_features(base)
if base.instance_variable_defined?(:@_dependencies)
# ...
else
# ...
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) # <-- Notice this line
# ...
end
end
end
end

How to access class method from the included hook of a Ruby module

There'a a method_added callback you could use:

module MyModule
def self.included(includer)
def includer.method_added(name)
puts "Method added #{name.inspect}"
end
end
end

class MyClass
include MyModule

def foo ; end
end

Output:

Method added :foo

If you want to track both, existing and future methods, you might need something like this:

module MyModule
def self.on_method(name)
puts "Method #{name.inspect}"
end

def self.included(includer)
includer.instance_methods(false).each do |name|
on_method(name)
end

def includer.method_added(name)
MyModule.on_method(name)
end
end
end

Example:

class MyClass
def foo ; end

include MyModule

def bar; end
end

# Method :foo
# Method :bar

Is there a way to use class method in a module without extend it in rails?

Use Module#module_function to make a single function to be a module function:

module M
def m1; puts "m1"; end
def m2; puts "m2"; end
module_function :m2
end

or:

module M
def m1; puts "m1"; end
module_function # from now on all functions are defined as module_functions
def m2; puts "m2"; end
end

M.m1 #⇒ NoMethodError: undefined method `m1' for M:Module
M.m2 #⇒ "m2"

ruby - how to dynamically call instance method in a module

Disclaimer: Bad practice and it makes little to no sense:

MyModule.instance_method(:Foo) # #<UnboundMethod: MyModule#Foo>
.bind(Object) # #<Method: MyModule#Foo>
.call #=> hello

You can't call an unbound method, thus you have to first bind it to some object.

References:

  • Module#instance_method

  • UnboundMethod#bind

  • Method#call


It would be much easier if you make the method singleton method:

module MyModule
def self.Foo
puts "hello"
end
end

MyModule.Foo
#=> "hello"

Another option is to use module_function (read up on the method's visibility for the including object when using this option):

module MyModule
def Foo
puts "hello"
end
module_function :Foo
end

MyModule.Foo
#=> "hello"

And yet another one is using extend self:

module MyModule
def Foo
puts "hello"
end

extend self
end

MyModule.Foo
#=> "hello"

P.S. It is not conventional to use capital letters in method names. It is very rare case in Ruby where it can be used and your case is 100% not the one :)



Related Topics



Leave a reply



Submit