【问题标题】:Cannot load Ruby Model under a namespace无法在命名空间下加载 Ruby 模型
【发布时间】:2015-06-30 17:13:43
【问题描述】:

我有一个命名空间的 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 的 sn-p

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

博客模块sn-p

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 ]

【问题讨论】:

  • 你能从routes.rb中添加与博客文章相关的sn-p吗...
  • @VamsiKrishna 我添加了 routes.rb sn-p。
  • 所以博客不是你的例子
  • 是的,它不是一个模型,它只是一个带有上面显示的sn-p的模块。
  • rails 希望名称间距也反映在目录树中。这些文件在哪里?

标签: ruby-on-rails ruby


【解决方案1】:

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

module Foo
  class Bar
  end
end

然后尝试使用Foo::Bar,自动加载器首先尝试定位app/models/foo/bar.rb。文件被加载,module Foo 在这里定义(尽管作为一个模块只包含Bar),所以自动加载器永远不会尝试加载app/models/foo.rb

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

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

那么blog.rb中就不需要定义self.table_name_prefix了。

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

注意:

这个错误似乎已在 rails 4 中得到解决。我在 rails 3 上使用了第二个解决方法,但我尝试在 rails 4 中重现该错误,但它不再出现。我认为他们修改了自动加载器的工作方式...有关更多信息,请参阅the rails guides on autoloading and reloading constants

【讨论】:

  • 是的,我检查过,但没有运气。我可以知道为什么在“/app/models/blog/post.rb”而不是Blog::Post 上期待模型帖子。我尝试创建一个新的 rails 4.1 应用程序,在那里我尝试了与我在这里所做的相同的事情,但它在那里工作。像我上面的帖子那样预加载所有类会影响它吗?
  • 是同样的错误 Expected/app/models/blog/post.rb to define Post。
  • 检查此问题并在此查找 m_x 给出的答案。不是公认的答案。 stackoverflow.com/questions/8911046/…
  • 非常感谢 Vamsi,require_dependency 为我工作。抱歉,我赞成您的回答,但只有在我获得至少 15 名声望后才能看到。另外我想知道使用“require_dependency”除了包含在每个命名空间类中(在性能或rails自动加载方面)之外是否有任何缺点?
  • 我认为您应该能够接受答案。我没有看到任何与此相关的性能问题。
猜你喜欢
  • 2011-06-22
  • 2017-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-10
  • 2012-09-10
相关资源
最近更新 更多