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"
Related
I have self-writed gem
module GemNamespace
class Foo; end
class Bar
def foo
#foo ||= Foo.new
end
end
end
Also I have application
module ApplicationNamespace
class Foo < GemNamespace::Foo; end
class Bar < GemNamespace::Bar; end
end
When I call foo method at my application it returned me instanceof GemNamespace object:
bar = ApplicationNamespace::Bar.new
puts bar.foo
=> #<GemNamespace::Foo:0x007f849d8169f0>
But I want get object of ApplicationNamespace how I can do this without redefine foo method
Your Problem is not, that you have several Namespaces, but that GemNamespace::Bar is tightly coupled to GemNamespace::Foo.
You could use something like this:
class Bar
def initialize(klass)
#klass = klass
end
def foo
#foo ||= #klass.new
end
end
So instead of only ever using GemNamespace::Foo within Bar, you could pass any class.
Your current version of the foo method will allways refer to GemNamespace::Foo because its context is set at definition (not at execution). Instead you could get the module of the current executing class dynamically. I don't think there is a build-in method that does this so you have to get it manually:
def foo
#foo ||= self.class.name.split("::")[0..-2].inject(Kernel) { |s, c| s.const_get c }.const_get("Foo").new
end
This will work for any number of nested modules.
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
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.
This is a continuation this original SO question: Using "::" instead of "module ..." for Ruby namespacing
In the original SO question, here is the scenario presented which I'm still having trouble understanding:
FOO = 123
module Foo
FOO = 555
end
module Foo
class Bar
def baz
puts FOO
end
end
end
class Foo::Bar
def glorf
puts FOO
end
end
puts Foo::Bar.new.baz # -> 555
puts Foo::Bar.new.glorf # -> 123
Can someone provide some explanation behind why the first call is returning 555 and why the second call is returning 123?
You can think of each appearance of module Something, class Something or def something as a “gateway” into a new scope. When Ruby is searching for the definition of a name that has been referenced it first looks in the current scope (the method, class or module), and if it isn’t found there it will go back through each containing “gateway” and search the scope there.
In your example the method baz is defined as
module Foo
class Bar
def baz
puts FOO
end
end
end
So when trying to determine the value of FOO, first the class Bar is checked, and since Bar doesn’t contain a FOO the search moves up through the “class Bar gateway” into the Foo module which is the containing scope. Foo does contain a constant FOO (555) so this is the result you see.
The method glorf is defined as:
class Foo::Bar
def glorf
puts FOO
end
end
Here the “gateway” is class Foo::Bar, so when FOO isn’t found inside Bar the “gateway” passes through the Foo module and straight into the top level, where there is another FOO (123) which is what is displayed.
Note how using class Foo::Bar creates a single “gateway”, skipping over the scope of Foo, but module Foo; class Bar ... opens two separate “gateways”
wow, great question. The best answer I can come up with is in this case you're using the module to define a namespace.
Check this out:
FOO = 123
module Foo
FOO = 555
end
module Foo
class Bar
def baz
puts FOO
end
def glorf3
puts ::FOO
end
end
end
class Foo::Bar
def glorf2
puts Foo::FOO
end
def glorf
puts FOO
end
end
puts Foo::Bar.new.baz # -> 555
puts Foo::Bar.new.glorf # -> 123
puts Foo::Bar.new.glorf2 # -> 555
puts Foo::Bar.new.glorf3 # -> 123
So my thought is that when you define:
module Foo
FOO = 555
end
you are creating FOO in the namespace of Foo. So when you use it here:
module Foo
class Bar
def baz
puts FOO
end
end
end
you are in the Foo namespace. However, when you refer to it in:
class Foo::Bar
def glorf
puts FOO
end
end
FOO is coming from the default namespace (as illustrated by ::FOO).
the first call:
puts Foo::Bar.new.baz # -> 555
prints the result of invoking method baz of an instance of class Foo::Bar
notice that Foo::Bar#baz definition is actually a closure on FOO. Following ruby's scope rules:
FOO is searched for in Foo::Bar (the class, not the instance) scope, it is not found,
FOO is searched for in the enclosing scope Foo (because we are within the module definition) and it is found there (555)
the second call:
puts Foo::Bar.new.glorf # -> 123
prints the result of invoking method glorf of an instance of class Foo::Bar
notice that Foo::Bar#glorf definition this time is also a closure on FOO, but if we follow ruby's scope rules you'll notice that the value closed upon this time is ::FOO (top level scope FOO) in the following way:
FOO is searched for in Foo::Bar (the class, not the instance) namespace, it is not found
FOO is searched in the enclosing scope ('top level') and it is found there (123)
glorf is a method of class Foo, in => [Foo, Module, Object, Kernel, BasicObject]
within that scope (i.e. in default/main module), FOO is assigned 123
module Foo is defined as
module Foo
FOO = 555
class Bar
def baz
puts FOO
end
end
end
where method baz belongs to class Bar in module Foo => [Bar, Foo, Object, Kernel, BasicObject]
and in that scope FOO was assigned 555
How do I determine the current open class in Ruby?
Inside the class itself:
class_name = self.class
On an initialized object named obj:
class_name = obj.class
Inside of a class definition body, self refers to the class itself. Module#name will tell you the name of the class/module, but only if it actually has one. (In Ruby, there is no such thing as a "class name". Classes are simply objects just like any other which get assigned to variables just like any other. It's just that if you happen to assign a class object to a constant, then the name method will return the name of that constant.)
Example:
puts class Foo
name
end
# Foo
But:
bar = Class.new
bar.name # => nil
BAR = bar
bar.name #=> 'BAR'
if you have obj = SomeClass.new you get the class with obj.class
If you're looking to compare via the class name (string):
class Foo
def my_class_name_is
self.class.name
end
end
Foo.new.my_class_name_is
=> "Foo"
In my case, the name method was overwrote, I find to_s give me this same result
class Foo
puts self.name
puts self.to_s
end
#=> Foo
#=> Foo