Include different module into same class based on use case - ruby

If there are 2 modules - module Abc and module Bcd and one class - class Efg.
Now we want to include module Abc into class Efg in once instance and need to include module Bcd into class Efg in another instance but not both modules at same time.
Is it possible to do in Ruby classes?

If I am understanding your question properly then I think you can use singleton_class to include a module only for a specific instance of a class:
inst1 = Efg.new
inst2 = Efg.new
inst3 = Efg.new
inst1.singleton_class.include Asd
inst2.singleton_class.include Bcd
Now inst1 will have the methods from Asd and inst2 will have the methods from Bcd. inst3 will have none of the methods.

Related

How to get classes interact with one another

I have classes person, dogs, cats, and fishes, and I need them to interact. More specifically, I need the Person class to buy an instance of the dog class, or name an instance of the cat class, etc.
How do I do that? My variables are class variables, they are out of scope to use in another class. I am not sure where to start.
Let's say you have person.rb:
Class Person
.......
end
and you have dog.rb:
class Dog
........
end
If you want to access to Dog class inside Person you have to include it.
That means your dog.rb would be:
require 'dog'
class Person
def my_method
dog = Dog.new()
end
end
require 'name_of_file_with_class'
This is just a pure example how to make it accessible, furthermore it depends on what exactly do you want.
Cheers.

What is best practice to extend class in Ruby?

I want to understand difference between following
module XYZ
class A
end
class B
end
end
Does class C inherit both class A and B, or I have to extend it using < ?
module YZZ
include XYZ
class C < A
end
end
What happens when I do following? Does it now include all classes, or just class A ?
module YZZ
extend XYZ
class C < A
end
end
What is the best way to extend class C from A ?
How can I extend class C using both A and B ?
How can I only include class A?
Ad 1., yes, C inherits both A and B when you use #include. #extend applies to class methods.
Ad 2. class C < A, think about it that A is merely a constant, that is assigned an object
of instance Class as value. So, of course, C becomes its subclass.
Ad 3. When you use #extend inside the 'module' statement, self is the module itself, and XYZ
basically gets included in its sigleton class.
As for your three bulleted questions in the end, I do not understand the first one. What is "extend class C from A"? Will class C < XYZ::A do? As for the second bulleted question, you can extend C "using both A and B" simply by C.extend( XYZ ). As for "how can you only include class A", there is no simple way to only include part of a module. If you plan to include class A separately, if it has its own concern to solve even without B, put it in a separate module and include that one. That's called separation of concerns. If you just want to cherry-pick, you could do:
module Something
A = XYZ::A
end
And there you have a constant Something::A pointing to the same class as XYZ::A.
This should work:
module XYZ
class A
end
class B < A
end
end
Then for including the XYZ module and extending C from A:
module ABC
include XYZ
class C < A
end
end

Why doesn't Ruby module inheritance work like class inheritance?

Suppose I have a module called Flight with both class and instance methods. I can get its methods into a class using include, extend, or both:
class Bat < Mammal
# Add Flight's class methods to Bat.
extend Flight
# Add Flight's instance methods to Bat.
include Flight
...
end
include will add Flight to Bat.ancestors, but extend will not.
My question is, why is this different for modules than for classes? When I subclass Mammal, I always get both class and instance methods at once. However, when I mix in a module, I cannot get both class and instance methods at once (unless I use the self.included hook or something like ActiveSupport::Concern).
Is there a language-design issue behind this difference?
Both Module#include and Object#extend are used to add the instance methods of a Module to an Object.
Given the module:
module Flight
def can_fly?
true
end
end
Module#include is used to add (or mix in) the instance methods of a module to the instance methods of a class or a module:
class Bat < Mammal
include Flight
end
a = Bat.new()
a.can_fly? # true
It actually affects the Object#is_a? method, so:
a.is_a? Flight # true
Module#include is a private method, so it can only be called with function notation when defining a class or another module:
class Bat < Mammal
self.include Flight # NoMethodError: private method called
end
Object#extend adds the instance methods of a module as singleton methods to the object on which it's called, so you can do this:
b = Mammal.new()
b.extend Flight
b.can_fly? # true
b.is_a? Flight # true
c = Mammal.new()
c.can_fly? # NoMethodError: undefined method
And only b will have the instance methods from Flight; other Mammal objects won't.
When calling Object#extend inside a class definition, the methods are added to the eigenclass of the class you're defining.
This is the important difference between the two methods when using them inside a class definition, because the methods are added as class methods:
class Bat < Mammal
extend Flight
end
Bat.can_fly? # true
d = Bat.new
d.can_fly? # NoMethodError: undefined method
I would like to address one part of your question:
include will add Flight to Bat.ancestors, but extend will not.
extend is not the same as include so it does something different obviously... You can think of extend being equal to an include on the class' metaclass.
Have a look at the following example:
module M
end
class A
include M
end
# then you will see M within A's ancestors as you know
A.ancestors # => [A, M, Object...]
class B
# the following is roughly the same as extend M:
class <<self
include M
end
end
# then you will see M within B's metaclass' ancestors
MetaclassOfB = class <<B; self; end
MetaclassOfB.ancestors # => [M, Class, Module...]
So, since extend is like an include on the metaclass, you see the extended modules showing up in the metaclass' ancestor chain...
"When I subclass Mammal, I always get both class and instance methods at once"
That's because Bat class as an object also inherited the instance methods from Mammal singleton class.
Including a module into a class changes the method look up chain. So actually the class doesn't inherit any instance methods.
Extending a class with a module is same as extending any object. The class simply acquires the module's instance methods as class instance methods (ie. methods on the class object itself).

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.

