Rails 5:在生产中加载lib文件
Rails 5: Load lib files in production
我已将我的一个应用程序从 Rails 4.2.6 升级到 Rails 5.0.0。 Upgrade Guide 表示,自动加载功能现在在生产中默认禁用。
现在我的生产服务器上总是出现错误,因为我在 application.rb
文件中使用自动加载加载了所有 lib 文件。
module MyApp
class Application < Rails::Application
config.autoload_paths += %W( lib/ )
end
end
目前,我已将 config.enable_dependency_loading
设置为 true
,但我想知道是否有更好的解决方案。默认情况下在生产中禁用自动加载肯定是有原因的。
There must be a reason that Autoloading is disabled in production by
default.
这里对这个问题进行了长时间的讨论。 https://github.com/rails/rails/issues/13142
由于线程安全,自动加载在生产环境中被禁用。感谢@Зелёный 的 link.
我按照 Github 上的建议,通过将 lib 文件存储在 app
目录中的 lib
文件夹中解决了这个问题。 app
文件夹中的每个文件夹都由 Rails 自动加载。
移动到 Rails 5 后我的更改列表:
- 将
lib
目录放入 app
因为应用程序中的所有代码在开发中都是 自动加载的 并且 急切加载的 在产品中,最重要的是 autoreloaded 在开发中,因此您不必在每次进行更改时都重新启动服务器。
- 删除
lib
中指向您自己的 类 的所有 require
语句,因为如果它们的 file/dir 命名正确,并且如果您离开 require
声明它可以中断自动重新加载。更多信息 here
- 在所有环境中设置
config.eager_load = true
以在开发中热切地查看代码加载问题
- 在使用线程之前使用
Rails.application.eager_load!
以避免 "circular dependency" 错误。
如果您有任何 ruby/rails 扩展,则将该代码保留在旧的 lib
目录中,并从初始化程序中手动加载它们。这将确保在您可以依赖它的进一步逻辑之前加载扩展:
# config/initializers/extensions.rb
Dir["#{Rails.root}/lib/ruby_ext/*.rb"].each { |file| require file }
Dir["#{Rails.root}/lib/rails_ext/*.rb"].each { |file| require file }
对于任何像我一样为此苦苦挣扎的人来说,仅仅在 app/
下放置一个目录是不够的。是的,你会得到自动加载,但不是必需的 reloading, which requires namespacing conventions to be fulfilled。
此外,使用初始化程序加载旧的根级别 lib
将阻止在开发过程中重新加载功能。
我只是使用 config.eager_load_paths
而不是 config.autoload_paths
就像在 github 评论中提到的 akostadinov:
https://github.com/rails/rails/issues/13142#issuecomment-275492070
# config/application.rb
...
# config.autoload_paths << Rails.root.join('lib')
config.eager_load_paths << Rails.root.join('lib')
它适用于开发和生产环境。
感谢 Johan 建议将 #{Rails.root}/lib
替换为 Rails.root.join('lib')
!
将 lib 文件夹移动到应用程序帮助解决了一个问题,我的 Twitter api 不会 运行 在生产中。我有 "uninitialized constant TwitterApi",我的 Twitter API 在我的 lib 文件夹中。
我的 application.rb 中有 config.autoload_paths += Dir["#{Rails.root}/app/lib"]
,但在移动文件夹之前它不起作用。
这成功了
这允许自动重新加载库,并且也可以在生产环境中工作。
P.S。我已经更改了我的答案,现在它添加到两个 eager- 自动加载路径,无论环境如何,都允许在自定义环境中工作(如舞台)
# config/initializers/load_lib.rb
...
config.eager_load_paths << Rails.root.join('lib')
config.autoload_paths << Rails.root.join('lib')
...
从某种意义上说,这里是 Rails 5 中的统一方法,用于集中预加载和自动加载配置,同时它会在配置预加载时添加所需的自动加载路径,否则将无法正常工作:
# config/application.rb
...
config.paths.add Rails.root.join('lib').to_s, eager_load: true
# as an example of autoload only config
config.paths.add Rails.root.join('domainpack').to_s, autoload: true
...
总结 Lev 的回答:mv lib app
足以让我的所有 lib
代码自动加载/自动重新加载。
(rails 6.0.0beta3 但在 rails 5.x 上应该也能正常工作)
只要在config/application.rb文件中将config.autoload_paths改成config.eager_load_paths即可。因为在 rails 5 中,生产环境默认禁用自动加载。更多详情请关注link.
#config.autoload_paths << "#{Rails.root}/lib"
config.eager_load_paths << Rails.root.join('lib')
它适用于环境开发和生产。
我同意某些依赖项属于 lib
,有些可能属于 app/lib
。
我更喜欢为所有环境加载我选择放入 lib
中的所有文件,因此我在 config/application.rb
中在需要捆绑包之后但在打开 MyApplicationName
之前立即执行此操作]模块。
# load all ruby files in lib
Dir[File.expand_path('../../lib/**/*.rb', __FILE__)].each { |file| require file }
这不依赖于 Rails.root
(尚未定义),也不依赖于预先加载(对于环境可能关闭)。
唯一对我有用的是在预加载路径中添加嵌套的 lib 路径并在 config.to_prepare 块中添加 require_dependency。
# application.rb
...
config.to_prepare do
require_dependency("#{Rails.root}/lib/spree/core/product_filters.rb")
end
config.eager_load_paths << Rails.root.join('lib').join('spree').join('core')
...
我已将我的一个应用程序从 Rails 4.2.6 升级到 Rails 5.0.0。 Upgrade Guide 表示,自动加载功能现在在生产中默认禁用。
现在我的生产服务器上总是出现错误,因为我在 application.rb
文件中使用自动加载加载了所有 lib 文件。
module MyApp
class Application < Rails::Application
config.autoload_paths += %W( lib/ )
end
end
目前,我已将 config.enable_dependency_loading
设置为 true
,但我想知道是否有更好的解决方案。默认情况下在生产中禁用自动加载肯定是有原因的。
There must be a reason that Autoloading is disabled in production by default.
这里对这个问题进行了长时间的讨论。 https://github.com/rails/rails/issues/13142
由于线程安全,自动加载在生产环境中被禁用。感谢@Зелёный 的 link.
我按照 Github 上的建议,通过将 lib 文件存储在 app
目录中的 lib
文件夹中解决了这个问题。 app
文件夹中的每个文件夹都由 Rails 自动加载。
移动到 Rails 5 后我的更改列表:
- 将
lib
目录放入app
因为应用程序中的所有代码在开发中都是 自动加载的 并且 急切加载的 在产品中,最重要的是 autoreloaded 在开发中,因此您不必在每次进行更改时都重新启动服务器。 - 删除
lib
中指向您自己的 类 的所有require
语句,因为如果它们的 file/dir 命名正确,并且如果您离开require
声明它可以中断自动重新加载。更多信息 here - 在所有环境中设置
config.eager_load = true
以在开发中热切地查看代码加载问题 - 在使用线程之前使用
Rails.application.eager_load!
以避免 "circular dependency" 错误。 如果您有任何 ruby/rails 扩展,则将该代码保留在旧的
lib
目录中,并从初始化程序中手动加载它们。这将确保在您可以依赖它的进一步逻辑之前加载扩展:# config/initializers/extensions.rb Dir["#{Rails.root}/lib/ruby_ext/*.rb"].each { |file| require file } Dir["#{Rails.root}/lib/rails_ext/*.rb"].each { |file| require file }
对于任何像我一样为此苦苦挣扎的人来说,仅仅在 app/
下放置一个目录是不够的。是的,你会得到自动加载,但不是必需的 reloading, which requires namespacing conventions to be fulfilled。
此外,使用初始化程序加载旧的根级别 lib
将阻止在开发过程中重新加载功能。
我只是使用 config.eager_load_paths
而不是 config.autoload_paths
就像在 github 评论中提到的 akostadinov:
https://github.com/rails/rails/issues/13142#issuecomment-275492070
# config/application.rb
...
# config.autoload_paths << Rails.root.join('lib')
config.eager_load_paths << Rails.root.join('lib')
它适用于开发和生产环境。
感谢 Johan 建议将 #{Rails.root}/lib
替换为 Rails.root.join('lib')
!
将 lib 文件夹移动到应用程序帮助解决了一个问题,我的 Twitter api 不会 运行 在生产中。我有 "uninitialized constant TwitterApi",我的 Twitter API 在我的 lib 文件夹中。
我的 application.rb 中有 config.autoload_paths += Dir["#{Rails.root}/app/lib"]
,但在移动文件夹之前它不起作用。
这成功了
这允许自动重新加载库,并且也可以在生产环境中工作。
P.S。我已经更改了我的答案,现在它添加到两个 eager- 自动加载路径,无论环境如何,都允许在自定义环境中工作(如舞台)
# config/initializers/load_lib.rb
...
config.eager_load_paths << Rails.root.join('lib')
config.autoload_paths << Rails.root.join('lib')
...
从某种意义上说,这里是 Rails 5 中的统一方法,用于集中预加载和自动加载配置,同时它会在配置预加载时添加所需的自动加载路径,否则将无法正常工作:
# config/application.rb
...
config.paths.add Rails.root.join('lib').to_s, eager_load: true
# as an example of autoload only config
config.paths.add Rails.root.join('domainpack').to_s, autoload: true
...
总结 Lev 的回答:mv lib app
足以让我的所有 lib
代码自动加载/自动重新加载。
(rails 6.0.0beta3 但在 rails 5.x 上应该也能正常工作)
只要在config/application.rb文件中将config.autoload_paths改成config.eager_load_paths即可。因为在 rails 5 中,生产环境默认禁用自动加载。更多详情请关注link.
#config.autoload_paths << "#{Rails.root}/lib"
config.eager_load_paths << Rails.root.join('lib')
它适用于环境开发和生产。
我同意某些依赖项属于 lib
,有些可能属于 app/lib
。
我更喜欢为所有环境加载我选择放入 lib
中的所有文件,因此我在 config/application.rb
中在需要捆绑包之后但在打开 MyApplicationName
之前立即执行此操作]模块。
# load all ruby files in lib
Dir[File.expand_path('../../lib/**/*.rb', __FILE__)].each { |file| require file }
这不依赖于 Rails.root
(尚未定义),也不依赖于预先加载(对于环境可能关闭)。
唯一对我有用的是在预加载路径中添加嵌套的 lib 路径并在 config.to_prepare 块中添加 require_dependency。
# application.rb
...
config.to_prepare do
require_dependency("#{Rails.root}/lib/spree/core/product_filters.rb")
end
config.eager_load_paths << Rails.root.join('lib').join('spree').join('core')
...