This modules thing somehow really does not work for me, please help.

Hey,

thank you in advance for reading this post. I struggle with
modularization / packaging / name spacing (however you want to call
it) via ruby modules. I have the feeling that somehow rails is
responsible for the problems I am facing. I hope, you can help me.

Here's an example to demonstrate more exactly what I mean:

module Storage
  module Administration

    def self.do_something()
      # .. omitted
    end

  end
end

This does sometimes work under the rails console (development mode),
most of the time I get:

ruby-1.9.2-p290 :004 > Storage::Administration.do_something()
NoMethodError: undefined method `do_something' for
Storage::Administration:Module

This one here works more often (but not always):

module Storage::Administration

    def self.do_something()
      # .. omitted
    end

end

What's about the constant resolution operator (::)? As far as I know
it is only allowed to reference constants (like module or class
names), but not directly in definitions (module X::Y::Z).

As I know it from pure ruby the first example should do it. You just
nest the modules as you need and simply reopen a module if you want to
extend it.

So, the contents of my ruby files used for this example:

"storage.rb" file (in a directory called "storage"):

module Storage
end

And my "administration.rb" (in a subdirectory of "storage" called
"administration"):

module Storage
  module Administration

  # .. omitted

  end
end

OR (as I also tried)

module Storage::Administration

    # .. omitted

end

What am I doing wrong? Why can't rails (console, runner, etc.) access
my static method _all the time_?

Thank you very much for any suggestions!
ms

Hey,

thank you in advance for reading this post. I struggle with

modularization / packaging / name spacing (however you want to call

it) via ruby modules. I have the feeling that somehow rails is

responsible for the problems I am facing. I hope, you can help me.

Here’s an example to demonstrate more exactly what I mean:

module Storage

module Administration

def self.do_something()

  # .. omitted

end

end

end

This does sometimes work under the rails console (development mode),

most of the time I get:

ruby-1.9.2-p290 :004 > Storage::Administration.do_something()

NoMethodError: undefined method `do_something’ for

Storage::Administration:Module

This one here works more often (but not always):

module Storage::Administration

def self.do_something()

  # .. omitted

end

end

What’s about the constant resolution operator (::)? As far as I know

it is only allowed to reference constants (like module or class

names), but not directly in definitions (module X::Y::Z).

As I know it from pure ruby the first example should do it. You just

nest the modules as you need and simply reopen a module if you want to

extend it.

So, the contents of my ruby files used for this example:

“storage.rb” file (in a directory called “storage”):

module Storage

end

And my “administration.rb” (in a subdirectory of “storage” called

“administration”):

module Storage

module Administration

… omitted

end

end

OR (as I also tried)

module Storage::Administration

# .. omitted

end

What am I doing wrong? Why can’t rails (console, runner, etc.) access

my static method all the time?

Thank you very much for any suggestions!

Under which directory exactly are you saving these files ?

Which version of rails?

If you use files under /lib, this may help (since recent versions of Rails,

files under lib are not by default auto-loaded).

Custom directories with classes and modules you want to be autoloadable.

