Lau*_*ent 6 ruby namespaces ruby-on-rails ruby-on-rails-5
我app/services
在Rails 5项目中的文件夹下需要加载/需要类的问题很多,而我开始放弃这个问题。
首先要弄清楚的是,services/
我在整个项目中都使用了简单的PORO类,以从控制器,模型等中抽象出大多数业务逻辑。
这棵树看起来像这样
app/
services/
my_service/
base.rb
funny_name.rb
my_service.rb
models/
funny_name.rb
Run Code Online (Sandbox Code Playgroud)
首先,当我尝试使用MyService.const_get('FunnyName')
它时,它FunnyName
来自我的models目录。MyService::FunnyName
但是,当我直接进行操作时,它似乎没有相同的行为,在我的大多数测试和更改中,此方法都可以正常工作,这很奇怪。
我意识到Rails config.autoload_paths
不会递归地加载东西;有意义的是,第一个FunnyName
被捕获的是,models/funny_name.rb
因为它肯定是已加载的,而不是另一个。
没关系,让我们找到一种解决方法。我将此添加到我的application.rb
:
config.autoload_paths += Dir[Rails.root.join('app', 'services', '**/')]
Run Code Online (Sandbox Code Playgroud)
这会将服务的所有子目录添加到中config.autoload_paths
。显然,从Rails 5开始,不建议编写类似的内容。但是这个想法对我来说确实不错。
现在,当我启动我的应用程序时,它崩溃并输出如下内容
无法自动加载常量Base,需要/.../backend/app/services/my_service/base.rb进行定义(LoadError)
名称已更改,但这是我之前编写的树的匹配路径
问题是,base.rb
在错误导致我进入的确切文件中进行了定义,其中包含类似
class MyService
class Base
end
end
Run Code Online (Sandbox Code Playgroud)
因此,我尝试了其他解决方法,其中很多方法都无效。因此,我最终完全删除了autoload_paths
,然后直接将其添加到application.rb
Dir[Rails.root.join('app', 'services', '**', '*.rb')].each { |file| require file }
Run Code Online (Sandbox Code Playgroud)
现在base.rb
已正确加载,MyService.const_get('FunnyName')
实际上将返回正确的类,并且一切正常,但这是一种令人作呕的解决方法。此外,它尚未经过测试,production
但根据环境可能会产生问题。
从application.rb
声音中要求整棵树是个坏主意,我不认为可以这样保留。
services/
在Rails中添加自定义目录的最干净方法是什么?它包含多个具有简单名称的子目录和类,这些名称也存在于应用程序的其他部分(模型base.rb
等)中。
您如何避免混淆autoload_paths
?还有其他我不知道的方法可以解决这个问题吗?为什么在base.rb
这里甚至崩溃?
经过更深入的研究和尝试,我意识到我不得不eager_load
使用服务以避免在调用诸如的元功能时出现错误的常量const_get('MyClassWithModelName')
。
事情是这样的:经典之作eager_load_paths
无法正常工作,因为出于某种原因,这些类显然会在初始化Rails的整个核心之前就被加载,而简单的类名(例如)Base
实际上会与核心混在一起,因此会使所有崩溃。
有人可能会说“然后将Base重命名为其他名称”,但是由于Rails告诉我,我应该更改包装到名称空间中的类名吗?我不这么认为。类名应该保持简单,我在自定义名称空间中所做的事情与Rails无关。
我必须仔细考虑并写下我自己的Rails配置钩子。我们加载核心及其所有功能,然后service/
递归地加载。
附带说明,它不会给生产环境增加任何负担,并且非常便于开发。
将其放置在config/environment/development.rb
您希望加载的所有其他环境中,而不会出现Rails类冲突(例如test.rb
我的情况)
# we eager load all services and subdirectories after Rails itself has been initializer
# why not use `eager_load_paths` or `autoload_paths` ? it makes conflict with the Rails core classes
# here we do eager them the same way but afterwards so it never crashes or has conflicts.
# see `initializers/after_eager_load_paths.rb` for more details
config.after_eager_load_paths = Dir[Rails.root.join('app', 'services', '**/')]
Run Code Online (Sandbox Code Playgroud)
然后创建一个initializers/after_eager_load_paths.rb
包含以下内容的新文件
# this is a customized eager load system
# after Rails has been initialized and if the `after_eager_load_paths` contains something
# we will go through the directories recursively and eager load all ruby files
# this is to avoid constant mismatch on startup with `autoload_paths` or `eager_load_paths`
# it also prevent any autoload failure dû to deep recursive folders with subclasses
# which have similar name to top level constants.
Rails.application.configure do
if config.respond_to?(:after_eager_load_paths) && config.after_eager_load_paths.instance_of?(Array)
config.after_initialize do
config.after_eager_load_paths.each do |path|
Dir["#{path}/*.rb"].each { |file| require file }
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
奇迹般有效。您也可以require
根据load
需要进行更改。
归档时间: |
|
查看次数: |
687 次 |
最近记录: |