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

时间:2010-07-19 15:48:01

标签: ruby-on-rails reload rails-engines ruby-on-rails-3

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

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

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

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

感谢您的帮助:)

11 个答案:

答案 0 :(得分:48)

我无法让上述任何一个对我有用,所以我在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目录中的所有内容都已加载。

Yays!

答案 1 :(得分:32)

<强> TL; DR

  • 将此内容放入 config / application.rb

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

  • 删除 lib 文件的require语句

访问!


让我详细解释一下。

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

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

我决定稍微调查一下,因为它困扰我,我甚至发现人们有一个解决方法,它涉及在开发中的app/models内保存lib文件,然后将其移动到{完成后{1}}我们可以做得更好,对吧?


我的解决方案经过测试:

  • Rails 3.0.20
  • Rails 3.1.12
  • Rails 3.2.13
  • Rails 4.0.0.rc1

将其放入/lib

config/application.rb

就是这样!™

请确保将其放在此处,因为如果您将其放在# in config/application.rb config.eager_load_paths += ["#{Rails.root}/lib"] 中,将无法正常工作

请确保删除config/environments/development.rb代码的所有require语句,因为/lib语句也会导致此解决方案失效。


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

require

您应该注意旧的# in config/application.rb config.eager_load_paths += ["#{Rails.root}/lib"] if Rails.env.development? 语句,因为在这种情况下,所有非开发环境仍然需要它们。

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

require

您可能认为编写关于不必要的内容的整个段落也是不必要的,但我认为警告人们在做一些不必要的事情时所需要的东西也是必要的。

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

答案 2 :(得分:21)

由于我们正在谈论Rails,最简单的方法是使用' require_dependency ''要求'你的lib / * .rb文件。只要控制器/帮助器/ etc(app /下的.rb文件)使用require_dependency而不是只需要重新加载工作,而不需要做任何有趣的事情。

在我开始追踪之前,唯一有效的解决方案是hemju.com上的解决方案,但我真的不想破解ApplicationController的开发速度。

答案 3 :(得分:13)

你必须添加

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

答案 4 :(得分:3)

在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

<强> /应用/控制器/ home_controller

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

答案 5 :(得分:3)

这是我的版本灵感来自 @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

答案 6 :(得分:1)

更新回答

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

旧答案

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

从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应用程序中执行所有操作,例如:创建使用引擎应提供的功能的控制器,模型,视图等。您通常也可以使用rails s目录中的test/dummy启动服务器并访问localhost:3000上的虚拟应用,并在运行测试时自动使用dummy应用集成测试。相当不错! : - )

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

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

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

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

总而言之:引擎提供的功能可以完全独立,因此它也应该自行开发和测试。然后,当它达到稳定状态时,任何其他需要其功能的应用程序都可以包含它。

以下是我觉得有用的一些资源:

我希望你觉得这个答案很有用。我对发动机一般都是新手,所以如果有任何错误的信息,请告诉我,我会纠正它。

答案 7 :(得分:1)

添加到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

为我工作。

答案 8 :(得分:0)

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

此外,除非您明确地在其中添加内容,否则它似乎应为空。

答案 9 :(得分:0)

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

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

答案 10 :(得分:0)

为Rails 3.2.13工作,用于在app的gem中重新加载lib:

require_dependency'the_class'

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