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

发布于 2024-09-10 04:44:03 字数 202 浏览 5 评论 0原文

我正在为 Rails 3 应用程序创建一个新引擎。正如您所猜测的,该引擎位于我的应用程序的 lib 目录中。

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

有办法避免这种情况吗?

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

感谢您的帮助 :)

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.

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 ?

Thanks for your help :)

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

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

发布评论

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

评论(11

奢望 2024-09-17 04:44:03

我无法让上述任何内容为我工作,所以我深入研究了 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 目录中的所有内容。

耶!

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:

New file: 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.

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.

I might also mention I have this in my 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.

Yays!

╄→承喏 2024-09-17 04:44:03

TL;DR

  • 将其放入 config/application.rb

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

  • 删除lib文件的require语句

Go!


让我详细解释一下。

我不知道为什么这个答案被接受,因为它对在每个请求上重新加载 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?

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

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

TL;DR

  • put this in config/application.rb

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

  • remove require statements for your lib files

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.

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/models in development and then moving it to /lib when done. We can do better, right?


My solution is tested against:

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

Put this into your 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 work if you put it in config/environments/development.rb, for example.

Make sure your remove all the require statements for your /lib code since require statements will also cause this solution to not work.


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 require statements, since they are still required on all the non-development environments, in this scenario.

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

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.

叫嚣ゝ 2024-09-17 04:44:03

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

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

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.

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.

你与昨日 2024-09-17 04:44:03

您必须添加

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

You have to add

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

to your Application class in config/application.rb

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

谷夏 2024-09-17 04:44:03

在 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

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.

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

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
花桑 2024-09-17 04:44:03

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

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.

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

Works as of 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
┈┾☆殇 2024-09-17 04:44:03

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

为我工作。

Add to application_controller.rb or your base controller:

  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.

迷迭香的记忆 2024-09-17 04:44:03

更新答案

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

旧答案

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

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

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

还有另一个选项--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 启动服务器,并在 localhost:3000 上访问虚拟应用程序,并且在运行时您的测试中,dummy 应用程序会自动用于集成测试。相当不错! :-)

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

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

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

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

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

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

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

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.

--full means 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.rb that will be merged right into your including app's app/helpers/my_helper.rb helper.

There's another option --mountable which 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.rb which won't collide with a helper app/helpers/my_helper.rb in your including app.

Now the more interesting part:

Within the generated engine's test folder, there's a dummy folder which holds a complete Rails application! What's it for?

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.

The "right" way seems to be this: develop and test your engine, as if it were a full Rails app using the dummy app! 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 s in your test/dummy directory and access the dummy app on localhost:3000, and when running your tests, the dummy app is automatically used for integration tests. Quite nice! :-)

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.

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

(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.)

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.

风筝在阴天搁浅。 2024-09-17 04:44:03

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

另外,它似乎应该是空的,除非你明确地在其中放入了一些东西。

note that in Rails 3 "load_once_paths" becomes "autoload_once_paths."

Also, it appears that it should be empty unless you explicitly put something in it.

橙幽之幻 2024-09-17 04:44:03

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

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

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)

#Dir.glob("./lib/*.{rb}").each { |file| require file } # require each file from lib directory
嘴硬脾气大 2024-09-17 04:44:03

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

require_dependency 'the_class'

AND

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

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

require_dependency 'the_class'

AND

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

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