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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3282655/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 22:55:57  来源:igfitidea点击:

Ruby on Rails 3 - Reload lib directory for each request

ruby-on-railsreloadrails-enginesruby-on-rails-3

提问by Nicolas Guillaume

I'm creating a new engine for a rails 3 application. As you can guess, this engine is in the lib directory of my application.

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

However, i have some problems developing it. Indeed, I need to restart my server each time I change something in the engine.

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

Is there a way to avoid this ?

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

Can I force rails to completely reload the lib directory or a specific file and his requirements for each request ?

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

Thanks for your help :)

谢谢你的帮助 :)

采纳答案by shime

TL;DR

TL; 博士

  • put this in config/application.rb

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

  • remove requirestatements for your libfiles

  • 把它放在config/application.rb 中

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

  • 删除lib文件的require语句

Go!

走!



Let me explain in detail.

让我详细解释一下。

I don't know why this answer is accepted, since it doesn't help with reloading lib folder on each request. First I thought that it works for Rails 2, but the question clearly states that it was for Rails 3 and the release date of 3.0.0 is before the date of the answer.

我不知道为什么这个答案被接受,因为它无助于在每个请求上重新加载 lib 文件夹。首先我认为它适用于 Rails 2,但问题明确指出它适用于 Rails 3,并且 3.0.0 的发布日期早于答案的日期。

Other answers seem over-complicated or don't provide a real solution.

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

I decided to investigate things a little, because it was bothering me and I've even found out that people have a workaround for this and it involves saving lib files inside app/modelsin development and then moving it to /libwhen done. We can do better, right?

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



My solution is tested against:

我的解决方案针对:

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

Put this into your config/application.rb:

把它放到你的config/application.rb

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

That's it!?

就是这样!?

Make sure you put it here since it will not workif you put it in config/environments/development.rb, for example.

确保你把它放在这里,因为如果你把它放进去,它就不起作用config/environments/development.rb,例如。

Make sure your remove all the requirestatements for your /libcode since requirestatements will also cause this solution to not work.

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



This code implicitly requires your code, so if you do environment checks (which are unnecessary) and instead of the above code, you decide to write something like this:

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

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

you should watch out on the old requirestatements, since they are still required on all the non-development environments, in this scenario.

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

So if you still decide to do environment checks, make sure you do inverse checks for require statements. Otherwise you'll get bitten!

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

require "beer_creator" unless Rails.env.development?

You might think that writing entire paragraph about something that's unnecessary is also unnecessary, but I think that warning people about something that's necessary when doing something unnecessary is also necessary.

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

If you would like to know more about this topic, check out this little tutorial.

如果您想了解有关此主题的更多信息,请查看此小教程

回答by Patrick Hogan

I couldn't get any of the above to work for me so I dug in the Rails code a bit and came up with this:

我无法让上述任何一项对我有用,所以我深入研究了 Rails 代码并想出了这个:

New file: config/initializers/reload_lib.rb

新文件: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

Yes, I know it's disgusting but it's a hack. There might be a better way to trigger a full reload, but this works for me. My specific use case was a Rack app mounted to a Rails route so I needed it to reload as I worked on it in development.

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

Basically what it does is it checks if any files in /lib have changed (modified timestamp) since last loaded and then triggers a reload if they change.

基本上它所做的是检查自上次加载以来 /lib 中的任何文件是否已更改(修改时间戳),然后在它们更改时触发重新加载。

I might also mention I have this in my config/application.rb

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

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

Which just by default makes sure everything in my lib directory gets loaded.

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

Yays!

是的!

回答by Paul Saladna