config.autoload_paths += %W(#{config.root}/lib)

config.autoload_paths += Dir["#{config.root}/lib/**/"]

to understand the issue, I suggest you try step by step

Storage

Storage::Administration

Storage::Administration.function

to see how far it works and where exactly it breaks.

This code below works reliably in Rails 3.1.x

$ cat lib/storage/administration.rb

module Storage

module Administration

def self.foo

“foo”

end

end

end

$ rails c

Loading development environment (Rails 3.1.3)

001:0> Storage

=> Storage

002:0> Storage::Administration

=> Storage::Administration

003:0> Storage::Administration.foo

=> “foo”

004:0> quit

$ rgrep -i autoload config/application.rb

Custom directories with classes and modules you want to be autoloadable.

config.autoload_paths += %W(#{config.root}/lib)

config.autoload_paths += Dir["#{config.root}/lib/**/"]

I would expect this code to be reliable on your side as well.

HTH,

Peter

Hey,

thank you in advance for reading this post. I struggle with
modularization / packaging / name spacing (however you want to call
it) via ruby modules. I have the feeling that somehow rails is
responsible for the problems I am facing. I hope, you can help me.

It sounds like the issue is with rails' autoloading system

What's about the constant resolution operator (::)? As far as I know
it is only allowed to reference constants (like module or class
names), but not directly in definitions (module X::Y::Z).

module X::Y::Z is fine, but requires that X::Y already exists, whereas

module X
  module Y
    module Z
    end
  end
end

Doesn't. This also means that in the first case rails will look (via const_missing) for X::Y (in x/y.rb) whereas in the second case it won't. If in x/y.rb you added some methods to Y and x/y.rb had never been loaded but z.rb had been then X::Y would still exist because of z.rb, but those methods from y.rb wouldn't.

As I know it from pure ruby the first example should do it. You just
nest the modules as you need and simply reopen a module if you want to
extend it.

So, the contents of my ruby files used for this example:

"storage.rb" file (in a directory called "storage"):

module Storage
end

Actually, rails would expect to find Storage in storage.rb at the top level, ie not in the storage folder

And my "administration.rb" (in a subdirectory of "storage" called
"administration"):

module Storage
module Administration

# .. omitted

end
end

OR (as I also tried)

module Storage::Administration

   # .. omitted

end

Ditto, rails expects to find Storage::Administration in storage/administration.rb

What am I doing wrong? Why can't rails (console, runner, etc.) access
my static method _all the time_?

Probably a combination of not putting things quite in the right place and using the
module X
  module Y
  end
end
style, which doesn't force rails to load x.rb, leading to different behaviour depending on whether you reference X before X::Y or the other way around.

Fred

Thanks for the very interesting explanation.

If I understand your reasoning correctly, the logic would be that

in the cases with separate module definitions:

  1. module X::Y::Z is loaded from the file …/lib/x/y/z.rb

  2. this implicitely also defines the constant X::Y

(based solely on information in file …/lib/x/y/z.rb

and not trying to read …/lib/x/y.rb)

  1. when X::Y is used later on, it is never really loaded from the

file …/lib/x/y.rb , so what is defined there can be

mysteriously missing (depending on the order modules where loaded).

However, I could not produce this behavior as I understood it.

As a test, I made 2 implementations of the module in ./lib/x/y/z.rb

In both cases I have in …/lib/x/y.rb

…/lib/x$ cat y.rb

puts “loading module X / module Y in file …/lib/x/y.rb”

module X

module Y

end

end

Test 1: separate module definitions

Yeah I mixed things up a bit there as well as slightly mis-
remembering. if you do things as rails expects, and rely entirely on
the const_missing hooks then either way works. but if you deviate from
that then things are different.

For example if z.rb is

module X
  module Y
    module Z
    end
  end
end

and you do require_dependency 'x/y/z' then x/y.rb won't be loaded

but if you do

module X::Y::Z
end

then it will.

I guess the point I was trying to make was that if ruby ends up
reading z.rb then using module X::Y::Z guarantees that x.rb and x/y.rb
will be read, but the other form doesn't, although the usual scenarios
would mean that x.rb and x/y.rb would have been read earlier on
anyway.

Fred

Thanks for the additional info. I was now able to reproduce your point.

…/lib/x/y$ cat z.rb

module X

module Y

module Z

def self.foo

“Z.foo”

end

end

end

end

and using ‘require_dependency’ the result the functionality in the

file x/y.rb is not loaded when loading module Z:

$ rails c

Loading development environment (Rails 3.1.3)

001:0> ActiveSupport::Dependencies.logger = Rails.logger

002:0> ActiveSupport::Dependencies.log_activity=true

003:0> require_dependency ‘x/y/z’

=> true

In log this gives:

Dependencies: called require_or_load("…/lib/x/y/z.rb", nil)

Dependencies: loading …/lib/x/y/z

Dependencies: called load_file("…/lib/x/y/z.rb", [“X::Y::Z”, “Y::Z”, “Z”])

Dependencies: called new_constants_in(“X::Y”, “Y”, :Object)

Dependencies: New constants: X::Y::Z, X

Dependencies: loading …/lib/x/y/z.rb defined X::Y::Z, X

X::Y::Z is defined as expected

X is also defined (that is an ancestor)

X::Y and Y are not defined (while these are ancestors too)

004:0> X::Y.foo

NoMethodError: undefined method `foo’ for X::Y:Module

from (irb):4

from .../gems/railties-3.1.3/lib/rails/commands/console.rb:45:in `start'

from .../gems/railties-3.1.3/lib/rails/commands/console.rb:8:in `start'

from .../gems/railties-3.1.3/lib/rails/commands.rb:40:in `<top (required)>'

from script/rails:6:in `require'

from script/rails:6:in `<main>'

005:0> require_dependency ‘x/y’

loading module X / module Y in file …/lib/x/y.rb

=> true

This shows in the log:

Dependencies: called require_or_load("…/lib/x/y.rb", nil)

Dependencies: loading …/lib/x/y

Dependencies: called load_file("…/lib/x/y.rb", [“X::Y”, “Y”])

Dependencies: called new_constants_in(“X”, :Object)

Dependencies: New constants:

006:0> X::Y.foo

=> “Y.foo”

With the version:

/lib/x/y$ cat z.rb

module X::Y::Z

def self.foo

“Z.foo”

end

end

The result is:

$ rails c

Loading development environment (Rails 3.1.3)

001:0> ActiveSupport::Dependencies.logger = Rails.logger

002:0> ActiveSupport::Dependencies.log_activity=true

003:0> require_dependency ‘x/y/z’

loading module X / module Y in file …/lib/x/y.rb

=> true

log now shows a recursive resolution

Dependencies: called require_or_load("…/lib/x/y/z.rb", nil)

Dependencies: loading …/lib/x/y/z

Dependencies: called load_file("…/lib/x/y/z.rb", [“X::Y::Z”, “Y::Z”, “Z”])

Dependencies: called new_constants_in(“X::Y”, “Y”, :Object)

Dependencies: called load_missing_constant(Object, :X)

Dependencies: called load_missing_constant(X, :Y)

Dependencies: called require_or_load("…/lib/x/y.rb", nil)

Dependencies: loading …/lib/x/y

Dependencies: called load_file("…/lib/x/y.rb", [“X::Y”, “Y”])

Dependencies: called new_constants_in(“X”, :Object)

Dependencies: New constants: X::Y

Dependencies: loading …/lib/x/y.rb defined X::Y

Dependencies: New constants: X::Y::Z, X

Dependencies: loading …/lib/x/y/z.rb defined X::Y::Z, X

004:0> X::Y.foo

=> “Y.foo”

Thanks again,

Peter