Is the functionality extend different from doing tap and then include - ruby

If I initialize an object like this:
a = A.new
a.extend(B)
Is that different from doing the following?
a = A.new.tap do |a|
include B
end

No, both are not equivalent.
a.extend(B) adds the methods of module B only to instance a, whereas
a = A.new.tap do |a|
include B
end
is equivalent to
include B
Inside the block of tap, the current self is main and whenever you include a module in main, the methods from included module becomes instance methods of Object class, as shown below:
class A
def class_meth
"class meth"
end
end
module B
def module_meth
"module meth"
end
end
p Object.instance_methods
#=> [:nil?, :===, :=~, :!~,...
a = A.new.tap do |a|
include B
end
p Object.instance_methods
#=> [:module_meth, :nil?, :===, :=~,...
Hence, you feel like the effect is similar to a.extend(B), but it is much more. Any new objects of any class will have methods of module B in them via the Object class.
class C
end
p C.instance_methods
#=> [:module_meth, :nil?, :===, :=~, :!~,...
p Hash.instance_methods.grep(/module_meth/)
#=> [:module_meth]
p [].module_meth
#=> "module meth"

include and extend are not the same thing. include can only add instance level methods, while extends "includes" far more:
But there are occasions when you would want to include some methods from a module as class-level methods in a class. This is where the method extend becomes useful.
This is just a high level difference, there's much more differences such as include being limited in the scenarios where it can work - you can read more here.

they are not same
extend is method if Object class
include is method if Module class
so, if you will do
object.extend(Module)
you will add methods from Module to object, but
SomeClass.include(Module)
will add methods from Module to instance of SomeClass

Related

Module methods in Object Ruby

I have troubles with understanding the global area of visibility in Ruby, so, I know that you cant use Module methods in your own class for example:
module Mod
def self.meth
“module method”
end
end
class Klass
include Mod
end
p Klass.meth
# Error
but when i knew that you can do such thing:
include Math
p sin 2
#0.909....
I was confused, because i thought you cant use module methods in any class without calling the method name. Also i had a supposition, that module Math has instance methods, like Kernel, but, unfortunately, no. Now i am doubting, that I understood such methods like extend and include correctly, so, could you please explain to me this thing and what will happen if we will change include to extend
You have encounter a weirdness of module_function: https://apidock.com/ruby/Module/module_function/
module Foo
def foo # this is (irb) 2
end
end
Foo.singleton_methods #=> []
Foo.instance_methods #=> [:foo]
Foo.instance_method(:foo).source_location #=> ["(irb)", 2]
module Foo
module_function :foo # this is (irb) 9
end
Foo.singleton_methods #=> [:foo]
Foo.singleton_method(:foo).source_location #=> ["(irb)", 2]
Foo.instance_methods #=> []
Foo.private_instance_methods #=> [:foo]
Foo.instance_method(:foo).source_location #=> ["(irb)", 2]
So, module_function takes an instance method of the module, makes it private and copies it onto a singleton class.
module_function can also be used without the method name, which works a bit like private method, modifying all the future methods added to this module.
Math is a full module_function module, meaning that the methods are defined both as singletons and private instance methods, which is why you can use it both ways.

Why module included outside the class adds instance methods to class's objects

I am experimenting with the Ruby include keyword as shown below:
module A
def show
puts "working"
end
end
include A
class E
end
class D
end
e = E.new
d = D.new
e.show
d.show
o = Object.new
puts o.respond_to?("show")
******************************output****************
working
working
true
I was expecting output to be undefined method but it's giving me the proper output. I have also observed that the show method defined in module A is becoming an instance method of Object.
Why are these methods becoming instance methods of the class Object?
Please help in understanding this concept.
Because instances of class Class inherit from Object.
Thus, modules, included into Object are available to instances of Class's instances (instances of your E and D classes).
class A
end
module B
def test; :hi end
end
#=> test
include B
#=> Object
A.new.test
#=> :hi
Object.new.test
#=> :hi
Having include B written in the top-level means include'ing B into Object.
include B is the outermost context is equivalent to:
class Object
include B
end
The only class, whose instances do not share module B's methods is BasicObject.

Using extend self in module