Since we are talking Rails, the easiest way is to 'require' your lib/* .rb files using 'require_dependency'. So long as the controller/helper/etc (.rb files under app/) uses require_dependency instead of just require reloading works, without the need to do anything funky.

由于我们在谈论 Rails,最简单的方法是使用“ require_dependency” “要求”您的 lib/* .rb 文件。只要控制器/助手/等(app/ 下的 .rb 文件)使用 require_dependency 而不是只需要重新加载工作,不需要做任何时髦的事情。

Before I went down that track, the only solution that worked was the one on hemju.com, but I really did not want to have to hack the ApplicationController for Dev speed.

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

回答by dishod

You have to add

你必须添加

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

to your Application class in config/application.rb

到 config/application.rb 中的 Application 类

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

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

回答by Jesse Wolgamott

In RAILS 3, here's the secret sauce to auto-reload lib files. The code below's a bit overkill, for the example, but it's what I did to make it work. You can change the message in YoYo#gogo and see it on the screen each page load. Remove out the initializer and it stays the same.

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

/config/initializers/lib_reload.rb (new file)

/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

/lib/yo_yo.rb

class YoYo
  def gogo
    "OH HAI THERE"
  end
end

/app/controllers/home_controller

/app/controllers/home_controller

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

回答by jeremiemv

Here is my version inspired from @pbhogan's answer that reloads all the ruby files in your rails /lib directory when any of those files is changed.

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

It also silences warnings to avoid messages regarding already initialized constants.

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

Works as of Rails 3.2.8

从 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

回答by Joshua Muheim

Updated answer

更新答案

I sum up all my findings on my blog, you better look there:

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

Old answer

旧答案

I looked around for a solution for this too, and (for completeness' sake and also to point others into this direction) here is what I found.

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

As of Rails3.1, engines can easily be generated through the command rails plugin new my_plugin --full. This generates the skeleton for an engine.

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

--fullmeans that the engine will be "merged" right into the including application, so that for example controllers should be directly accessible as if they were defined in the including app. This lets you e.g. have a helper file in my_engine/app/helpers/my_helper.rbthat will be merged right into your including app's app/helpers/my_helper.rb helper.

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

There's another option --mountablewhich creates a namespace for the engine so that its controllers etc. will never collide with the including application's ones. This results in e.g. a helper being in my_engine/app/helpers/my_engine/my_helper.rbwhich won't collide with a helper app/helpers/my_helper.rbin your including app.

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

Now the more interesting part:

现在更有趣的部分

Within the generated engine's testfolder, there's a dummyfolder which holds a complete Rails application! What's it for?

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

When you develop an engine, its functionalities are meant to work completely on their own, and it should also be tested completely on its own. So it's the "wrong" way to develop an engine "within" another Rails app (though this intuitively often will feel right when extracting existing functionalities from a Rails app into an engine), and so theoretically it is also not needed to reload an engine's code with every request to the including application.

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

The "right" way seems to be this: develop and test your engine, as if it were a full Rails app using the dummyapp! Therein you can do everything you can do in any "normal" Rails app, e.g. create controllers, models, views, etc. which use the functionalities the engine should provide. You also can normally start a server using rails sin your test/dummydirectory and access the dummy app on localhost:3000, and when running your tests, the dummyapp is automatically used for integration tests. Quite nice! :-)

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

You have to be careful where to put your stuff:

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

  • Any functionality that is meant to be used within another Rails app goes into my_engine/app, while any functionality that is only needed to test the engine's functionality goes into test/dummy/app.
  • 任何打算在另一个 Rails 应用程序中使用my_engine/app的功能都放在test/dummy/app.

Then afterwards you can easily load your engine in your main app's Gemfilelike this: gem 'my_engine', :path => 'path/to/my_engine'or publish it to GitHub as a gem.

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

(One interesting thing (and to come back to this topic's subject) is that when I start the dummy's server, then all changes in the engine seem to be reflected within it! So somehow it seems to be possible to include an engine within a Rails app without caching it...? I don't know how this happens.)

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

So to sum up: an engine provides functionality that can stand completely on its own, so it should also be developed and tested on its own. Then, when it has reached a stable state, it can be included by any other app that needs its functionality.

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

Here's some resources I find useful:

以下是我认为有用的一些资源:

I hope you find this answer useful. I'm still very new to engines in general, so if there's any wrong information, please tell me, and I'll correct it.

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

回答by Haris Krajina

Add to application_controller.rbor your base controller:

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

Worked for me.

对我来说有效。

回答by rndrfero

Worked for Rails 3.2.13 for reloading lib inside of gem of an app:

为 Rails 3.2.13 工作,用于在应用程序的 gem 内重新加载 lib:

require_dependency 'the_class'

require_dependency 'the_class'

AND

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

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

回答by vish

Also, make sure that you comment out the following line in application.rb (in addition to @dishod's solution), and make sure that your module name is the same as your file name (otherwise, rails won't be able to find it)

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

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