What does this extend do? - ruby

Given this example:
module A
module B
def foo
puts 'foo'
end
extend A::B
end
end
What does this extend A::B do?

It extends module A::B with itself, essentially make method foo available on module object A::B itself.
Without that line, you are not able to call A::B.foo in your code.
You may want to read more on Ruby extend aModule vs include aModule.

Related

Why can't ruby classes inside modules have the same scope as regular classes?

I have a module with a class inside, but I find that the class inside can't reach any of the methods in the enclosing module without specifying the module path.
Another way to look at it is that the module_function doesn't seem to carry into the class.
Example:
module MyMod
def meaning
42
end
class Foo
def initialize
puts "New Foo"
puts "I can call #{MyMod.meaning} from here"
puts "But I can't get to #{meaning} from here"
end
end
def go
puts "I can get to #{meaning} from here"
bar = Foo.new
end
module_function :go, :meaning
go
end
If you run this, you get an error on the "But I can't..." line of:
undefined local variable or method `meaning'
But if you remove the MyMod bits around the whole file, it has no problem accessing the outer method.
Is there an easy way to make these accessible without having to give the full path?
(One of the reasons I am nitpicking over not including the full path is because I'm using http://redshift.sourceforge.net/script/ in order to create ruby scripts that I can instantiate - and they don't have a clear and simple name like 'MyMod' that I can just add to the path, I actually have to pass the scope around even though it's just the enclosing scope to the class)
I have a module with a class inside,
No, you don't. You have a module definition with a class definition inside, but that does not make the class a nested class. Ruby does not have nested classes.
Ruby is not Beta, Scala, or Newspeak, there are no nested classes in Ruby.
Nesting a module or class definition inside another module or class definition does not create nesting relationship between the two classes / modules. It only makes the constant which references the class / module part of the outer class' / module's namespace.
In other words, there is no difference between
module Foo
class Bar
end
end
and
class Quux
end
module Foo
Bar = Quux
end
Only the constant is nested, but not the object that is referenced by the constant.
but I find that the class inside can't reach any of the methods in the enclosing module without specifying the module path.
That is precisely because there is no "enclosing module". There is a lexically enclosing module definition but that does not create any form of relationship whatsoever between the Foo class and the MyMod module.
Another way to look at it is that the module_function doesn't seem to carry into the class.
I honestly don't understand what you mean by that, what it means for a method to "carry into a class", but Module#module_function is not magic. It does exactly what the documentation says it does: it takes an instance method of the module, copies it as an instance method of the singleton class of the module, and makes the original instance method private.
You can read its specification in the Ruby/Spec, it is fairly simple. Also, the Rubinius source code, both the basic version for booting the Rubinius kernel and the full version are fairly readable.
In the end, Module#module_function really does not do much more than
class Module
def module_function(*meths)
meths.each do |meth|
define_singleton_method(meth, &instance_method(meth).bind(self))
private meth
end
self
end
end
If you run this, you get an error on the "But I can't..." line of:
undefined local variable or method `meaning'
The reason is simple: neither the class Foo nor any of its superclasses has any method of that name, so of course you get an exception.
But if you remove the MyMod bits around the whole file, it has no problem accessing the outer method.
There is no "outer method". Ruby does not have Beta-like nested classes. That is really the fundamental cause of your misunderstanding. You expect Ruby to behave like Beta, but it just doesn't. Ruby takes inspiration from any languages, most notably (in rough order of importance) Smalltalk, Lisp, Perl, and Clu, but Beta is not among them.
This here works for a completely different reason:
def meaning
42
end
class Foo
def initialize
meaning
end
end
Methods that are defined at the top-level are implicitly defined as private instance methods of Object. This is because the default definee at the top-level is ::Object. Since Foo inherits from Object, method lookup will eventually find the meaning method defined in Object.
Is there an easy way to make these accessible without having to give the full path?
Inheritance. For example, Module#append_features, which is called by Module#include, makes the module the superclass of the including class, and thus all instance methods of the module become part of the method lookup ancestry chain.
An aside: if there is no nesting, then what does Module::nesting do? Well, yeah, that is an unfortunately named method. The term "nested class" or "nested module" has a well-defined meaning in OO going all the way back to Beta. But this method is about a completely different kind of nesting:
It refers to the lexical nesting of module definitions, and not to nesting of modules themselves.
For example, these module definitions all define the exact same module, but the definition text has different nesting:
module Foo
module Bar
module Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo::Bar, Foo]
end
end
end
end
module Foo
module Bar
module Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar, Foo]
end
end
end
module Foo
module Bar::Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo]
end
end
end
module Foo::Bar
module Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo::Bar]
end
end
end
module Foo
module Bar::Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo]
end
end
module Foo::Bar::Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz]
end
end
module Foo::Bar
module Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar]
end
end
module Foo::Bar::Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux]
end
Again, this is purely lexical nesting of the module definition. The module itself is not nested; the module itself is the same in all of these cases. This nesting only affects constant lookup.
Constants are looked up first lexically outwards in enclosing module definitions, then upwards the inheritance chain.
There is another instance where things can be nested: blocks create nested lexical scopes, whereas all other lexical scopes (script, module / class definition, and method definition) don't nest. In other words, blocks and only blocks have access to the local variables (and self) of their enclosing lexical scopes.
Because of the way Ruby name resolution and method look up works. When you write x anywhere, Ruby will, in this order:
Look for a x local variable
Look for a x method in self.class
Look for a x method in self.superclass
Repeat step 3 until superclass is nil
Ruby will walk up the class hierarchy
trying to find the method x
until it reaches BasicObject.
Invoke method_missing
Its default behavior is to raise the error you encountered.
MyMod merely contains Foo, it is not part of Foo's class hierarchy. That is why the method cannot not be found.

extend a module with submodule methods and constants

I want to be able to extend a module with the methods and constants of a submodule.
If I use extend I receive undefined constant.
The only way I made it work is using both extend and include. I tried also using self.method on submodule.
module Car
module Container
HOLA = 'Helloo!'
def testing
HOLA
end
end
include Container
extend Container
end
So this both should work:
Car.testing # Hello!
Car::HOLA # Hello!
I guess this is a code smell..., but what other ways to make it work you know?
As per the description shared, it seems like you want to access the nested module constants and methods in some_other_class.
This is the module definition as mentioned in the post.
module Car
module Container
HOLA = 'Helloo!'
def testing
HOLA
end
end
end
Now Suppose , that you want to use this method in some class say Vehicle
require 'car' #this is the module file since it is residing in some other file
class Vehicle
extend Car
def test
Car.testing
end
end
Now calling Vehicle.new.test will print
"Helloo!"
Hope it helps!!

Include module in a class within the module

I have two classes. I want to namespace them. I also have a bit of functionality that they share. I do something like this:
module Talker
def say_bye
puts 'bye'
end
class Bob
include Talker
def say_yo
puts 'yo'
end
end
class Tom
include Talker
def say_hello
puts 'hello'
end
end
end
These are all valid method calls.
Talker::Bob.new.say_yo
Talker::Bob.new.say_bye
Talker::Tom.new.say_hello
Talker::Tom.new.say_bye
I was told: "This include is going to include Bob again. You should close the Talker module before starting a class that includes Talker." Can somebody explain to me if I'm doing something that results in unexpected ruby behavior or is considered taboo? Is it a bad practice to include a module like this from inside a class within that module? What might be a criticism for this pattern? Should I use inheritance here?
I figured it out. The include ends up being somewhat recursive. The code above will do things like this:
Talker::Bob::Bob
Talker::Bob::Tom
Talker::Bob::Bob::Bob::Tom::Bob::Tom
And that is not something to have. So he was right when he said it would re-include the other classes in the module into each class that includes the module.

How does include Module work?

module A
end
class Klass
include A
end
How does this include influence Klass? Does it simply put Klass into module A or do something more?
Short Answer: If you have some methods inside your module and you use include in a class, those methods can be used in the class.
Module A
def shout
puts "HEY THERE!!!!"
end
end
class Klass
include A
end
# Create instance of Klass
instance = Klass.new
# Produces "HEY THERE!!!!"
instance.shout
The include method takes all the methods from another module and
includes them into the current module. This is a language-level thing
as opposed to a file-level thing as with require. The include method
is the primary way to "extend" classes with other modules (usually
referred to as mix-ins). For example, if your class defines the method
"each", you can include the mixin module Enumerable and it can act as
a collection. This can be confusing as the include verb is used very
differently in other languages.
from here: What is the difference between include and require in Ruby?
also take a look at this page: http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html it has a verbose explanation about how include works.
include is one of the ways to include methods of a Module in another Module or Class.
Please read my article on how that affects method calls in Ruby/

Making a module inherit from another module in Ruby

I'm making a small program for Rails that includes some of my methods I've built inside of a module inside of the ApplicationHelper module. Here's an example:
module Helper
def time
Time.now.year
end
end
module ApplicationHelper
# Inherit from Helper here...
end
I know that ApplicationHelper < Helper and include Helper would work in the context of a class, but what would you use for module-to-module inherits? Thanks.
In fact you can define a module inside of another module, and then include it within the outer one.
so ross$ cat >> mods.rb
module ApplicationHelper
module Helper
def time
Time.now.year
end
end
include Helper
end
class Test
include ApplicationHelper
def run
p time
end
self
end.new.run
so ross$ ruby mods.rb
2012
One potential gotcha is that if the included module attaches class methods, then those methods may be attached to the wrong object.
In some cases, it may be safer to include the 'parent' module directly on the base class, then include another module with the new methods. e.g.
module ApplicationHelper
def self.included(base)
base.class_eval do
include Helper
include InstanceMethods
end
end
module InstanceMethods
def new_method
#..
end
end
end
The new methods are not defined directly in ApplicationHelper as the include Helper would be run after the method definitions, causing them to be overwritten by Helper. One could alternatively define the methods inside the class_eval block

Resources