ActiveRecord Model Incorrectly Accessible From Arbitrary Module #8396

webmonarch opened this Issue Dec 1, 2012 · 4 comments


None yet
4 participants

When I create a model class, say ::NewModel, I can access it from an arbitrary unrelated Module once, then on the second access, it fails...

For example:

'This works...dont know why';
NewModel(id: integer, created_at: datetime, updated_at: datetime)

'This rightfully fails';
NameError: uninitialized constant ActiveSupport::NewModel
    from (irb):7
    from /home/ewebb/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/railties-3.2.9/lib/rails/commands/console.rb:47:in `start'
    from /home/ewebb/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/railties-3.2.9/lib/rails/commands/console.rb:8:in `start'
    from /home/ewebb/.rbenv/versions/1.9.3-p327/lib/ruby/gems/1.9.1/gems/railties-3.2.9/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

Using I wasn't able to repro this outside of Rails. A full script for repro is below:

rails new bug

cd bug
rails g model NewModel
rake db:migrate

echo "
'This works...dont know why';

'This rightfully fails';
::ActiveSupport::NewModel" | rails c


$ ruby -v
ruby 1.9.3p327 (2012-11-10 revision 37606) [x86_64-linux]

$ bundle show | grep '* rails'
  * rails (3.2.9)

senny commented Dec 2, 2012

This is a result of how the rails autoloading tries to mimic the real constant lookup but it's not exactly the same. AS I see it the issue is caused from the line klass_name = name.presence || "Object" which will differ when the class was already loaded (the second time you try to access the constant). The first time, the lookup path will include the root Object which will have the constant defined.

I'm not that familiar with the internals of the constant autoloading. I think @fxn can clarify the situation.


benolee commented Dec 9, 2012

I'm not sure what the expected behavior is here.

The first time ActiveSupport::NewModel is evaluated, ActiveSupport::Dependencies.load_missing_constant tries to find the qualified constant ActiveSupport::NewModel by looking for a file in "active_support/new_model" and, failing that, it calls const_missing on ActiveSupport's parent, i.e. Object. Now, ActiveSupport::Dependencies.load_missing_constant looks for the file "new_model" and succeeds here.

However, the second time ActiveSupport::NewModel is evaluated, this line fails because local_const_defined?(Object, :NewModel) is true. So it raises NameError.

The question seems to be: is it unexpected behavior to autoload the missing constant from the initial module's parents (especially Object)? The comments in load_missing_constant make it seem like this is intentional and expected behavior. So I'm not sure.


fxn commented Dec 18, 2012

This is a know mismatch between dependencies.rb and Ruby constant resolution.

Dependencies is based on const_missing and Ruby does not tell the hook whether the constant that triggered the call was missing in a relative or absolute lookup. In the first case AS assumes the situation is

module ActiveSupport

and it works because in such situation the top-level scope (Object) is checked.

In the second call, dependencies.rb is able to conclude that the reference is absolute, because it does a path walk upwards checking for the constant. If the constant is present, then the algorithm must be absolute, because if it was relative const_missing would have not been triggered in the first place.

The most important point here is to understand that AS does not emulate Ruby constant resolution algorithms because it lacks information. So AS does not pretend to. What AS offers is a contract, if the code follows the contract, then it works.


fxn commented Dec 24, 2012

I believe we can close this one, thanks for the report anyway.

@fxn fxn closed this Dec 24, 2012

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment