Defining a public method in a Ruby module? - ruby

I must be making a n00b mistake here. I've written the following Ruby code:
module Foo
def bar(number)
return number.to_s()
end
end
puts Foo.bar(1)
test.rb:6:in <main>': undefined methodbar' for Foo:Module (NoMethodError)
I wish to define a single method on a module called Foo.bar. However, when I try to run the code, I get an undefined method error. What am I doing wrong?

You could do with:
module Foo
def self.bar(number)
number.to_s
end
end
puts Foo.bar(1)

Every module in Ruby can be mixed in an object. Once a class is an object, you could mix the methods in a class using the word extend:
module Foo
def bar
'bar'
end
end
class MyInstanceMethods
include Foo
end
class MyClassMethods
extend Foo
end
## Usage:
MyInstanceMethods.new.bar
=> "bar"
MyClassMethods.bar
=> "bar"
If you wish calling the bar method directly from the Foo module, you could do in the same way #xdazz wrote, but since the extend word mixes to a Class:
MyInstanceMethods.class
=> Class
MyClassMethods.class
=> Class
Module.class
=> Class # Hey, module is also a class!!!!!
The trick:
module Foo
extend self # self of Foo is the Module!
def bar
# .....
end
end
Now you can see Foo.bar returning the expected result :P

Related

Creating class level methods in a module in Ruby

Why doesn't this work?
module Greeter
def self.greet
puts "anyang"
end
end
Greeter.greet # => anyang
class KoreanKid
include Greeter
greet
end
# !> NameError : undefined local variable or method `greet' for KoreanKid:Class
KoreanKid.greet
# !> NoMethodError : undefined method `greet' for KoreanKid:Class
When I call greet right inside the KoreanKid class, that's just simply calling a class method right? That's the same thing as KoreanKid.greet right? Why doesn't the above work?
In my module, I'll have a mix of class methods and instance methods... how do I get both to work cleanly?
Kernel#include adds the existing methods in the module as instance methods of the class. To add class methods, you have to use Kernel#extend:
module Foo
def bar
42
end
end
class Baz
extend Foo
end
Baz.bar # => 42
Note that the methods that we extended were instance methods in the original module.
A popular way to do both is to use the Module#included hook to also extend:
module Foo
def bar
:wut
end
def self.included(target)
target.extend ClassMethods
end
module ClassMethods
def baz
:indeed
end
end
end
class Test
include Foo
end
Test.new.bar # => :wut
Test.baz # => :indeed

Ruby - accessing instance methods / variables from anonymous class

