Ruby on Rails 3 - 为每个请求重新加载 lib 目录

Posted

技术标签:

【中文标题】Ruby on Rails 3 - 为每个请求重新加载 lib 目录【英文标题】:Ruby on Rails 3 - Reload lib directory for each request 【发布时间】:2011-03-17 23:52:28 【问题描述】:

我正在为 rails 3 应用程序创建一个新引擎。你可以猜到,这个引擎在我的应用程序的 lib 目录中。

但是,我在开发它时遇到了一些问题。确实,每次更改引擎中的某些内容时,我都需要重新启动服务器。

有没有办法避免这种情况?

我可以强制 rails 完全重新加载 lib 目录或特定文件以及他对每个请求的要求吗?

感谢您的帮助:)

【问题讨论】:

【参考方案1】:

我无法让上述任何方法为我工作,所以我在 Rails 代码中挖掘了一下并想出了这个:

新文件:config/initializers/reload_lib.rb

if Rails.env == "development"
  lib_reloader = ActiveSupport::FileUpdateChecker.new(Dir["lib/**/*"]) do
    Rails.application.reload_routes! # or do something better here
  end

  # For Rails 5.1+
  ActiveSupport::Reloader.to_prepare do
    lib_reloader.execute_if_updated
  end

  # For Rails pre-5.1 
  ActionDispatch::Callbacks.to_prepare do
    lib_reloader.execute_if_updated
  end

end

是的,我知道这很恶心,但它是一种黑客行为。可能有更好的方法来触发完全重新加载,但这对我有用。我的具体用例是安装在 Rails 路由上的 Rack 应用程序,因此我需要在开发过程中重新加载它。

基本上它的作用是检查 /lib 中的任何文件自上次加载以来是否发生了变化(修改了时间戳),如果它们发生变化则触发重新加载。

我可能还会提到我的 config/application.rb 中有这个

