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