I have following Ruby code:
class Baz
def foo()
qux = Class.new() {
def call()
bar()
end
}.new()
qux.call()
end
def bar()
puts "bar"
end
end
b = Baz.new()
b.foo()
How can I access method bar from the anonymous class, that means from qux.call? Is it possible?
I'm keeping getting this message:
classes-test.rb:5:in `call': undefined method `bar' for #<#<Class:0x00000002d9c248>:0x00000002d9c1a8> (NoMethodError)
I'm new to Ruby, so any advice or even deeper explanation of the problem will be appreciated. Thanks in advance.
Since .bar is an instance method of Baz, you need to have an instance of Baz available in your context to call .bar. You can do that by passing the instance object to the class on initialization, so you can call its .bar method on it.
This works:
class Baz
def foo
qux = Class.new do
def initialize(a)
#a = a
end
def call
#a.bar
end
end.new(self)
qux.call
end
def bar
puts "bar"
end
end
b = Baz.new
b.foo
=> 'bar'
If you need to pass a class to Class.new() as you mention in the comments, you can override the initializer method like this (please note that you may have to consider the arguments that your Closure class initialize needs for super:
qux = Class.new(Fiddle::Closure) do
def initialize(baz)
#baz = baz
super
end
def call
#baz.bar
end
end.new(self)
On a side note, you don't need all those (), it's Ruby style to omit them if not needed.

Using helper methods in Ruby modules

I'm having some trouble understanding how to incorporate my own helper methods into a Ruby module.
My code:
module MyModule
def self.foo
bar
end
def bar
# helper for MyModule.foo
end
end
MyModule.foo
#=> NameError: undefined local variable or method `bar' for MyModule:Module
I'm not sure why MyModule cannot recognize the bar method. What aspect of scope in Ruby am I being oblivious to?
Modules can be integrated into classes as mixins. So, you need to include it in a class so it can be used with instance of that class.
As of now, you can make bar as your module method so it can be accessed as is.
module MyModule
def self.foo
bar
end
def self.bar
puts "Now it works"
end
end
MyModule.foo #=> Now it works
Ruby Docs
A Module is a collection of methods and constants. The methods in a
module may be instance methods or module methods. Instance methods
appear as methods in a class when the module is included, module
methods do not.
You are trying to call an instance method from a class method. You would have to write
module MyModule
def MyModule.foo
MyModule.bar
end
# Or you can have it this way
def MyModule::bar
# helper for MyModule.foo
end
end
MyModule.foo
to get what you want.
You're missing the scope of a method (module as well as instance) and basically it's lifecycle..
Module method way: -
Following is how you define the module with module methods.
module MyModule
def self.foo
puts "called self.foo"
bar
end
def self.bar
puts "self.bar got called"
# helper for MyModule.foo
end
end
Now, This way, you do not have to instantiate any object to call those methods.. Here is how you would call the methods (one inside the other)
MyModule.foo
Using a class to instantiate the Module and calling methods will not work as they are not instance methods.
Output -
called foo
bar got called
Instance method way: - Following is how you'll define the module with instance methods so that they will work between classes and objects..
module MyModule
def foo
puts "called foo"
bar
end
def bar
puts "bar got called"
# helper for MyModule.foo
end
end
class TestModule
include MyModule
end
Choosing to use the module methods this way you have to call the methods inside module as per below -
#instantiating module MyModule via class
myinstance = TestModule.new
myinstance.foo
Output -
called foo
bar got called

Accessing module methods from a class in that module?

Under Ruby 2.0, what is the correct way to access a module method from a class in that module?
For instance, if I have
module Foo
class Foo
def do_something
Foo::module_method
end
end
def self.module_method
puts 'How do I call this?'
end
end
I get,
./so-module.rb:7:in do_something': undefined methodmodule_method'
for Foo::Foo:Class (NoMethodError) from ./so-module.rb:16:in `'
What is the correct way to define the module method so I can access it from class Foo?
You have to define the method on the module’s singleton class:
module Foo
class Bar
def do_something
Foo.module_method
end
end
def self.module_method
'Success!'
end
end
Foo::Bar.new.do_something #=> "Success!"
Please take a look at following code:
module Foo
class Bar
include Foo
def do_something
module_method
end
end
def module_method
puts 'How do I call this?'
end
end
b = Foo::Bar.new
b.do_something
result:
How do I call this?
You can even try adding following code:
b1 = Foo::Bar::Bar.new
b1.do_something
It's tricky and has same result:
How do I call this?

Remove/undef a class method

You can dynamically define a class method for a class like so:
class Foo
end
bar = %q{def bar() "bar!" end}
Foo.instance_eval(bar)
But how do you do the opposite: remove/undefine a class method? I suspect Module's remove_method and undef_method methods might be able to be used for this purpose, but all of the examples I've seen after Googling for hours have been for removing/undefining instance methods, not class methods. Or perhaps there's a syntax you can pass to instance_eval to do this as well.
Thanks in advance.
class Foo
def self.bar
puts "bar"
end
end
Foo.bar # => bar
class <<Foo
undef_method :bar
end
# or
class Foo
singleton_class.undef_method :bar
end
Foo.bar # => undefined method `bar' for Foo:Class (NoMethodError)
When you define a class method like Foo.bar, Ruby puts it Foo's singleton class. Ruby can't put it in Foo, because then it would be an instance method. Ruby creates Foo's singleton class, sets the superclass of the singleton class to Foo's superclass, and then sets Foo's superclass to the singleton class:
Foo -------------> Foo(singleton class) -------------> Object
super def bar super
There are a few ways to access the singleton class:
class <<Foo,
Foo.singleton_class,
class Foo; class << self which is commonly use to define class methods.
Note that we used undef_method, we could have used remove_method. The former prevents any call to the method, and the latter only removes the current method, having a fallback to the super method if existing. See Module#undef_method for more information.
This also works for me (not sure if there are differences between undef and remove_method):
class Foo
end
Foo.instance_eval do
def color
"green"
end
end
Foo.color # => "green"
Foo.instance_eval { undef :color }
Foo.color # => NoMethodError: undefined method `color' for Foo:Class
You can remove a method in two easy ways. The drastic
Module#undef_method( )
removes all methods, including the inherited ones. The kinder
Module#remove_method( )
removes the method from the receiver, but it
leaves inherited methods alone.
See below 2 simple example -
Example 1 using undef_method
class A
def x
puts "x from A class"
end
end
class B < A
def x
puts "x from B Class"
end
undef_method :x
end
obj = B.new
obj.x
result -
main.rb:15:in
': undefined methodx' for # (NoMethodError)
Example 2 using remove_method
class A
def x
puts "x from A class"
end
end
class B < A
def x
puts "x from B Class"
end
remove_method :x
end
obj = B.new
obj.x
Result -
$ruby main.rb
x from A class
I guess I can't comment on Adrian's answer because I don't have enough cred, but his answer helped me.
What I found: undef seems to completely remove the method from existence, while remove_method removes it from that class, but it will still be defined on superclasses or other modules that have been extened on this class, etc.
If you would like to remove method with name what calculate dinamically, you should use eigenclasses like:
class Foo
def self.bar
puts "bar"
end
end
name_of_method_to_remove = :bar
eigenclass = class << Foo; self; end
eigenclass.class_eval do
remove_method name_of_method_to_remove
end
this way is better than others answers, becouse here i used class_eval with block. As you now block see current namespace, so you could use your variables to remove methods dinamically
Object.send(:remove_const, :Foo)

Resources