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
Related
Two modules Foo and Baa respectively define a method with the same name name, and I did include Foo and include Baa in a particular context.
When I call name, how can I disambiguate whether to call the name method of Foo or Baa?
Only the order of modules inclusion decides which one will get called. Can't have both with the same name - the latter will override the former.
Of course, you can do any tricks, just from the top of my head:
module A
def foo
:foo_from_A
end
end
module B
def foo
:foo_from_B
end
end
class C
def initialize(from)
#from = from
end
def foo
from.instance_method(__method__).bind(self).call
end
private
attr_reader :from
end
C.new(A).foo #=> :a_from_A
C.new(B).foo #=> :a_from_B
But that's no good for real life use cases :)
Technically, there is no name collision because the method foo is redefined.
In the following exemple, A.foo is redefined and is never called
module A
def foo
raise "I'm never called"
end
end
module B
def foo
puts :foo_from_B
end
end
class C
include A
include B
end
C.new.foo
# =>
# foo_from_B
If you write A and B module, you can use super to call previous definition of foo. As if it where an inherited method.
module A
def foo
puts :foo_from_A
end
end
module B
def foo
super
puts :foo_from_B
end
end
class C
include A
include B
end
C.new.foo
# =>
# foo_from_A
# foo_from_B
There are side effects and I would not use this but this is doing the trick :
module A
def foo
puts :foo_from_A
end
end
module B
def foo
puts :foo_from_B
end
end
class C
def self.include_with_suffix(m, suffix)
m.instance_methods.each do |method_name|
define_method("#{method_name}#{suffix}", m.instance_method(method_name))
end
end
include_with_suffix A, "_from_A"
include_with_suffix B, "_from_B"
end
c= C.new
c.foo_from_A
c.foo_from_B
begin
c.foo
rescue NoMethodError
puts "foo is not defined"
end
# =>
# foo_from_A
# foo_from_B
# foo is not defined
Provided none of the methods of Foo or Baa call name (which seems a reasonable assumption), one can simply create aliases.
module Foo
def name; "Foo#name"; end
end
module Baa
def name; "Baa#name"; end
end
class C
include Foo
alias :foo_name :name
include Baa
alias :baa_name :name
undef_method :name
end
c = C.new
c.foo_name
#=> "Foo#name"
c.baa_name
#=> "Baa#name"
C.instance_methods & [:foo_name, :baa_name, :name]
#=> [:foo_name, :baa_name]
The keyword alias is documented here. One may alternatively use the method #alias_method. See this blog for a comparison of the two.
Module#undef_method is not strictly necessary. It's just to ensure that an exception is raised if name is called.
You should definetely read about method lookups.
Anyway, I would do it this way:
module Foo
def name
:foo
end
end
module Bar
def name
:bar
end
end
class MyClass
include Foo
include Bar
def foo_name
Foo.instance_method(:name).bind(self).call
end
def bar_name
Bar.instance_method(:name).bind(self).call
end
#
# or even like this: obj.name(Foo)
#
def name(mod)
mod.instance_method(:name).bind(self).call
end
end
BTW if you are using Module#instance_method and UnboundMethod#bind you don't really need to include specific module. This code works:
Foo.instance_method(:name).bind('any object (e.g. string)').call
I want to modify the existing module A from a library:
module A
class << self
def foo
bar('Baz')
end
private
def bar(val)
val.upcase
end
end
end
A.foo
=> "BAZ"
module B
extend A
def self.bar(val)
val.downcase
end
end
B.foo # hoping for 'baz', but instead:
NoMethodError: undefined method `foo' for B:Module
Is there a way to re-use method .foo from A and only modify method .bar?
extend A doesn't work, because foo and bar are no instance methods of A, but of A's singleton class. To re-use those methods, you have to either create a copy of A as mudasobwa described, or you can use a refinement of A's singleton class like this:
module B
extend(refine(A.singleton_class) do
def bar(val)
val.downcase
end
end)
end
B.foo # => "baz"
You cannot use extend A.singleton_class as extend doesn't accept a class as argument. refine returns a module which is exactly what's needed.
Assuming you have A declared as above, the following code would do:
▶ B = A.clone
#⇒ B
▶ (class << B; self; end).send :define_method, :bar do |val|
val.downcase
end
▶ A.foo
#⇒ "BAZ"
▶ B.foo
#⇒ "baz"
There probably should be less tricky way, but I can’t yet figure it out.
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
The following prints Bar twice:
class Foo
def foo
p self.class # => prints Bar
end
end
class Bar < Foo
def foo
p self.class # => prints Bar
super
end
end
b = Bar.new
b.foo
How do I get it to print
Bar
Foo
? i.e. I want to know what class each method is defined on.
To capture the context in which a method was originally defined, you can use define_method instead of def to get the appropriate closure. A simple example:
class Foo
klass = self
define_method(:foo){p klass}
end
class Bar < Foo
def foo
p self.class
super
end
end
b = Bar.new
b.foo
You could change Foo#foo like so (provided there is just one subclass level):
class Foo
def foo
if self.class == Foo
p self.class
else
p self.class.superclass
end
end
end
class Bar < Foo
def foo
p self.class
super
end
end
Foo.new.foo
Foo
Bar.new.foo
Bar
Foo
You can use
b.class.superclass <= "Foo"
The problem you are having there is that self is the instance of Bar, b.
b.class <= always going to be Bar
self.class <= always going to be Bar if you are invoking Bar.
You say that you are defining a method at runtime, and that you don't know the class name. I don't really know what you mean ... the way I would handle this would be something like
class Bar
def initialize
puts 'In BAR class'
end
def foo
p self.class.name # => prints Bar
end
end
and then
Bar.class_eval do
def brand_new_method
# do something new
p "Still in Bar, but this is dynamically added"
end
end
Maybe you are talking about dynamically adding methods to classes higher in the inheritance chain ... to "Foo" in your example ... based on some conditional happening in an instance of "Bar". If that is the case, then why don't you use a single module to define your inherited methods:
module Foo
def foo
p self.class
end
end
and then use module_eval the same way as class_eval?
module Foo
def self.bar
"Module.bar"
end
end
def Foo
"main.Foo"
end
puts Foo # Foo
puts Foo.bar # Module.bar
puts Foo() # main.Foo
puts Foo.class # Module
Why is it possible for the module Foo and the method Foo to coexist in the same namespace? Why doesn't the definition of the method Foo overwrite the module?
They don't exist in the same namespace. Constants and methods are in different namespaces. That's how they coexist. The language looks in the constant namespace unless you use the method-call parens, which unambiguously say "This is supposed to be a method."