How can I call a class method on its instance? - ruby

a1 is a method on A.
module A
def self.a1
puts "I am defined in A"
end
end
class Sample
include A
end
samp = Sample.new
How can I call samp.a1?
puts samp.a1

There's a couple of things to keep in mind here:
If you declare module methods with the self prefix that means they're able to be directly called on the module, like A.a1, but it also means they won't be imported with include. Those declared without self are "mix-in methods", or those that will automatically be imported on include.
If you include more than one module which defines a method then the last module included will be the one that actually gets called.
The fix here is either to change def self.a1 to def a1 in module A or instead call it directly with A.a1. If B also defines a1 then it will have precedence unless you include B first, making it lower priority.

If you know this sort of thing will happen ahead of time, you can use alias as you're including modules (note this changes the method definitions from def self.a1 to def a1 so that they are properly included as instance methods):
module A
def a1
puts "I am defined in A"
end
end
module B
def a1
puts "I am defined in B"
end
end
class Sample
include A
alias :a_a1 :a1
include B
alias :b_a1 :a1
end
samp = Sample.new
samp.a_a1
samp.b_a1
samp.a1
running this outputs:
I am defined in A
I am defined in B
I am defined in B
As a general rule, though, I would avoid this situation and rename one of the methods in the modules, if you're able to; though sometimes you're not able to for one reason or another.

If it's just for a1, then this should work:
class Sample
include A
def a1
self.class.a1
end
end
If you need it for all methods, you can try Catching the NoMethodError with a send-command for the class, but that is usually overkill.

Related

How to overwrite eigenclass method in ruby?

I have a module
module A
class << self
def is_okay?; false; end
end
end
and I need to overwrite is_okay? method in another module. Module B is included into A in this way
A.send(:include, B)
I have tried this
module B
class << self
def is_okay?; true; end
end
end
and that
module B
def self.is_okay?; true; end
end
but it didn't work. How can I achieve this?
This may or may not work in your situation:
module B
def is_okay?
true
end
end
module A
class << self
prepend B
def is_okay?
false
end
end
end
prepend is similar to include, but inserts itself before the class, at the "bottom" of the ancestor chain.
EDIT:
Since you clarified in your comments below (I would suggest clarifying your original question), you can alias the same as any other method.
module A
class << self
alias original_is_okay? is_okay?
def is_okay?
true
end
end
end
This will allow for "overwriting it, whether or not you have access to it.
Consider the following.
module B
def bi
"hello from bi"
end
def self.bm
"hello from bm"
end
end
B.instance_methods(false)
#=> [:bi]
B.methods(false)
#=> [:bm]
Note that defining a module method (here bm) with self. is the same as defining an instance method on the module's singleton class.
Now create a module A that includes B.
module A
def self.am
"hello from am"
end
end
A.methods(false)
#=> [:am]
A.include B
A.instance_methods.include?(:bi)
#=> true
A.methods.include?(:bm)
#=> false
As expected, bi is now an instance method of A. include, however, disregards module methods, here B::bm. Is there any way for the module method B::m to become a module method of A? The answer is "no". In effect, we want
A.singleton_class.include B.singleton_class
but that doesn't work because B.singleton_class is a class.
Module#include does not make it clear whether a module (that is possibly a class) can include a class. Try it, however, and you will see the following an exception is raised:
TypeError (wrong argument type Class (expected Module))
If module methods of a module M are not made available to another module that includes M, is there any reason for modules to have module methods? Yes, to provide libraries of methods! An example is the module Math. That module contains many module methods and no instance methods. When used, those methods are therefore invoked on their receiver, Math. For example,
Math.sin(x)

Why is Ruby include polluting unrelated classes?

