Object not missing constant... what am I doing wrong?

T

Teleolurian

Background: I'm using ActiveRecord to handle some bulk uploads. I
decided it would be a good idea to put all my models into subfolders,
then load them with autoload [essentially, so I can namespace
everything into a module]. Here's the relevant code (i'm using
MODULENAMEBase.rb as the naming convention for the class that does
establish_connection):

module DBNamespace
def self.included(m)
@models_path = "models/#{m}"
require "#{@models_path}/#{m}Base.rb"
Dir.new(@models_path).entries.grep(/\.rb$/).collect do |fn|
cname = fn.sub(/\.rb$/, '')
cname[0] = cname[0].chr.upcase
m.autoload(cname, "#{@models_path}/#{fn}") unless fn ==
"#{m}Base.rb"
end
end

end

module PL
include DBNamespace
end

----
Right now, I can check PL.autoload?:)Agent) and get the correct path;
I can even load the constant with PL.const_get:)Agent). However,
trying PL::Agent gives me a:

/usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/
dependencies.rb:278:in `load_missing_constant': uninitialized constant
PL::Agent (NameError)
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/
active_support/dependencies.rb:467:in `const_missing'

And doing an include PL; Agent gives me the odder:

/usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/
dependencies.rb:252:in `load_missing_constant': Object is not missing
constant Agent! (ArgumentError)
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/
active_support/dependencies.rb:467:in `const_missing'
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/
active_support/dependencies.rb:479:in `const_missing'
from header.rb:7


Can anybody clear this up?
 
B

Bryan JJ Buckley

Hi,

Looks like you're trying to reinvent something that Rails already
tries to do for you, and the two mechanisms aren't playing nicely
together.

2008/6/18 Teleolurian said:
Background: I'm using ActiveRecord to handle some bulk uploads. I
decided it would be a good idea to put all my models into subfolders,
then load them with autoload [essentially, so I can namespace
everything into a module]. Here's the relevant code (i'm using
MODULENAMEBase.rb as the naming convention for the class that does
establish_connection):

module DBNamespace
def self.included(m)
@models_path = "models/#{m}"
require "#{@models_path}/#{m}Base.rb"
Dir.new(@models_path).entries.grep(/\.rb$/).collect do |fn|
cname = fn.sub(/\.rb$/, '')
cname[0] = cname[0].chr.upcase
m.autoload(cname, "#{@models_path}/#{fn}") unless fn ==
"#{m}Base.rb"
end
end

end

module PL
include DBNamespace
end

----
Right now, I can check PL.autoload?:)Agent) and get the correct path;
I can even load the constant with PL.const_get:)Agent). However,
trying PL::Agent gives me a:

/usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/
dependencies.rb:278:in `load_missing_constant': uninitialized constant
PL::Agent (NameError)
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/
active_support/dependencies.rb:467:in `const_missing'

This happens because when you try to access PL::Agent directly, your
PL module hasn't been explicitely included into anything, and
therefore your `included` block was never called. Hence, no constant
PL::Agent.
And doing an include PL; Agent gives me the odder:

/usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/
dependencies.rb:252:in `load_missing_constant': Object is not missing
constant Agent! (ArgumentError)
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/
active_support/dependencies.rb:467:in `const_missing'
from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/
active_support/dependencies.rb:479:in `const_missing'
from header.rb:7

This is where you're tripping over Rails's ActiveSupport autoloading.
First you include PL, which includes DBNamespace, and calls
DBNamespace::included, and that terminates properly. At this point,
you have added Agent as autoloadable... That would (probably) be fine
in a pure Ruby application. But Rails already has an autoloading
mechanism, which is based on overriding Class#const_missing, and which
is apparently called as soon as you reference the (as yet unknown)
constant Agent. This apparantly is already known (probably due to your
manual addition of it to the autoload map).
Can anybody clear this up?