Before voting for closing due to question duplication I want to say that my question is really simple one (not asked in above mentioned questions).
There are two modules, one defines module method using extend self, another defines mixin method.
module A
extend self
def module_a_meth
"Called module_a_meth"
end
end
module B
def module_b_meth
"Called module_b_meth"
end
end
There is a class, where I both include and extend these modules:
class Test
include A
extend A
include B
extend B
end
When we includeing module, its methods become class' instance methods, when extending - class methods.
Question:
it doesn't matter for class, if methods in module defined as module methods or mixin methods, right? I mean, when included - EVERY method (either module methods or mixin methods) become instance methods, and when extended - either become class methods.
If I'm wrong - where is the difference?
obj = Test.new
puts obj.module_a_meth
puts obj.module_b_meth
puts Test.module_a_meth
puts Test.module_b_meth
#=> Called module_a_meth
#=> Called module_b_meth
#=> Called module_a_meth
#=> Called module_b_meth
EDIT
Please start your answer with Yes or No, since my question implies this type of answer :).
Regardless of whether you are using extend or include you are always copying over instance methods. The difference is where those instance methods live.
When you call Class#include you are "copying" all of the instance methods in the module to be instance methods in the class. It's similar to how inheritance work, and if you call Class#ancestors you'll see the module there.
When you call Object#extend you are copying all of the instance methods of the module to the object's singleton class. This is a class reserved just for this object instance that is created at runtime. This is how you get "class methods" (e.g. MyClass.hello_world); by adding them to the class's singleton. You can also do things like extend a particular object instance (e.g. s = String.new; s.extend(SomeModule); s.hello_world)
There are some other differences too. The context binding is different depending on whether you use extend or include. extend doesn't cause the module to show up in the ancestor chain while include does.
When trying to add both "class" and instance methods, one common pattern you'll see is doing things like this which uses the included callback to extend the base class with a ClassMethods module:
module MyModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def hello_world
end
end
end
ActiveSupport::Concerns also abstracts this pattern allowing you to add both instance and "class" methods in one call.
I personally prefer having modules only work with instance methods and using singleton methods (e.g. def self.my_method) to have scoped methods (sort of like how you would use private methods). This allows consumers to use either extend or include however they want and have it work as expected.
I'm not sure if that answers your question or not, but there's some info for you
Let's look at this in steps.
module A
puts "self = #{self}"
extend self
def module_a_meth
"Called module_a_meth"
end
end
class Test
end
Test.include A
#-> self = Test
Test.instance_methods.include?(:module_a_meth)
#=> true
Test.methods.include?(:module_a_meth)
#=> false - no class method
So include includes :module_a_meth as an instance method. As self is Test, the line:
extend self
is equivalent to:
extend Test
which of course makes no reference to the module. Now we extend and obtain the expected result:
Test.extend A
#=> true
Test.methods.include?(:module_a_meth)
#=> true
including and extending B is normal:
module B
def module_b_meth
"Called module_b_meth"
end
end
Test.include B
Test.instance_methods.include?(:module_b_meth)
#=> true
Test.extend B
Test.methods.include?(:module_b_meth)
#=> true
First of all, regarding the actual question: No :).
Class (or any other object) cares how methods are defined in a module you're including. Basically, method's in a module you've described are defined as mixin methods. extend self doesn't redefine methods to be a module methods, but, basically, duplicates them to both contexts.
It's pretty much a question about how does extend work, it's just a tricky case.
First of all, think of extend as an include in object's singleton class context. Those two definitions are equal:
module SomeModule
def hi
'hi'
end
end
class SomeClass
extend SomeModule
end
class SomeClass
class << self
include SomeModule
end
end
Given that, by using extend self in a module you're saying: Take all of the mixin methods I've defined and extend module's singleton class with them. This magic is a result of ruby's nature: an ability to re-open any definition. Here's how a verbose version of extend self would look like:
module Module1
def hi
'hi'
end
end
module Module1
extend Module1 # which is self
#### now "hi" is both here:
# def hi; end
#### and here:
# class << self; def hi; end
end
Module1.hi # => 'hi'
class SomeClass; include Module1; end;
SomeClass.new.hi # => 'hi'
__ EDIT __
Just a quick proof that object cares about how methods in a module are defined:
module SomeModule
def self.hi
'hi'
end
end
object = 'some string'
class << object
include SomeModule
end
object.hi # => NoMethodError: undefined method