I am really confused why the methods in modules B and C are mixed in to the class in module A when I use include C. Does include somehow recursively mix in with all classes below the namespace where it is called?
module A
class AClass
def a_method_
end
end
end
module B
extend self
def b_method_
end
end
module C
extend self
include B
def c_method_
puts A::AClass.new.methods.grep /_method_/
end
end
C.c_method_
puts "----"
include C
c_method_
With the result that before the include, the AClass instance correctly only has method a_method_, but after the include it has access to the other two methods as well.
a_method_
----
a_method_
c_method_
b_method_
Can someone please help explain this?
If you change the tail end of your code to this:
C.c_method_
puts self.inspect
puts "----"
include C
c_method_
you should help you see what's going on. That will give you this output:
a_method_
main
----
a_method_
c_method_
b_method_
So what is this main business? Chuck has a nice summary over in this answer:
Everything in Ruby occurs in the context of some object. The object at the top level is called "main". It's basically an instance of Object with the special property that any methods defined there are added as instance methods of Object (so they're available everywhere).
That means that saying this at the top level:
include C
is the same as saying:
Object.send(:include, C)
Adding things to Object pollutes everything.
You might find this illuminating:
puts Object.instance_methods.grep /_method_/
# c_method_
# b_method_
Your methods have been added to everything, not just AClass! And to really demonstrate the weirdness, try changing that last bit to:
class D
include C
puts Object.instance_methods.grep /_method_/
end
No more output! You've stumbled upon one of the weird, intentional behaviors of Ruby's main (the implicit receiver when nothing else is specified). The basic motivation is that if you define a method in main, you want to be able to call it anywhere:
def foo
end
# now all of these work:
class A
foo
end
module B
foo
end
A.new.instance_eval { foo }
A.singleton_class.class_eval { foo }
This isn't about scope resolution either; if you change that to foo = :bar you'll get NameErrors. This is for stuff like puts and gets where you generally don't care who the receiver is when you call them. But this behavior doesn't jive at all with Ruby's object model, so matz hacked it in: every method defined in main gets added to Object as an instance method. Since everything is an object, the rest works as expected.

Refer to a class without explicitly mentioning its namespace

I have a class within several modules: This::Is::A::Long::ClassName. Is there any way, within one script or method, to make ClassName available without having to reference the namespace? Instead of writing:
This::Is::A::Long::ClassName.do_something
This::Is::A::Long::ClassName.do_something_else
This::Is::A::Long::ClassName.do_something_different
is anything as below possible?
include This::Is::A::Long
ClassName.do_something
ClassName.do_something_else
ClassName.do_something_different
If you are using modules for namespacing, the code you posted should work, see this example:
module Long
module Name
class ClassName
end
end
end
ClassName
# => ... uninitialized constant ClassName (NameError)
include Long::Name
ClassName
# => Long::Name::ClassName
Ruby has no equivalent to C++ using namespace, and you can not reference a class without being in the right namespace, but you can always make it a variable since a class is also an object
long_class = This::Is::A::Long::ClassName
long_class.do_something
long_class.do_something_else
# and so on
EDIT
An include does not put you in the right namespace, it includes the methods & classes in the module you are including (that is, it puts the module in the classes ancestors) and is therefore most certainly not suitable for your needs: Consider the following:
module This
module Is
module A
def foo
puts 'A#foo'
end
def bar
puts 'A#bar'
end
class ClassName
end
end
end
end
Now, you may not want to write This::Is::A::ClassName in another class, let's say:
class C
def foo
puts 'C#foo'
end
end
class B < C
include This::Is::A
end
Now, B.new.foo still puts out C#foo, right? Wrong. Since you included the module, the method has been overwritten.

Why function in same module is not accessable in ruby?

