neounix
(Dark Matter)
1
嘿,Discourse 插件专家们,
关于 Discourse 插件和 Rails 初始化器,有个小问题想请教。
如果一个 Discourse 插件包含一个名为 “config” 的目录,且在该目录下有一个名为 “initializers” 的子目录,那么 Discourse 的 Rails 应用是否会像 Rails 6 那样,自动读取 “initializers” 目录下的所有插件初始化文件?
我之所以这么问,是因为我正在为一个客户从头开发一个“后台”Rails 6 应用(纯 Rails,没有叠加 EmberJS 或其他 JS 框架)。在该应用中,我在 initializers 下设置了如下结构的目录:
./config/initializers/client/
所有专属于该客户的初始化器都放在这个 “client” 子目录中。
Rails 6 会读取标准 initializers 目录下的所有文件(包括子目录)。因此我想知道,如果 Discourse 插件采用类似的初始化器目录结构,它是否会像 Rails 6 那样,自动读取插件内的所有初始化器,例如:
./plugins/my_plugin/config/initializers/myclient/
client_initializer1.rb
client_initializer2.rb
client_initializer3.rb
而无需在 plugin.rb 文件中注册这些资源?
谢谢!
附注:我在 GitHub 上查看了大约 10 个 Discourse 插件,其中没有一个在 config 目录下放置了初始化器。这也是我决定提出这个问题的原因(而且我目前的 Rails 开发环境尚未配置为 Discourse 环境,而是专门用于该客户项目)。
2 个赞
hawm
(Hawm)
2
我还想了解 Discourse 插件与普通 Rails Engine 的区别。我刚才查看了源代码,但没太看懂。
fzngagan
(Faizaan Gagan)
3
浏览了这篇指南 Ruby on Rails Guides: Configuring Rails Applications 后,我认为 plugin.rb 的作用与此类似。大多数规模较大的插件都将其作为逻辑的入口点,而非逻辑本身。
neounix
(Dark Matter)
4
是的,我最近经常阅读那份 Rails 指南。基本上,我对 Rails 6 的配置方式还算“OK”(仍在学习中,但每天都越来越得心应手)。
我只是想知道,在 Discourse 插件中是否可以使用相同的结构(针对初始化器)?Rails 是否会像处理 Rails 6 那样读取子目录中的初始化器?还是说,我们需要在 plugin.rb 中将插件的初始化器目录注册为一个配置“资源”(暂且这么称呼)?
我昨天查阅了关于 Rails 插件的资料,并做了一些对比。
fzngagan
(Faizaan Gagan)
5
据我所知,Discourse 除了 plugin.rb 之外不读取任何 Ruby 文件。它会读取 JS 和配置文件。所有的 Ruby 文件都必须在 plugin.rb 中通过 require 引入。
3 个赞
我也这么认为。
你可以在 plugin.rb 中加载所需的任何额外的 Ruby 文件。
以下是 Follow 插件中的一个示例:
1 个赞
neounix
(Dark Matter)
7
我在 GitHub 上阅读了许多 Discourse 插件后,也有类似的印象。
看起来 Discourse 只读取 plugin.rb,我们必须在该文件中加载所有可能需要使用的其他 Ruby 文件,例如初始化文件(initializers)。
不过,我曾希望我的理解有误,或许 Discourse 插件与 Rails 之间存在更“深层”的集成;毕竟 Rails 6 的表现太出色,让我有些依赖了。
感谢确认。
1 个赞
fzngagan
(Faizaan Gagan)
8
我认为也应该有一种方法可以在 plugin.rb 中要求加载初始化代码。
你在 plugin.rb 中尝试过这个方法吗?
Rails.application.config.before_initialize do
# 初始化代码放在这里
end
neounix
(Dark Matter)
9
是的,在 plugin.rb 中调用和加载初始化器并不是问题。
我之所以提出这个问题,是因为我正在为一个企业编写一个相当大型的 Rails 6 应用,将其数十年的遗留后台脚本转换为 Rails 应用。在 Rails 中,要添加初始化器,我只需要在 config/initializers 下创建一个子目录,Rails 会自动包含所有初始化器文件,无需编写任何代码来引入这些文件。
感谢大家的回复,非常感谢!
1 个赞
fzngagan
(Faizaan Gagan)
10
是的,我很希望将 Rails 的这些优势移植到 Discourse 插件中。我个人最期待的功能之一是能够无需重启服务器即可实时重载 Ruby 文件。
1 个赞
这正是 reloadable_patch 的用途。如果你将 Ruby 类的更改包裹在 reloadable_patch 中,它应该能够实时重载!
1 个赞
fzngagan
(Faizaan Gagan)
12
在开发时,我能否将整个 plugin.rb 文件都包裹在其中?更严肃地说,这种方法的应用范围能有多大?
另外,添加新文件或更改任何 yml 文件是否都需要重启服务器?
2 个赞
你不必对所有内容都使用 reloadable_patch。instance.rb 中定义的许多旨在简化插件开发的方法(例如 add_to_serializer)在内部都使用了 reloadable_patch。
理想情况下,我们的插件 API 应该足够完善,让你无需进行大量的 reloadable_patch 操作。
是的,确实如此。我在想是否有什么办法可以解决 yml 文件变更的问题;这一点我个人觉得挺烦人的。
3 个赞
fzngagan
(Faizaan Gagan)
14
确实如此。我之前不知道 reloadable_patch 是用于支持热重载的。除了 Discourse 目前的使用场景外,我能想到许多其他用例,例如:
reloadable_patch do
require 'x/y/z'
end
或者用于猴子补丁(monkey patches)。
1 个赞
fzngagan
(Faizaan Gagan)
15
我认为,移除或添加此类函数仍然会存在问题,因为 reloadable_patch 是在函数内部被调用的。
无论如何,这确实帮了大忙。
1 个赞
我开始研究 YAML 变更的热重载,发现它确实有效。如果插件未正确使用 reloadable_patch,可能会破坏热重载功能;但一旦所有插件都能安全重载,您的 YAML 变更也将支持热重载。
4 个赞
fzngagan
(Faizaan Gagan)
17
不过,如果您能详细描述使用可重载补丁的具体步骤,那将非常有帮助。
这是我尝试过但效果不佳的方法(我肯定漏掉了什么):
after_initialize do
add_to_serializer(:topic_view, :check, false) do
puts 'nocheck'
end
end
服务器启动后,即使我将 nocheck 改为 check,在重新加载主题路由后,它仍然打印 nocheck。
after_initialize do
reloadable_patch do |plugin|
puts 'nocheck'
end
end
在更改 puts 下的字符串后重新加载任何页面,依然没有效果。
我想我在上面的帖子中误导了大家。reloadable_patch 对 Discourse 开发很有帮助,但 @david 已经非常清楚地解释了它的用法:
插件 plugin.rb 中 after_initialize 块内的任何内容仅在 应用启动 时加载,而不会在随后的重载中加载。
因此,假设您想为用户序列化器(user serializer)添加一些内容。通常的行为如下:
启动时:
- Discourse 加载
user_serializer.rb
- Discourse 加载
plugin.rb,其中包含对 user_serializer 的覆盖
重载时:
- Discourse 重新加载
user_serializer.rb
- (
plugin.rb 中的补丁不会被重新加载,插件的覆盖失效)
使用我们的 reloadable_patch 系统:
启动时:
- Discourse 加载
user_serializer.rb
- Discourse 加载
plugin.rb 并为 user_serializer 注册 reloadable_patch
- 执行可重载补丁
重载时:
- Discourse 重新加载
user_serializer.rb
- 执行可重载补丁
- (太好了,插件的覆盖仍然生效)
6 个赞
neounix
(Dark Matter)
21
这描述的是 Rails 的一个核心特性,与 Discourse 本身并无直接关联。
在 Rails 中,所有初始化器(initializers)仅在 Rails 启动时加载。因此,任何基于初始化器(例如 after_initialize)执行代码的插件,只会在 Rails 启动或重启时执行(如果我说错了请指正)。
我之所以熟悉这一点,是因为我目前正在为客户构建一个 Rails 应用程序,并为各种任务(布尔值、静态数组等)编写了许多初始化器。在每种情况下,如果我们更改了 Rails 初始化器中的任何代码,都必须重启 Rails(在开发环境中执行“按 Ctrl+C,然后运行 rails s”),才能使初始化器中的新值生效并加载到 Rails 应用中。
如果我说错了请指正!谢谢。
有时,我认为这可能会让一些 Discourse 插件开发者感到困惑,尤其是那些主要专注于 Discourse 插件开发、而不太熟悉通用 Rails 应用程序的开发者。当我们说"Discourse 这样做”或"Discourse 那样做”时,实际上我们描述的可能是 Rails 的核心功能或特性,而这些特性并不一定专门属于 Discourse。
Rails 6 仅在 Rails 启动时读取 config/initializers 目录(以及所有 config/initializers 子目录)中的所有文件。同样(关于插件我不完全确定),依赖 after_initialize 的 Discourse 插件代码(请任何人确认一下)只会在 Rails 启动或重启时由 Rails 应用加载,因为这是 Rails 启动过程的一个基本特性。
我猜这里所有从事 Rails 开发的人都已经知道这些了。我只是最近才开始了解这些细节(而且感觉要成为专家还有很长的路要走),因为我目前正在每天处理一个 Rails 应用程序。顺便说一句,我现在真希望自己在十年前就开始学习 Rails,因为从 LAMP 开发背景转过来后,我发现 Rails 要好得多(既简单又有趣)。
今年我已经成为了 Rails 的忠实粉丝,我非常感激 Discourse 引领我走上了这条道路。
再次强调,如果我说错了请指正!谢谢。我正在努力让自己在 Rails 方面更加专业。
6 个赞
没错!你的理解是正确的,关于 Rails 初始化器的理解也是如此。
我也是 Rails 的超级粉丝 
7 个赞