Thanks again for your input on this which helped me a lot in digging deeper into this topic.
You're welcome!
Found some additional (uniting?) insight [1] on this: »A Ruby module is the container part of a class *without the factory. You can’t instantiate a module*, but you can put things inside of a module. Modules can hold methods, constants, classes, and even other modules.«
Learned a lot from both of you and Russ about the OO-part, but
That is one usage of them [modules]. Creating namespaces is another.
how does this relate to my original somewhat contrived but still real and probably more name-spacing (lexical?) related problem?
~~~
module M
def tst
puts "Tst: in #{inspect}"
end
puts "Main: in #{inspect}"
tst
end
#=> NameError: undefined local variable or method `tst’ for M:Module
~~~
From my initial assumption (modules as namespaces) and supported (?) by Robert’s explanation I would assume my code not failing here. But it does. Matthew’s explanation re. the OO-part and modules being like classes, therefore defining my method with self.tst at the class hence module level, made perfectly sense to me. And worked. But what about the name space part of the story?
In this case there is no "name space part of the story". Modules have
different purposes but not all of them show up in each use case. The
namespace part comes into play if you define constants OR define
instance methods of the module instance:
Case 1: constants
irb(main):001:0> module Ns1; module Ns2; class X; end; p X; end end
Ns1::Ns2::X
=> Ns1::Ns2::X
irb(main):002:0> class Ns3; module Ns2; class X; end; p X; end end
Ns3::Ns2::X
=> Ns3::Ns2::X
irb(main):003:0> module Ns4; FOO = 123; end
=> 123
irb(main):004:0> Ns4::FOO
=> 123
irb(main):005:0> Ns4.FOO
NoMethodError: undefined method `FOO' for Ns4:Module
from (irb):3
from /usr/bin/irb:11:in `<main>'
Case 2: instance methods of the module (implicitly defined in the
eigenclass as Matthew has shown):
irb(main):001:0> module Ns1; def self.tst; printf "tst in %p\n", self end end
=> :tst
irb(main):002:0> Ns1.tst
tst in Ns1
=> nil
irb(main):003:0> Ns1::tst
tst in Ns1
=> nil
Note the different possible calling syntax: for a constant only "::"
works but for a method "." works as well.
What I’m trying to achieve with my name space / module is to have some methods which can be invoked from anywhere as I was just refactoring a quite straight forward Ruby script (no OO); exactly as Robert laid out:
[…] methods defined at top level look a bit like functions which are available independently from any instance. Making them private instance methods of Object allows that; they can be invoked anywhere without a receiver.
More precisely: they (private methods) must be invoked without a
receiver. If you want to define methods which are available everywhere
why don't you just define them on top level? If you want to put them
into a namespace then you need to define instance methods of the
module as done in the example above and as Matthew already
demonstrated. So you would do
irb(main):001:0> module Ns1; def self.foo; printf "foo in %p\n", self; end end
=> :foo
irb(main):002:0> module Ns2; Ns1::foo; end
foo in Ns1
=> nil
irb(main):003:0> module Ns2; ::Ns1::foo; end
foo in Ns1
=> nil
And, you see, the global namespace is not polluted:
irb(main):004:0> module Ns2; foo; end
NameError: undefined local variable or method `foo' for Ns2:Module
from (irb):4:in `<module:Ns2>'
from (irb):4
from /usr/bin/irb:11:in `<main>'
irb(main):005:0> foo
NameError: undefined local variable or method `foo' for main:Object
from (irb):5
from /usr/bin/irb:11:in `<main>'
But that’s only working with main / private instance methods of Object…so where does my method tst live in the above example? As the error message states it’s obviously not in my module M. But if it were in main it should be available from anywhere, shouldn’t it? Hence, tst neither seems to be living in main…
It is an instance method defined in the module. This means, it can be
executed on instances of a class mixing in that module OR by any
instance extended by that module:
irb(main):001:0> module M; def tst; printf "tst in %p\n", self end end
=> :tst
irb(main):002:0> M.tst
NoMethodError: undefined method `tst' for M:Module
from (irb):2
from /usr/bin/irb:11:in `<main>'
irb(main):003:0> M::tst
NoMethodError: undefined method `tst' for M:Module
from (irb):3
from /usr/bin/irb:11:in `<main>'
irb(main):004:0> class X; include M end
=> X
irb(main):005:0> X.new.tst
tst in #<X:0x0000000177a1e0>
=> nil
irb(main):009:0> o = Object.new
=> #<Object:0x00000001763d00>
irb(main):010:0> o.tst
NoMethodError: undefined method `tst' for #<Object:0x00000001763d00>
from (irb):10
from /usr/bin/irb:11:in `<main>'
irb(main):011:0> o.extend M
=> #<Object:0x00000001763d00>
irb(main):012:0> o.tst
tst in #<Object:0x00000001763d00>
=> nil
[1] Russ Olsen, »Eloquent Ruby«: Chapter 15 »Use Modules as Name Spaces«
Kind regards
robert
···
On Mon, May 16, 2016 at 5:55 PM, Michael Schwarze <michael@schwarze-web.de> wrote:
Am 16.05.2016 um 13:03 schrieb Robert Klemme <shortcutter@googlemail.com>:
On Mon, May 16, 2016 at 1:19 AM, Matthew Kerwin <matthew@kerwin.net.au> wrote:
Am 16.05.2016 um 15:15 schrieb Robert Klemme <shortcutter@googlemail.com>:
--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/