Let's say we have code looks like this
module TM
def aa
puts "aa"
end
def bb
TM::aa
end
module_function :bb
end
TM.bb
Why bb cannot access aa by no matter "TM::aa", "self.aa" or "TM.aa"? They are in the same module. What is the benefit of setting such restriction and how do I solve the "undefined method" error?
Did you mean something like this?
module TM
class << self
def aa
puts "aa"
end
def bb
TM::aa
end
end
end
TM.bb
UPDATE: methods as you defined them will be accessible as instance methods when you include your module or class methods if you extend your class
module TM
def aa
puts "aa"
end
def bb
aa
end
end
class A
include TM
end
class B
extend TM
end
A.new.bb
B.bb
Because aa is a instance function. If you add it to the module_function list (or use one of the many other methods of declaring module methods), everything works. Personally, I prefer:
module TM
def self.aa
puts "aa"
end
def self.bb
TM::aa
end
end
TM.bb
This does not work because you are defining an instance method (def aa) instead of a module method (def self.aa).
Check out the documentation:
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. Conversely, module methods may be called without creating an encapsulating object, while instance methods may not. (See Module#module_function)
So what you want might be:
module TM
def self.aa
puts "aa"
end
def bb
TM::aa
end
module_function :bb
end
TM.bb
I think you may be confused about the functionality of the call to module_function, as this method actually creates a copy of the method and makes the original method private, and mixes the copy into the meta-class (or eigenclass, in any case one step up on the method lookup chain). This is so it can be overridden without effecting internal use of the method, i.e. to make it safe for private use while also being publicly usable OR publicly overrideable. It's only the copy that's mixed into the meta-class that can't access aa, and that's because aa doesn't exist above it in the lookup chain. If both methods were passed into module function then you won't get the undefined method error.

How can a nested class access a method in the outer class in Ruby?

def class A
def a
raise "hi" #can't be reached
end
class B
def b
a() #doesn't find method a.
end
end
end
I want to invoke a from b and raise the exception. How can I?
Ruby doesn't have nested classes.
The only way to inherit behavior is, well, via inheritance.
If you want your code to work, you need to use a language which supports nested classes. While this is an incredibly neat and powerful feature, I unfortunately know of only two languages that have nested classes:
BETA, the language which introduced nested classes (and its successor gbeta)
Newspeak
I don't know of any other.
Java has a construct called nested classes, but they have some unfortunate design limitations.
In your example above, it's not the class B that is nested inside A, it is the constant B that is nested inside A. Think about this:
C = A::B
Now, the class is available under two names: A::B and C. It should be immediately obvious that C is global and not nested inside A. (Well, actually, C is nested inside Object, because there aren't really global constants either, but that's beside the point.) But since C and A::B are the same class, it obviously cannot be both nested and not nested. The only logical conclusion is that the class itself isn't nested.
The defining feature of nested classes is that method lookup goes along two dimensions: up the inheritance chain, and outwards through the nesting. Ruby, like 99.9% of all OO languages, only supports the former. (In some sense, nested classes inherit not only the features of their superclass, but also the features of their surrounding class.)
This is just for the lulz:
class A
def a
puts "hello from a"
end
class B
def b
Module.nesting[1].new.a()
end
end
end
I typically do something like this:
class A
def a
puts "hi"
end
def createB
B.new self
end
class B
def initialize(parent)
#parent=parent
end
def b
#parent.a
end
end
end
A.new.createB.b
If you want then nested class to extend the outer class, then do so:
class Outer
class Inner < Outer
def use_outer_method
outer_method("hi mom!")
end
end
def outer_method(foo)
puts foo
end
end
foo = Outer::Inner.new
foo.use_outer_method #<= "hi mom"
foo.outer_method("hi dad!") #<= "hi dad"
Well depending on your circumstances there is actually a solution, a pretty easy one at that. Ruby allows the catching of method calls that aren't captured by the object. So for your example you could do:
def class A
def a
raise "hi" #can't be reached
end
class B
def initialize()
#parent = A.new
end
def b
a() #does find method a.
end
def method_missing(*args)
if #parent.respond_to?(method)
#parent.send(*args)
else
super
end
end
end
end
So then if you do this:
A::B.new().b
you get:
!! #<RuntimeError: hi>
It is probably an easier way to make something like a SubController that only handles certain activities, but can easily call basic controller methods (You would want to send in the parent controller as an argument in the initializer though).
Obviously this should be used sparingly, and it can really get confusing if you use it all over the place, but it can be really great to simplify your code.
You can use methods like module_parent, module_parent_name, module_parents from ActiveSupport to get outer modules, eg.:
class A
def self.a; puts 'a!' end
class B
def self.b; module_parent.a end # use `parent` if rails < 6.0
end
end
A::B.b #=> a!
Was a supposed to be a class method for class A?
class A
def self.a
raise "hi"
end
class B
def b
A::a
end
end
end
A::B.new.b
If you want to keep it as an instance method, you'll obviously have call to it on an instance, like for example A.new.a.

Resources