如何让 Rails 在应用程序代码*之后*加载插件?

发布于 2024-09-06 19:44:11 字数 370 浏览 4 评论 0原文

我正在尝试编写一个定义 MongoMapper 模型的插件。问题是,当我运行 script/console 时,出现以下错误:

/home/helder/.rvm/gems/ruby-1.8.7-p249/gems/mongo_mapper-0.8。 2/lib/mongo_mapper/connection.rb:29:in ``database':NameError: uninitialized class variable @@database_name in MongoMapper::Connection`

这让我认为它在设置数据库连接之前尝试加载我的插件模型。如何让它在应用程序代码的其余部分之后加载插件?

I'm trying to write a plugin that defines a MongoMapper model. The problem is that when I run script/console, I get this error:

/home/helder/.rvm/gems/ruby-1.8.7-p249/gems/mongo_mapper-0.8.2/lib/mongo_mapper/connection.rb:29:in
``database':NameError: uninitialized class variable @@database_name in MongoMapper::Connection`

which leads me to think that it's trying to load my plugin model before setting up the database connection. How do I make it load the plugin after the rest of my application code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

夕色琉璃 2024-09-13 19:44:11

我将尝试解决我遇到的错误以及标题中所述的一般问题。

具体错误

我弄清楚是什么问题了。正如我在上面的评论中所说,问题是Rails(2.3.8)在使用ActiveRecord时,首先设置数据库连接,然后加载gems,然后加载插件(按此顺序)。因此,如果您有任何插件需要在初始化期间访问数据库(即在插件的 init.rb 或该插件所需的其他文件 require 内),一切正常。

但是当使用 MongoMapper 时,Rails 会加载 MongoMapper 的以及所有其他 gem/插件,但不会建立其连接(Rails 不会这样做,并且插件本身也不会触发该连接) )。 MongoDB 文档目前推荐的方法是创建一个像这样的初始化器:

MongoMapper.connection = Mongo::Connection.new('localhost', 27017)
MongoMapper.database = "#myapp-#{Rails.env}"

if defined?(PhusionPassenger)
   PhusionPassenger.on_event(:starting_worker_process) do |forked|
     MongoMapper.connection.connect_to_master if forked
   end
end

但是由于插件是在运行 config/initializers 中的文件之前初始化的,如果您尝试定义 MongoMapper 模型,一旦它调用访问数据库(就像调用 key 类方法),BOOM。您收到问题中引用的错误。

解决此问题的一种方法是在 init.rb需要需要数据库的文件,而只是将它们添加到加载路径中(如果它们不在lib/app/models,Rails 自动添加到加载路径)。这样,只有在引用模型时,Rails 的自动类加载器才会需要模型,这通常位于应用程序代码中。到那时,数据库连接就已经建立了。当然,只有当您确实不需要在插件初始化期间引用这些类时,这才有效。否则,请继续阅读。

一般问题

如何让 Rails 在初始化代码之后加载插件?

在插件的 init.rb 上,抛出任何东西 需要在此块内等待:

config.after_initialize do
  # require my models
  # do this
  # do that
end

config 变量与传递给 config/ 内的 Rails::Initializer.run 的块相同environment.rb 文件,并且由 Rails 提供给您的插件的 init.rb,无需额外费用。

您放入该块中的任何内容都将被执行(通过 Rails::Initializer# after_initialize之后一切都完成加载并初始化,但之前任何请求进来。享受吧。

I will try to address both the error I encountered as well as the general question as stated in the title.

The specific error

I figured out what the problem was. As I said in a comment above, the problem is that Rails (2.3.8), when using ActiveRecord, first sets up the database connection, then loads gems, then plugins (in this order). So if you have any plugins that need to access the database during their initialization (that is, inside the plugin's init.rb or some other file required by this one), everything works fine.

But when using MongoMapper, Rails loads MongoMapper's classes together with all other gems/plugins, but doesn't set up its connection (Rails doesn't do it, and the plugin doesn't trigger that itself either). The way currently recommended on MongoDB's documentation is to create an initializer like this:

MongoMapper.connection = Mongo::Connection.new('localhost', 27017)
MongoMapper.database = "#myapp-#{Rails.env}"

if defined?(PhusionPassenger)
   PhusionPassenger.on_event(:starting_worker_process) do |forked|
     MongoMapper.connection.connect_to_master if forked
   end
end

But since plugins are initialized before files in config/initializers are run, if you try to define a MongoMapper model, as soon as it gets to a call that access the database (like a call to the key class method), BOOM. You get the error quoted in the question.

One way to solve this is to not require in init.rb the files that need the database, but rather only add them to the load path (if they're not in lib/ or app/models, which Rails automatically adds to the load path). That way, the models will only be required by Rails' auto class loader when they are referenced, which will usually be inside your application code. By then, the db connection will already be set up. Of course, this only works if you indeed don't need to reference these classes during your plugin's initialization. Otherwise, read on.

The general question

How to make Rails load a plugin after the initialization code?

On your plugin's init.rb, throw anything that needs waiting inside this block:

config.after_initialize do
  # require my models
  # do this
  # do that
end

That config variable is the same passed to the block which is passed to Rails::Initializer.run inside your config/environment.rb file, and is made available to your plugin's init.rb by Rails at no extra cost.

Anything you put inside that block will be executed (by Rails::Initializer#after_initialize) after everything is finished loading and getting initialized, but before any requests come in. Enjoy.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文