0

I have a namespaced Post controller as below

class Admin::Blog::PostsController < Admin::BaseController
end

and a namespaced model as follows.

class Blog::Post < ActiveRecord::Base
end

But when I try to access the model inside the index action of the post controller as below

def index
    @posts = Blog::Post.where(:foo_id => params[:id]).paginate(:page => params[:page], :per_page => 20)
  end

I get the following error

LoadError at /admin/blog/posts

Expected/app/models/blog/post.rb to define Post

But when I move the model to Admin::Blog::Post namespace from Blog::Post is works.

I'm bit confused with this and not able to get what is going on with this. Is it required that Controller and Model should be present in the same namespace ?

Following is the snippet from routes.rb

namespace :admin do
    namespace :blog do
        resources :posts
        resources :categories
    end
end

Blog module snippet

module Blog
  def self.table_name_prefix
    'blog_'
  end
end

Preloading controllers and models

config.autoload_paths += Dir["#{Rails.root}/app/models/**/**"]
    config.autoload_paths += Dir["#{Rails.root}/app/controllers/**/**"]
    config.autoload_paths += Dir["#{config.root}/app/helpers/**/**"]
    config.autoload_paths += Dir["#{config.root}/app/tags/**/**"]
    config.autoload_paths += %W[ #{Rails.root}/app/extensions #{Rails.root}/app/modules #{Rails.root}/app/drops #{Rails.root}/app/filters  #{Rails.root}/app/mailers ]

1 Answers1

2

This is probably caused by rails' autoloader. When doing this :

module Foo
  class Bar
  end
end

And then trying to use Foo::Bar, the autoloader first tries to locate app/models/foo/bar.rb. The file is loaded, and module Foo is defined here (albeit as a module containing solely Bar) so the autoloader never attempts to load app/models/foo.rb.

This should only happen in development mode, as in production mode all of your files are require'd on startup.

There are two workarounds AFAIK :

Require the module

using require_dependency :

require_dependency 'foo'
module Foo
  class Bar
  end
end

This is IMHO the right solution, as it does not break the constant lookup, but it is also a bit annoying as you have to add the require statement on top of each namespaced file.

Create Custom Active record Base

This solution doesn't rely on autoloading. Set the models to inherit from the following, instead of from ActiveRecord::Base directly:

class CustomActiveRecordBase < ActiveRecord::Base
  self.abstract_class = true

  # If no table name prefix has been defined, include the namespace/module as
  # table name prefix, e.g., Blog:: -> blog_
  def self.table_name
    # If a table_name_prefix has been defined, follow default behaviour
    return super if full_table_name_prefix.present?

    # Find the prefix, e.g., Blog::Post -> 'blog', User -> ''
    prefix = model_name.name.deconstantize.underscore

    # If no prefix, follow default behaviour
    return super unless prefix.present?

    # Otherwise add the prefix with an underscore
    "#{prefix}_#{super}"
  end
end

Then there is no need to define self.table_name_prefix in blog.rb.

This could all be done by monkey-patching ActiveRecord::Base, but this interferes with other classes, such as ActiveRecord::SchemaMigration, which doesn't have a table prefix.

Note :

This bug seems to have been resolved in rails 4. I used the second workaround a lot while on rails 3, but I've tried to reproduce the bug in rails 4 and it does not show up anymore. I think they modified the way the autoloader works... For more info, see the rails guides on autoloading and reloading constants

Vamsi Krishna
  • 3,742
  • 4
  • 20
  • 45
  • Yes I checked that but no luck. May I know like why is it expecting for model Post at "/app/models/blog/post.rb" instead of Blog::Post. I tried to create a new rails 4.1 app and there I just tried the same thing what I have done here, but it worked over there. Does preloading all the classes like in my post above affects it ? – Nitin Satish Salunke Jun 30 '15 at 06:21
  • Yes same error Expected/app/models/blog/post.rb to define Post. – Nitin Satish Salunke Jun 30 '15 at 06:28
  • Check this question and look for tha answer given by m_x in this. Not the accepted answer. http://stackoverflow.com/questions/8911046/rails-table-name-prefix-missing – Vamsi Krishna Jun 30 '15 at 06:31
  • Thanks a lot Vamsi, require_dependency worked for me. Sorry I had upvoted your answer but it will be visible only after me gaining minimum 15 reputation. Also I wanted to know if using "require_dependency" has any disadvantage other than including it in every namespaced class (in terms of performance or rails autoloading) ? – Nitin Satish Salunke Jun 30 '15 at 18:50
  • I think you should be able to accept the answer though. I don't see any performance issue regarding the same. – Vamsi Krishna Jul 01 '15 at 06:48