我正在为 rails 3 应用程序创建一个新引擎。你可以猜到,这个引擎在我的应用程序的 lib 目录中。
但是,我在开发它时遇到了一些问题。确实,每次更改引擎中的某些内容时,我都需要重新启动服务器。
有没有办法避免这种情况?
我可以强制 rails 完全重新加载 lib 目录或特定文件以及他对每个请求的要求吗?
谢谢你的帮助 :)
我正在为 rails 3 应用程序创建一个新引擎。你可以猜到,这个引擎在我的应用程序的 lib 目录中。
但是,我在开发它时遇到了一些问题。确实,每次更改引擎中的某些内容时,我都需要重新启动服务器。
有没有办法避免这种情况?
我可以强制 rails 完全重新加载 lib 目录或特定文件以及他对每个请求的要求吗?
谢谢你的帮助 :)
我无法让上述任何一项为我工作,所以我在 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 目录中的所有内容都被加载。
耶!
由于我们在谈论 Rails,最简单的方法是使用“ require_dependency ”“要求”你的 lib/* .rb 文件。只要controller/helper/etc(app/下的.rb文件)使用require_dependency而不是仅仅需要重新加载就可以了,不需要做任何时髦的事情。
在我走这条路之前,唯一有效的解决方案是hemju.com上的解决方案,但我真的不想为了开发速度而破解 ApplicationController。
你必须添加
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
在 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
这是我的版本灵感来自@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
添加到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
为我工作。
更新的答案
我在我的博客上总结了我所有的发现,你最好看看那里:
旧答案
我也四处寻找解决方案,并且(为了完整起见,也将其他人指向这个方向)这就是我发现的。
从 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
应用程序会自动用于集成测试。相当不错!:-)
你必须小心把你的东西放在哪里:
my_engine/app
的功能都进入test/dummy/app
.然后,您可以Gemfile
像这样轻松地将引擎加载到您的主应用程序中:gem 'my_engine', :path => 'path/to/my_engine'
或将其作为 gem 发布到 GitHub。
(一件有趣的事情(回到本主题的主题)是,当我启动虚拟服务器时,引擎中的所有更改似乎都反映在其中!所以不知何故,似乎可以在 Rails 中包含一个引擎应用程序没有缓存它...?我不知道这是怎么发生的。)
所以总结一下:一个引擎提供的功能可以完全独立于自己,所以它也应该自己开发和测试。然后,当它达到稳定状态时,它可以被任何其他需要其功能的应用程序包含。
这里有一些我觉得有用的资源:
我希望你觉得这个答案有用。总的来说,我对引擎还是很陌生,所以如果有任何错误信息,请告诉我,我会纠正它。
请注意,在 Rails 3 中,“load_once_paths”变成了“autoload_once_paths”。
此外,除非您明确将某些内容放入其中,否则它似乎应该是空的。
另外,请确保您在 application.rb 中注释掉以下行(除了@dishod 的解决方案),并确保您的模块名称与您的文件名相同(否则,rails 将无法找到它)
#Dir.glob("./lib/*.{rb}").each { |file| require file } # require each file from lib directory
为 Rails 3.2.13 工作,用于在应用程序的 gem 中重新加载 lib:
需要依赖 'the_class'
和
config.autoload_paths += %W(#{config.root}/../fantasy/lib)