I would recommend that if you want to namespace models like this, let
Rails to the work its own way for you. Put files in a hierarchical
structure under app/models or lib/ (which are both in the load path
anyway), and name those files consistently. For example, in
db_namespace.rb, just have

Dir["#{File.basename(__FILE__, 'rb')}/**/*.rb"].each do |requirement|
require requirement
end

and in db_namespace/agent.rb, do something like

module DBNamespace
class Agent
# stuff
end
end

Then Agent is autoloaded the first time ruby sees DBNamespace::Agent.

Apologies if I've missed some of the point of what you were trying to
do, but I do recommend that you stick to Rails conventions when doing
something like this, as trying to second-guess all the ActiveSupport
core extensions to Module and Class are almost guaranteed to cause
headaches.
 
T

Teleolurian

Hi,

Looks like you're trying to reinvent something that Rails already
tries to do for you, and the two mechanisms aren't playing nicely
together.

2008/6/18 Teleolurian <[email protected]>:


Background: I'm using ActiveRecord to handle some bulk uploads. I
decided it would be a good idea to put all my models into subfolders,
then load them with autoload [essentially, so I can namespace
everything into a module]. Here's the relevant code (i'm using
MODULENAMEBase.rb as the naming convention for the class that does
establish_connection):
module DBNamespace
 def self.included(m)
   @models_path = "models/#{m}"
   require "#{@models_path}/#{m}Base.rb"
   Dir.new(@models_path).entries.grep(/\.rb$/).collect do |fn|
     cname = fn.sub(/\.rb$/, '')
     cname[0] = cname[0].chr.upcase
     m.autoload(cname, "#{@models_path}/#{fn}") unless fn ==
"#{m}Base.rb"
   end
 end

module PL
 include DBNamespace
end
/usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/
dependencies.rb:278:in `load_missing_constant': uninitialized constant
PL::Agent (NameError)
       from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/
active_support/dependencies.rb:467:in `const_missing'

This happens because when you try to access PL::Agent directly, your
PL module hasn't been explicitely included into anything, and
therefore your `included` block was never called. Hence, no constant
PL::Agent.


And doing an include PL; Agent gives me the odder:
/usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/
dependencies.rb:252:in `load_missing_constant': Object is not missing
constant Agent! (ArgumentError)
       from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/
active_support/dependencies.rb:467:in `const_missing'
       from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/
active_support/dependencies.rb:479:in `const_missing'
       from header.rb:7

This is where you're tripping over Rails's ActiveSupport autoloading.
First you include PL, which includes DBNamespace, and calls
DBNamespace::included, and that terminates properly. At this point,
you have added Agent as autoloadable... That would (probably) be fine
in a pure Ruby application. But Rails already has an autoloading
mechanism, which is based on overriding Class#const_missing, and which
is apparently called as soon as you reference the (as yet unknown)
constant Agent. This apparantly is already known (probably due to your
manual addition of it to the autoload map).


Can anybody clear this up?

I would recommend that if you want to namespace models like this, let
Rails to the work its own way for you. Put files in a hierarchical
structure under app/models or lib/ (which are both in the load path
anyway), and name those files consistently. For example, in
db_namespace.rb, just have

  Dir["#{File.basename(__FILE__, 'rb')}/**/*.rb"].each do |requirement|
    require requirement
  end

 and in db_namespace/agent.rb, do something like

  module DBNamespace
    class Agent
      # stuff
    end
  end

Then Agent is autoloaded the first time ruby sees DBNamespace::Agent.

Apologies if I've missed some of the point of what you were trying to
do, but I do recommend that you stick to Rails conventions when doing
something like this, as trying to second-guess all the ActiveSupport
core extensions to Module and Class are almost guaranteed to cause
headaches.

Thank you very much! Adding module namespaces around the model classes
was what I needed to do. Probably should have been obvious, but I've
just come back to ruby after a bit of a hiatus :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,154
Members
46,702
Latest member
LukasConde

Latest Threads

Top