Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby nested modules as namespaces

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 ;.

like image 931
SasQ Avatar asked Sep 03 '13 06:09

SasQ


1 Answers

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 extends 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).

like image 61
3 revs Avatar answered Sep 28 '22 08:09

3 revs