Rails中嵌套模块破坏了table_name_prefix

4

我正试图在Rails应用程序中始终使用嵌套的模块/类定义,而不是紧凑的(::)语法。但是,它并不总是加载模块文件本身,该文件包含table_name_prefix

在Ruby 2.1.1上使用Rails 4.1.8...

rails new my_app
...
rails g scaffold User
rails g scaffold Blog::Post

这会创建一个app/models/blog.rb文件:
module Blog
  def self.table_name_prefix
    'blog_'
  end
end

似乎有很多意外的方法会阻止Rails自动加载blog.rb。 最简单的例子是通过helpers。

app/helpers/blog/posts_helper.rb改为:

module Blog::PostsHelper
end

to:

module Blog
  module PostsHelper
  end
end

启动服务器,访问/users,然后访问/blog/posts:
SQLite3::SQLException: no such table: posts: SELECT "posts".* FROM "posts"

类似问题还可能在其他地方出现,比如模型测试中。这不仅限于helpers。

最好的解决方法是明确加载blog.rb和任何其他命名空间模块吗?

我能够重现这个问题 - 谢谢Anthony。我会在Github上创建一个问题,看看团队会说什么。 - Anthony
也许...不过我更想找到确保blog.rb被加载的最佳方法,而不是建议改变Rails。显然,这不是许多人遇到的问题... - Anthony Smith
在Rails 6中(Zeitwerk自动加载程序),这似乎不再是一个问题。 - Kris
1个回答

0
一种不依赖于自动加载的解决方案是将模型设置为继承以下内容,而不是直接从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

可以通过lib/templates/active_record/model/model.rbmodule.rb中的适当文件设置这些内容为未来模型的默认值,基于默认的活动记录模板

这个过程可能可以通过猴子补丁ActiveRecord::Base完成,但这会干扰其他类,例如没有表前缀的ActiveRecord::SchemaMigration


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接