config.autoload_paths += %W(#config.root/lib)
config.autoload_paths += Dir["#config.root/lib/**/"]

这只是默认情况下确保我的 lib 目录中的所有内容都被加载。

耶!

【讨论】:

优秀 - 如果您从 gem 继承,效果很好。我将它与 Grape 一起使用(例如 /lib/appname/api.rbAppname::API < Grape::API 开头)并且它有效(而 autoload_paths 修复无效)。 @pbhogan FileUpdateChecker#initialize 的方法签名在 Rails 3.2.x 中有所改变。摆脱true,一切都会恢复正常。 这对我来说也适用于 Grape gem,谢谢@nfm。但是,我确实必须将 FileUpdateChecker 语法稍微更改为:ActiveSupport::FileUpdateChecker.new([], Dir["lib/**/*"]) 有人可以编辑这篇文章并解释如何从 lib 目录重新加载文件吗?当前示例仅重新加载路由(如果我没看错的话),这并不能回答问题。 在 Rails 5.1+ 中,ActionDispatch::Callbacks.to_prepare 已弃用,取而代之的是 ActiveSupport::Reloader.to_prepare【参考方案2】:

TL;DR

把它放在 config/application.rb

config.eager_load_paths += ["#Rails.root/lib"]

为您的 lib 文件删除 require 语句

去吧!


让我详细解释一下。

我不知道为什么接受这个答案,因为它对在每个请求上重新加载 lib 文件夹没有帮助。首先我认为它适用于 Rails 2,但问题清楚地表明它适用于 Rails 3,并且 3.0.0 的发布日期在答案日期之前。

其他答案似乎过于复杂或无法提供真正的解决方案。

我决定稍微调查一下,因为这让我很困扰,我什至发现人们有一个解决方法,它包括在开发中将 lib 文件保存在 app/models 中,然后将其移动到 /lib完成后。我们可以做得更好,对吧?


我的解决方案已经过测试:

Rails 3.0.20 Rails 3.1.12 Rails 3.2.13 Rails 4.0.0.rc1

把这个放到你的config/application.rb:

# in config/application.rb
config.eager_load_paths += ["#Rails.root/lib"]

就是这样!™

确保将其放在此处,因为如果将其放在 config/environments/development.rb 中,它将不起作用

确保删除 /lib 代码的所有 require 语句,因为 require 语句也会导致此解决方案不起作用。


这段代码隐含地需要你的代码,所以如果你做环境检查(这是不必要的),而不是上面的代码,你决定写这样的东西:

# in config/application.rb
config.eager_load_paths += ["#Rails.root/lib"] if Rails.env.development?

您应该注意旧的 require 语句,因为在这种情况下,所有非开发环境仍然需要它们。

因此,如果您仍然决定进行环境检查,请确保对 require 语句进行反向检查。不然会被咬!

require "beer_creator" unless Rails.env.development?

你可能认为写整段关于不必要的事情也是不必要的,但我认为在做不必要的事情时警告人们一些必要的事情也是必要的。

如果您想了解有关此主题的更多信息,请查看this little tutorial。

【讨论】:

那是前一阵子了,你也可以在没有 eager_load_path 的情况下使用 load。创建将字符串作为参数的助手,如果 env 是 dev 则使用 load ,如果 env 是 prod 则使用 require 。如果您尝试这样做,请告诉我它是否有效:) 在 Rails 3.0.20 上不管有没有字符串末尾的额外 '' 似乎都不起作用。 这在 3.2.13 上对我不起作用——相反,在添加了 eager_load_paths 之后,我的所有其他文件(控制器 + 模型)也停止在请求之间重新加载! (如果我删除那条线,就会恢复正常。)奇怪。我终于得到了这个答案:***.com/a/10954365/1164573 你能让它在 Rails 4.1 中工作吗?我有一个question (with bounty) about this here,但还没有找到答案。 删除 require 语句会导致以下错误 NameError (uninitialized constant ...【参考方案3】:

添加到application_controller.rb 或您的基本控制器:

  before_filter :dev_reload if Rails.env.eql? 'development'

  def dev_reload
    # add lib files here
    ["rest_client.rb"].each do |lib_file|
      ActiveSupport::Dependencies.load_file lib_file
    end
  end

为我工作。

【讨论】:

【参考方案4】:

适用于 Rails 3.2.13,用于在应用程序的 gem 中重新加载 lib:

require_dependency 'the_class'

config.autoload_paths += %W(#config.root/../fantasy/lib)

【讨论】:

【参考方案5】:

更新答案

我在我的博客上总结了我所有的发现,你最好看看那里:

http://lsd4coders.muheimwebdesign.ch/2012/09/how-to-develop-rails-3-1-engines-with-ease-part-i-the-engine/ http://lsd4coders.muheimwebdesign.ch/2012/09/how-to-develop-rails-3-1-engines-with-ease-part-ii-the-gem/

旧答案

我也四处寻找解决方案,并且(为了完整起见并为其他人指出这个方向)这就是我发现的。

从 Rails3.1 开始,可以通过命令rails plugin new my_plugin --full 轻松生成引擎。这会生成引擎的骨架。

--full 表示引擎将直接“合并”到包含应用程序中,因此例如控制器应该可以直接访问,就好像它们是在包含应用程序中定义的一样。这让你例如在my_engine/app/helpers/my_helper.rb 中有一个帮助文件,它将直接合并到您的包含应用程序的app/helpers/my_helper.rb helper 中。

还有另一个选项--mountable 为引擎创建一个命名空间,这样它的控制器等就不会与包含应用程序的控制器发生冲突。这导致例如my_engine/app/helpers/my_engine/my_helper.rb 中的助手不会与包含应用程序中的助手 app/helpers/my_helper.rb 发生冲突。

现在是更有趣的部分

在生成引擎的test 文件夹中,有一个dummy 文件夹,其中包含一个完整的Rails 应用程序!有什么用?

当您开发引擎时,其功能应完全独立运行,并且还应完全独立测试。因此,在另一个 Rails 应用程序“内”开发引擎是“错误”的方式(尽管在将现有功能从 Rails 应用程序提取到引擎中时,直觉上通常感觉是正确的),因此理论上也不需要重新加载引擎的包含对包含应用程序的每个请求的代码。

“正确”的方法似乎是这样:开发和测试您的引擎,就好像它是一个使用 dummy 应用程序的完整 Rails 应用程序一样!在其中,您可以在任何“普通”Rails 应用程序中做所有可以做的事情,例如创建使用引擎应提供的功能的控制器、模型、视图等。您通常也可以使用test/dummy 目录中的rails s 启动服务器并访问localhost:3000 上的虚拟应用程序,并且在运行测试时,dummy 应用程序会自动用于集成测试。相当不错! :-)

你必须小心把你的东西放在哪里:

任何打算在另一个 Rails 应用程序中使用的功能都进入my_engine/app,而任何只需要测试引擎功能的功能都进入test/dummy/app

然后,您可以轻松地将引擎加载到主应用的 Gemfile 中,如下所示:gem 'my_engine', :path => 'path/to/my_engine' 或将其作为 gem 发布到 GitHub。

(一件有趣的事情(回到这个主题的主题)是,当我启动虚拟服务器时,引擎中的所有更改似乎都反映在其中!所以不知何故,似乎可以包含一个引擎在 Rails 应用程序中而不缓存它...?我不知道这是怎么发生的。)

所以总结一下:一个引擎提供的功能可以完全独立于自己,所以它也应该自己开发和测试。然后,当它达到稳定状态时,它可以被任何其他需要其功能的应用程序包含。

这里有一些我觉得有用的资源:

Developing a RubyGem using Bundler Mountable Engines RailsCast Book "Rails 3 in Action"

我希望这个答案对您有用。总的来说,我对引擎还是很陌生,所以如果有任何错误信息,请告诉我,我会纠正它。

【讨论】:

【参考方案6】:

这是我的版本,灵感来自 @pbhogan 的回答,当任何这些文件发生更改时,它会重新加载 rails /lib 目录中的所有 ruby​​ 文件。

它还会使警告静音以避免有关已初始化常量的消息。

适用于 Rails 3.2.8

if Rails.env.development?

  lib_ruby_files = Dir.glob(File.join("lib/**", "*.rb"))
  lib_reloader ||= ActiveSupport::FileUpdateChecker.new(lib_ruby_files) do
    lib_ruby_files.each do |lib_file|
      silence_warnings  require_dependency(lib_file) 
    end
  end

  ActionDispatch::Callbacks.to_prepare do
    lib_reloader.execute_if_updated
  end

end

【讨论】:

【参考方案7】:

另外,请确保在 application.rb 中注释掉以下行(除了@dishod 的解决方案),并确保您的模块名称与您的文件名相同(否则,rails 将无法找到它)

#Dir.glob("./lib/*.rb").each  |file| require file  # require each file from lib directory

【讨论】:

【参考方案8】:

既然我们在谈论 Rails,最简单的方法是使用“require_dependency”来“要求”您的 lib/* .rb 文件。只要controller/helper/etc(app/下的.rb文件)使用require_dependency而不是仅仅要求重新加载就可以了,不需要做任何时髦的事情。

在我走上这条路之前,唯一有效的解决方案是hemju.com 上的解决方案,但我真的不想为了开发速度而破解 ApplicationController。

【讨论】:

这似乎也对我有用。太糟糕了,它不能只使用普通的需求...... 与 Rails 3.2.6 配合得很好。这绝对是最好和最干净的答案。【参考方案9】:

在 RAILS 3 中,这是自动重新加载 lib 文件的秘诀。例如,下面的代码有点矫枉过正,但这是我为使其工作所做的。您可以更改 YoYo#gogo 中的消息,并在每个页面加载时在屏幕上看到它。删除初始化程序,它保持不变。

/config/initializers/lib_reload.rb(新文件)

ActiveSupport::Dependencies.explicitly_unloadable_constants << 'YoYo'
ActiveSupport::Dependencies.autoload_once_paths.delete(File.expand_path(File.dirname(__FILE__))+'/lib')

/lib/yo_yo.rb

class YoYo
  def gogo
    "OH HAI THERE"
  end
end

/app/controllers/home_controller

require 'yo_yo'
class HomeController < ApplicationController
  def index
    @message = YoYo.new.gogo
  end
end

【讨论】:

我相信在 Rails 3 中它是 autoload_once_paths 而不是 load_once_paths 最佳答案,但您可以使用 "#Rails.root/lib" 缩短 File.expand_path(File.dirname(FILE))+'/lib' 和还放了一个 if Rails.env.development?检查【参考方案10】:

请注意,在 Rails 3 中,“load_once_paths”变成了“autoload_once_paths”。

此外,它似乎应该是空的,除非你明确地把东西放进去。

【讨论】:

【参考方案11】:

你必须添加

config.autoload_paths += %W(#config.root/lib)

到 config/application.rb 中的 Application 类

https://rails.lighthouseapp.com/projects/8994/tickets/5218-rails-3-rc-does-not-autoload-from-lib

【讨论】:

最好放在 development.rb 中 这允许我在 lib 中的文件被 autoloaded (当您以相同的名称访问常量时加载),但似乎无助于使它们成为 在每个请求/修改时重新加载。我在 Rails 3.2.3 上。 是的,lib 中的此代码文件会自动加载但不会重新加载,但是当您将文件放在其他地方时它们会重新加载:$:.unshift(config.root); config.autoload_paths += %w(app/models/misc) 我认为我的 lib 文件是业务逻辑,我把业务逻辑也进入我的模型,所以它们是我的模型但不是 db-persisted,它们在 app/models/misc 中而不是在 lib 中是正确的。

以上是关于Ruby on Rails 3 - 为每个请求重新加载 lib 目录的主要内容,如果未能解决你的问题,请参考以下文章

Ruby on Rails,如何确定请求是由机器人还是搜索引擎蜘蛛制作的?

ruby on rails 中的控制器和动作有啥区别?

Rails Guides - Ruby on Rails 安全指南

让 Ruby-on-Rails 6/Webpack + Bootstrap 一起正常工作的问题

在 Ruby on Rails 3 应用程序中实现搜索?

ruby on rails创建的页面訪问很慢