Multiple Inheritance in Ruby?

I thought that Ruby only allowed single inheritance besides mixin. However, when I have class Square that inherits class Thing, Thing in turn inherits Object by default.
class Thing
end
class Square < Thing
end
Doesn't this represent multiple inheritance?
I think you are taking the meaning of multiple inheritance in a wrong way. Probably what you have in mind as multiple inheritance is like this:
class A inherits class B
class B inherits class C
If so, then that is wrong. That is not what multiple inheritance is, and Ruby has no problem with that. What multiple inheritance really means is this:
class A inherits class B
class A inherits class C
And you surely cannot do this in Ruby.
No, multi inheritance means one class have more than one parent class. For example in ruby you can have that behavior with modules like:
class Thing
include MathFunctions
include Taggable
include Persistence
end
So in this example Thing class would have some methods from MathFunctions module, Taggable and Persistence, that wouldn't be possible using simple class inheritance.
Multiple inheritance - This is absolutely not possible in ruby not even with modules.
multilevel inheritance - This is what is possible even with the modules.
Why ?
Modules acts as an absolute superclasses to the class that includes them.
Ex1 :
class A
end
class B < A
end
This is a normal inheritance chain, and you can check this by ancestors.
B.ancestors => B -> A -> Object -> ..
Inheritance with Modules -
Ex2 :
module B
end
class A
include B
end
inheritance chain for above example -
B.ancestors => B -> A -> Object -> ..
Which is exactly same as a Ex1. That means all the methods in module B can override the methods in A, And it acts as a real class inheritance.
Ex3 :
module B
def name
p "this is B"
end
end
module C
def name
p "this is C"
end
end
class A
include B
include C
end
A.ancestors => A -> C -> B -> Object -> ..
if you see the last example even though two different modules are included they are in a single inheritance chain where B is a superclass of C, C is a superclass of A.
so,
A.new.name => "this is C"
if you remove the name method from module C, above code will return "this is B". which is same as inheriting a class.
so,
At any point there is only one multilevel inheritance chain in ruby, which nullifies having multiple direct parent to the class.
If class B inherits from class A,
then instances of B have the behaviors of both class A and class B
class A
end
class B < A
attr_accessor :editor
end
Ruby has single inheritance, i.e. each class has one and only one parent class.
Ruby can simulate multiple inheritance using Modules(MIXINs)
module A
def a1
end
def a2
end
end
module B
def b1
end
def b2
end
end
class Sample
include A
include B
def s1
end
end
samp=Sample.new
samp.a1
samp.a2
samp.b1
samp.b2
samp.s1
Module A consists of the methods a1 and a2. Module B consists of the methods b1 and b2. The class Sample includes both modules A and B. The class Sample can access all four methods, namely, a1, a2, b1, and b2. Therefore, you can see that the class Sample inherits from both the modules. Thus you can say the class Sample shows multiple inheritance or a mixin.

Resources