无法在命名空间下加载Ruby Model

时间:2015-06-29 17:19:18

标签: ruby-on-rails ruby

我有一个命名空间的Post控制器,如下所示

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

和命名空间模型如下。

class Blog::Post < ActiveRecord::Base
end

但是当我尝试在帖子控制器的索引动作中访问模型时,如下所示

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

我收到以下错误

LoadError at /admin/blog/posts

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

但是,当我将模型从Blog :: Post移动到Admin :: Blog :: Post命名空间时,它就可以了。

我对此感到困惑,无法了解这一点。 是否需要Controller和Model应存在于同一名称空间中?

以下是routes.rb

的摘录
namespace :admin do
    namespace :blog do
        resources :posts
        resources :categories
    end
end

博客模块代码段

module Blog
  def self.table_name_prefix
    'blog_'
  end
end

预加载控制器和模型

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 个答案:

答案 0 :(得分:0)

这可能是由rails的自动加载器引起的。这样做时:

module Foo
  class Bar
  end
end

然后尝试使用Foo::Bar,自动加载器首先尝试找到app/models/foo/bar.rb。该文件已加载,此处定义了module Foo(虽然该模块仅包含Bar),因此自动加载器永远不会尝试加载app/models/foo.rb

这应该只在开发模式下进行,因为在生产模式下,所有文件在启动时都是require

AFAIK有两种解决方法:

需要模块

使用require_dependency

require_dependency 'foo'
module Foo
  class Bar
  end
end

这是恕我直言的正确解决方案,因为它不会破坏常量查找,但它也有点烦人,因为你必须在每个命名空间文件的顶部添加require语句。

创建自定义有效记录库

此解决方案不依赖于自动加载。将模型设置为从以下内容继承,而不是直接从ActiveRecord::Base继承:

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

然后,无需在self.table_name_prefix中定义blog.rb

这个可以全部通过猴子修补ActiveRecord::Base完成,但这会干扰其他类,例如ActiveRecord::SchemaMigration,它没有表前缀。< / p>

注意:

这个bug似乎已经在rails 4中解决了。我在rails 3上使用了第二个解决方法,但是我试图重现rails 4中的bug并且它不再出现了。我认为他们修改了自动加载器的工作方式...有关详细信息,请参阅the rails guides on autoloading and reloading constants