I have a nested structure of modules, which are used only for namespacing purposes; no mixing into classes etc. So I have a code like this:
module Lib
module A
def A.foo
puts 'Lib::A::foo'
end
end
module B
def B.bar
puts 'Lib::B::bar'
end
end
end
Now suppose I want to add one more helper method in module A
, and I want it to be used easily by both modules. I get the following:
module Lib
module A
def A.foo
puts 'Lib::A::foo'
end
def A.helper
puts 'Lib::A::helper'
end
end
module B
def B.bar
puts 'Lib::B::bar'
A::helper
end
end
end
and it seems to work, but it has some drawback I'd like to get rid of:
I don't want to call helper
by its full qualified name (A::helper
) all the time from inside B
. I'd prefer to tell Ruby somehow that this namespace prefix is "default" and call it simply as helper
. In C++ I could just write using A::helper
inside namespace B
and it would solve the problem. But how to do it in Ruby?
I tried adding include A
and extend A
inside B
, but none of them works. They seem to work only inside classes, when these modules are mixed in, but not when they're stand-alone, used just for namespacing purposes.
Are there any other ways to make it work the way I want?
Oh, and one more thing:
Suppose a different scenario, where I want the A::helper
to be used only from inside A
's methods, because it's just some implementation function, where I factored out some common code used by many functions inside A
, but now I don't want it to be visible for the outside world, just to A
's methods. How can I do it?
I tried with module_function
+ removing the A.
prefix from all other functions which are supposed to be hidden, but then they're hidden also for other methods in A
, because they're instance methods then, and modules cannot be instantiated. So how can I hide a module method from outside world and still allow it for the internal use by other module methods?
Edit Why downvoting? I tried to be as clear as I possibly can, that what I need is defaulting a namespace inside another namespace to get rid of long fully-qualified names altogether (not just aliasing them to something shorter), and that my problem doesn't concern classes & objects at all, just plain modules used for namespacing purposes only. How else could I explain it?
It's not my fault that namespacing mechanisms doesn't seem to be fully supported in Ruby natively as in languages such as C++, and that it seems to be just a side-effect of using modules and classes to have some functionality of true namespaces (they quack like ducks, they have ducky beaks, but they're platypuses, not ducks, and shouldn't be advertised as namespaces which they apparently aren't, because it only confuses people coming to Ruby from other languages with true namespaces), nor that you apparently don't understand concepts from other programming languages if they aren't easily possible in Ruby (since I see that you seem to pretend that my problem doesn't exist and revert back to what you can easily do in Ruby instead; but this is not what I need).
And why the only answer which was relevant to my question has been deleted? Not cool ;.
OK, since the only answer which was relevant to my question has been deleted, I'll try to answer my question myself, basing on that deleted answer by @sawa (thanks to @sawa for hinting me in the correct direction). I modified it somewhat to better fit my needs and be more elegant. Later I'll describe why the original @sawa's answer wasn't what I was looking for yet.
OK, so without further ado, here's my own attempt at the solution:
module Lib
module A
extend self
def foo
puts 'Lib::A::foo'
end
def helper
puts 'Lib::A::helper'
foo
end
end
module B
extend A
def self.bar
puts 'Lib::B::bar'
helper
end
end
end
puts 'Calling Lib::A::foo:'
Lib::A::foo # => Lib::A::foo
puts 'Calling Lib::A::helper:'
Lib::A::helper # => Lib::A::helper; Lib::A::foo
puts 'Calling Lib::B::bar:'
Lib::B::bar # => Lib::B::bar; Lib::A::helper; Lib::A::foo
Here's how it works:
First off, it defines all its methods as instance methods of the particular module class itself (A
in this case). But then, to make them available for external use without instantiating (which is impossible for modules after all), I extend
the A
module with itself, which makes those methods become its class methods too. But thanks to the fact that they're also instance methods of the module A
, they can be called from inside other methods of this module without prefixing them with the module name. The same goes for the module B
, which also extend
s itself with the module A
's methods, making them his own. Then I can call them inside B
's methods too without prefixing, just as I wanted.
As you can see, not only I can call both modules' methods from the outside as if they were their class's methods, and I can call A::helper
from B::foo
without fully qualifying its name, but I can also call A::foo
from A::helper
without qualifying. This is exactly what I needed and seems to work as I expected.
The only problem with this approach could be with the fact that their interfaces are mixing together. This is not much a problem inside B
, since that's what I actually wanted: to be able to access A
's methods as if it were B
's methods, without the need for prefixing them with a full qualification. So I've got what I deserved. But this might cause a problem from the outside, because it's an implementation detail of B
that it uses A
's methods internally. It shouldn't leak to the outside world, but it does. I'll try to fix it somehow with access control, maybe it'll be possible somehow.
Edit: Yup, it can be done by inserting the following line just after extend A
in B
:
private_class_method *A.public_instance_methods
This way B
can call A
's methods internally, but they're not accessible from the outside world.
Now what was wrong with the original @sawa's solution:
It's been using a third module only to proxy the interface through it. For me this was rather an ugly hack than an elegant solution, because it introduces this additional module which will confuse the users of such a library. They will not know whether they should use A
or C
, and why such a contraption is used at all. It's not obvious how it works by mere looking at it. It needs some more thorough analysis to figure out what it really does and why it is constructed that way. It's not a clean design.
In my solution, on the other hand, there are only two modules, as originally designed, and their purpose should be clear for users of this library. There's this strange extend self
, but still it appears to be a more common idiom in Ruby than spreading proxy modules all over the place.
So thanks guys for your attempts. Next time try to be less arogant (when you see someone asking a question, it's not always the case he's a noob) and fixated on your beloved One True Language (don't get me wrong, I like Ruby language, it's quite cool and clean, but it has some drawbacks too, as any language, and it's better to seek for solving them instead of burying your heads and pretending that there's no problem at all, since it's not something the language was designed for).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With