Usage of "self", mixin methods, and exposed methods

This is some code that I use in a class called Game:
def play
puts "There are #{#players.length} players in #{#title}."
#players.each do |n|
puts n
end
#players.each do |o|
GameTurn.take_turn(o)
puts o
end
end
It uses a line of code that references a module called GameTurn. Within GameTurn, I have a method called self.take_turn:
require_relative "die"
require_relative "Player"
module GameTurn
def self.take_turn(o)
die = Die.new
case die.roll
when 1..2
o.blam
puts "#{o.name} was blammed homie."
when 3..4
puts "#{o.name} was skipped."
else
o.w00t
end
end
end
I'm a little confused why we use "self" and the difference between exposed methods and mixin methods in modules. I asked this "instance methods of classes vs module methods"
Is take_turn really an exposed method? Even though we're feeding into the take_turn method an object from the player class, is this method still considered a module method that we're using directly? Is this not considered a mixin method? We are feeding into the take_turn method an object from another class, so isn't it mixing in with other classes?
Also, I am still trying to figure out when/why we use the term "self"? It just seems weird that we need to define the method take_turn within the GameTurn module using the term "self". It seems like it should be defined without "self" no?
Ok, from the start:
self always returns the object in which context it is executed. So here:
class A
self #=> A
end
In ruby, you can define methods on objects in flight, you can for example do:
o = Object.new
o.foo #=> NameError
def o.foo
:foo
end
o.foo #=> :foo
Classes and modules are just objects as everything else, hence you can define methods on them as well:
def A.method
'class method'
end
A.method #=> 'class_method'
However it is much easier and more convinient to define it within the class body - because of self, which always returns the class itself:
class A
def self.foo
:foo
end
end
self returns A, so this can be read as:
class A
def A.foo
:foo
end
end
The good thing about this is that if you decide to change class name, you only need to do it on top, next to class - self will take care of the rest.
Within the method self is always the receiver of the method. So:
o = Object.new
def o.method
self
end
o.method == o #=> true
It might be however pretty confusing from time to time. Common confusion come from the code:
class A
def get_class
self.class
end
end
class B < A
end
b = B.new
b.get_class #=> B
even though get_class is defined on class A, self refers to the receiver of a method, not the method owner. Hence it evaluates to:
b.class #=> B
For the same reason self within class methods always points to the class the method is executed on.

what's difference between # and ## in a module?

Assuming a module is included, not extended, what's difference between module instance variable and class variable?
I do not see any difference between two.
module M
#foo = 1
def self.foo
#foo
end
end
p M.foo
module M
##foo = 1
def self.foo
##foo
end
end
p M.foo
I have been using # as ## within a module, and I recently saw other codes are using ## within a module. Then I thought I might have been using it incorrectly.
Since we cannot instantiate a module, there must be no difference between # and ## for a module. Am I wrong?
----------------------- added the following --------------------
To answer some of questions on comments and posts, I also tested the following.
module M
#foo = 1
def self.bar
:bar
end
def baz
:baz
end
end
class C
include M
end
p [:M_instance_variabies, M.instance_variables] # [#foo]
p [:M_bar, M.bar] # :bar
c = C.new
p c.instance_variables
p [:c_instance_variabies, c.instance_variables] # []
p [:c_baz, c.baz] :baz
p [:c_bar, c.bar] # undefined method
When you include a module within a class, module class variables and class methods are not defined in a class.
Class variables can be shared between modules and classes where these modules are included.
module A
##a = 5
end
class B
include A
puts ##a # => 5
end
Meanwhile, instance variables belong to self. When you include module A into class B, self object of A is not the same as self object of B, therefore you will not be able to share instance variables between them.
module A
#a = 5
end
class B
include A
puts #a # => nil
end
## refers to class variable and # refers to instance variable. While using this with module makes big difference. When you include a module you add class methods to your class while extending add instance methods to your class
Following is a nice article on this
http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
I found this SO post that talks about how to create class variables in modules, "they are supported natively." One commenter even says the name 'class variable' is misleading since classes are just modules with a few extra powers.
All I'm sure of is nearly every article I've read about class variables thinks they're evil and to avoid them at all costs because of the weird inheritance issues you can encounter.

Resources