ActiveRecord bug with namespaces or expected behavior?

178 Views Asked by At

Rails 4.1.16

Ruby 2.2.7

I have this ActiveRecord model:

class Something::Type < ActiveRecord::Base

When referencing the model like this:

Something::Type.find_by_whatever("test")

I get the following error:

NoMethodError: undefined method `find_by_whatever' for ActiveRecord::AttributeMethods::Serialization::Type:Class

I understand that Ruby constants are broken up, so that Type exists as its own constant, and the autoloader is finding ActiveRecord::AttributeMethods::Serialization::Type first.

However, referencing the namespace in an "absolute" way (prefixing with colons) is supposed to solve the issue, but the result is the same. Any ideas why?

::Something::Type.find_by_whatever("test")

NoMethodError: undefined method `find_by_whatever' for ActiveRecord::AttributeMethods::Serialization::Type:Class
1

There are 1 best solutions below

1
max On

The problem when you define a class with the scope resultion operator is that the module nesting is resolved to the point of definition (the point where you use the module keyword). If you look at the module nesting:

class Something::Type < ActiveRecord::Base
  puts Module.nesting.inpsect # [Something::Type]
end

The class not even really nested in the Something module. Which will give you an extremely surprising constant lookup:

module Something
  FOO = "test"
end

class Something::Type
  puts Foo # gives a uninitialized constant error since its not looking in the Something module
end

Instead you should ALWAYS declare namespaced classes with explicit nesting:

module Something
  class Type
    puts Module.nesting.inspect # [Something::Type, Something]
    puts Foo  # test
  end
end

Which gives the module nesting [Something::Type, Something] which means that it will properly look up constants in the same Something namespace.

This is something that the old classic autoloader tended to gloss over as it relied on monkeypatching Module#const_missing. Zeitwork does not so do it right.