Define a module inside a module - ruby

Having the following module:
module Foo
end
How can we add inside this Foo module another module from its name (with for example: name = 'Bar')?
I would like to dynamically get this:
module Foo
module Bar
end
end

Without (ugly) string eval:
module Foo
end
bar = Module.new
Foo.const_set(:Bar, bar)

That's pretty straightforward:
module Foo
end
name = 'Bar'
Foo.class_eval <<RUBY
module #{name}
end
RUBY
puts Foo::Bar
# >> Foo::Bar

You just write it:
# In some part of your codebase:
module Foo
end
# Extension:
module Foo
module Bar
end
end

Related

How to call a method defined in another module in Ruby?

class Foo
include Bar
include Baz
end
module Bar
def do_something
end
end
module Baz
do_something
end
Baz module does not have access to Bar. Is there a way for it to call a method in Bar?
One approach is to extend Baz with Bar, but I want to include them all in Foo instead.
If for whatever reason you don't want to extend Baz with Bar, you can create an object that extends Bar inside Baz:
module Bar
def do_something
puts 42
end
end
module Baz
Object.new.extend(Bar).do_something # prints 42
end

accessing method in module of module ruby

I'm trying to modify existing ruby code, and ruby is not my first languange. Part of the code is like below:
#someFile1.rb
module A
module B
def somefunction()
end
end
end
class X::Y
include A::B
end
#someFile2.rb
module A
module C
def anotherfunction()
#somefunction() <-- error
end
end
end
class X::Y
include A::C
end
Somehow I can't access method somefunction() in anotherfunction.
How to access method defined in module B in method inside module C? Why it's not working?
Instance methods in modules aren't generally accessible until you mix them into a class and create an object of that class.
module A
module B
def some_method
"foo"
end
end
end
module A
module C
def another_method
some_method
end
end
end
class X
include A::B
include A::C
end
X.new.another_method
# => "foo"
But I'd say it isn't very elegant to have a module that depends upon the fact that some other module has also been mixed into the same object
Class methods in modules, on the other hand, are accessible like this:
module A
module B
def self.somefunction
"foo"
end
end
end
module A
module C
def self.another_function
A::B.somefunction
end
end
end
A::C.another_function
# => "foo"
Assuming you want to call the module functions themselves, you would first need to make them module functions (think static in Java or namespace in C++). Then you can use the :: (namespace resolution) operator. See foo and bar.
If you want to import them into classes, just import both, and both will be visible. See baz and qux.
module A
module B
def self.foo
puts "foo"
end
def baz
puts "baz"
end
end
end
module A
module C
def self.bar
puts "bar"
A::B::foo
end
def qux
puts "qux"
baz
end
end
end
class X
include A::B
include A::C
end
A::C::bar
x = X.new
x.qux
Output:
bar
foo
baz
qux

Can I overwrite instance method from module?

I know, I can overwrite class method from module this way
class Foo
class << self
def some_static_method
puts 'some_static_method'
end
end
end
module BAR
class << Foo
def some_static_method
puts 'another_static_method'
end
end
end
class Foo
include BAR
end
Foo.some_static_method # => 'another_static_method'
Is it possible for an instance method?
You can do the following:
class Foo
def self.some_static_method; puts "Hello from Foo" end
end
module Bar
def self.included(base)
base.instance_eval do
def some_static_method; puts "Hello from Bar" end
end
end
end
class Foo
include Bar
end
Foo.some_static_method
This should work
UPDATE
To override instance method use:
class Foo
def some_instance_method; puts "Hello from Foo" end
end
module Bar
def self.included(base)
base.class_eval do
def some_instance_method; puts "Hello from Bar" end
end
end
end
class Foo
include Bar
end
Foo.new.some_instance_method
Your question is actually not about method overriding. It is about what class is referred to within a class ... construction in a module body.
When you do
module Bar
class << Foo
p self
end
end
# => #<Class:Foo>
the << Foo points to the singleton class of the Foo in the main environment because class << Foo cannot define the singleton class directly of a class Foo that has not been defined in advance. So it looks up for Foo that is already defined, and such class is found in the main environment.
When you do
module Bar
class Foo
p self
end
end
# => Bar::Foo
a new class Bar::Foo is created; the Foo points to this Bar::Foo that is newly created, and it does not point to the Foo in the main environment. In order to point to it, you have to explicitly specify that with ::.
module Bar
class ::Foo
p self
end
end
# => Foo
If you are using Ruby > 2.0.0 then what you can use is Module#prepend. Instead of include you can prepend an module and that way all of the module's methods are overriding any existing class instance methods with the same name. You can see a quick example here.
Prior to Ruby 2, Rails had introduced a similar hack: #alias_method_chain
Here is a nice comparison of the two approaches.

Getting module a class is defined in without string manipulation

Referencing ( Ruby name of Module from a class defined within, Module.nesting within instance_eval/exec or module_eval/exec)
In the following setup:
module Foo
class Bar
end
end
Is there are way to get the module name Foo without having to modify Bar and without resorting to string manipulation on the fully-qualified name Foo::Bar?
I'm cheating, I don't modify Bar but Class:
class Class
def my_module
self.to_s.split('::').first
end
end
module Foo
class Bar
end
end
p Foo::Bar.my_module #-> "Foo"
#Works also after include
include Foo
p Bar.my_module #-> "Foo"

Reflect on nested namespace

I am trying to find the root class/module of a nested namespace.
Is this the most efficient way to find it? I don't like that I am converting to a string. It seems like there should be a more elegant solution.
class Foo
class Bar
def parent
Object.const_get self.class.to_s.split(/::/).first
end
end
end
Foo::Bar.new.parent #=> Foo
There is Module.nesting
module Foo
module Bar
module Baz
p Module.nesting # => [Foo::Bar::Baz, Foo::Bar, Foo]
p Module.nesting.last # => Foo
end
end
end

Resources