18

Today I decided to reorganize big amount of user related models and I'm having a problem with it.

Before I had such structure:

app/models/user.rb
app/models/user_info.rb
app/models/user_file.rb
...

So I moved all user_ models to user subfolder like this:

app/models/user.rb
app/models/user/info.rb
app/models/user/file.rb
...

and changed their definitions to

class User::Info < ActiveRecord::Base
class User::File < ActiveRecord::Base
...

User model wasn't changed (except associations).

Everything works fine except User::File model. When i'm trying to access this model i get the following error:

warning: toplevel constant File referenced by User::File

and indeed it returns standard ruby File class.

What i'm doing wrong?

UPD1:

root# rails c
Loading development environment (Rails 3.2.13)
2.0.0p195 :001 > User::File
(irb):1: warning: toplevel constant File referenced by User::File
 => File
2.0.0p195 :002 > User::Info
 => User::Info(...)

UPD2:

2.0.0p195 :001 > User::SomeModel
NameError: uninitialized constant User::SomeModel
2.0.0p195 :002 > User::IO
(irb):2: warning: toplevel constant IO referenced by User::IO
 => IO 
2.0.0p195 :003 > User::Kernel
(irb):3: warning: toplevel constant Kernel referenced by User::Kernel
 => Kernel 

My app doesn't have any IO or Kernel classes, except ruby default.

UPD3:

# app/models/user.rb
class User < ActiveRecord::Base
  has_many :files, class_name: 'User::File'
  ..
end

# app/models/user/file.rb
class User::File < ActiveRecord::Base
  belongs_to :user
  # some validations, nothing serious
end
kenko
  • 183
  • 1
  • 6

2 Answers2

29

Update: This years Christmas present was the release of Ruby 2.5.0 with which this error won't happen anymore. With Ruby 2.5+ you will either get the constant you asked for or an error. For older Ruby versions read on:

Your User::File class is not loaded. You have to require it (e.g. in user.rb).

The following happens when ruby/rails sees User::Info and evaluates it (simplified; only User is defined yet).

  • check if User::Info is defined - it is not (yet)
  • check if Info is defined - it is not (yet)
  • uninitialized constant -> do rails magic to find the user/info.rb file and require it
  • return User::Info

Now lets do it again for User::File

  • check if User::File is defined - it is not (yet)
  • check if File is defined - it is (because ruby has a built in File class)!
  • produce a warning, because we've been asked for User::File but got ::File
  • return ::File

We observe that the rails magic, that automatically requires files for (yet) unknown constants, does not work for User::File because File is not unknown.

tessi
  • 13,313
  • 3
  • 38
  • 50
  • 2
    Thanks for explanation. Adding require at the top of `user.rb` gives me error - `.../app/models/user.rb:2:in `': User is not a class (TypeError)`, but adding require at the end of file solves the problem. – kenko Jul 17 '13 at 14:52
  • Beautiful explanation. – Bala Jul 17 '13 at 15:44
  • If I require `user/file` from `User`, I get a circular dependency error because `User::File` refers to `User` – Jonathan Allard Aug 08 '15 at 22:12
  • I don't understand why `User::Info` should check that `Info` is defined. Returning `::File` instead of `User::File` seems wrong. I'd rather have an exception. It leads to weird behaviour : `My::Deeply::Nested::Dog` returns `Dog` the first time, and `NameError: uninitialized constant My::Deeply::Nested::Dog` the second time. See http://stackoverflow.com/questions/40928631/name-clash-with-top-level-constant-when-autoloading-with-rails/40930587?noredirect=1#comment69077891_40930587 – Eric Duminil Dec 02 '16 at 14:01
-1

Try referring to the class as User::File to distinguish it from regular ruby Files. You can use ::File to refer to those when it is ambiguous

Slicedpan
  • 4,995
  • 2
  • 18
  • 33