测试开发工具【免费下载链接】factory_botA library for setting up Ruby objects as test data.项目地址https://gitcode.com/gh_mirrors/fa/factory_bot点击查看免费下载导读factory_bot 内置的build、create、build_stubbed、attributes_for等策略都有对应的回调生命周期但当你通过 自定义策略文档 注册了全新的策略例如输出 JSON 的:json策略时默认回调并不会自动适配。本文以仓库文档 自定义回调 为骨架结合源码深入讲解如何为自定义策略定义before(:json)、after(:json)以及任意命名的callback(:make_json_awesome)并揭示notify→CallbacksObserver→Callback#run的完整分发链路让读者能够为自己的自定义策略实现一套完整、可复用的回调体系。一、为什么需要自定义回调factory_bot 内置策略在 strategy 目录 中各自实现了association与result两个核心方法。以 Create 策略 为例其result方法会在构建过程中依次发出四个通知def result(evaluation) evaluation.notify(:before_build, nil) evaluation.object.tap do |instance| evaluation.notify(:after_build, instance) evaluation.notify(:before_create, instance) evaluation.create(instance) evaluation.notify(:after_create, instance) end end正是这些evaluation.notify(...)调用才让工厂里写的before(:build)、after(:create)等回调得以触发对应关系见 回调文档 中的回调时序表。问题在于通知的名字是写死在策略实现里的。如果你自定义了一个:json策略它没有发出:before_json/:after_json通知那么就算你在工厂里写before(:json)也不会生效。要打通这条链路需要两件事同时就位自定义策略在自己的result方法里显式调用evaluation.notify(:your_name, ...)工厂定义中通过before/after/callback声明对应名字的回调。这就是 自定义回调文档 所讲解的核心内容。二、完整示例为 :json 策略挂接自定义回调下面这段代码完整继承自原文档是一个可运行的最小闭环自定义JsonStrategy→ 注册:json策略 → 在工厂里声明三个自定义回调。class JsonStrategy def initialize strategy FactoryBot.strategy_by_name(:create).new end delegate :association, to: :strategy def result(evaluation) result strategy.result(evaluation) evaluation.notify(:before_json, result) result.to_json.tap do |json| evaluation.notify(:after_json, json) evaluation.notify(:make_json_awesome, json) end end def to_sym :json end end FactoryBot.register_strategy(:json, JsonStrategy) FactoryBot.define do factory :user do before(:json) { |user| do_something_to(user) } after(:json) { |user_json| do_something_to(user_json) } callback(:make_json_awesome) { |user_json| do_something_to(user_json) } end end逐段拆解其中值得注意的细节1. 策略包装复用而不是重写strategy FactoryBot.strategy_by_name(:create).new通过 Internal 模块 中的strategy_by_name从注册表取出内置:create策略类并实例化。随后delegate :association, to: :strategy把关联构建的职责转交给被包装策略——这意味着工厂中的association声明依然可以正常处理无需重新实现。2. 通知的时机与载荷evaluation.notify(:before_json, result)在对象构建完成、尚未序列化之前触发块参数拿到的是模型实例如userevaluation.notify(:after_json, json)在to_json之后触发块参数拿到的是序列化后的 JSON 字符串evaluation.notify(:make_json_awesome, json)一个完全自定义命名的通知同样在 JSON 生成后触发。同一个result流程里可以发出任意多个通知名字完全由你定义这正是自定义回调的灵活性所在。3. to_sym 的作用to_sym返回:json用于标识该策略对应的符号名配合FactoryBot.strategy_by_name与注册机制使用。三、注册策略后自动获得的方法调用FactoryBot.register_strategy(:json, JsonStrategy)后Internal 模块 会执行def register_strategy(strategy_name, strategy_class) strategies.register(strategy_name, strategy_class) StrategySyntaxMethodRegistrar.new(strategy_name).define_strategy_methods end其中StrategySyntaxMethodRegistrar#define_strategy_methods见 strategy_syntax_method_registrar.rb会在 Syntax::Methods 模块上动态定义三个方法注册后自动生成的方法行为FactoryBot.json(:user)单次执行:json策略FactoryBot.json_list(:user, 3)批量执行返回 3 个 JSON 结果FactoryBot.json_pair(:user)执行两次返回 2 个结果的数组也就是说回调声明与策略语法方法是同一套注册机制产出的注册:json后既可以在工厂里写before(:json)也可以直接调用FactoryBot.json(:user)。这与内置的build/create等策略的_list/_pair变体规则完全一致。四、底层原理notify 是如何找到回调的1. Evaluation#notify —— 唯一的入口Evaluation 类 中notify的实现极简def notify(name, result_instance) observer.update(name, result_instance) end它不做任何名字匹配只是把通知转交给在构建开始时装配好的 observer。这也是自定义策略只需要调用notify就能触发回调的原因。2. CallbacksObserver —— 按名字分发与去重CallbacksObserver 负责真正的分发def update(name, result_instance) callbacks_by_name(name).each do |callback| if !completed?(result_instance, callback) callback.run(result_instance, evaluator) record_completion!(result_instance, callback) end end endcallbacks_by_name(name)把当前工厂含继承与 trait 合并后的全部回调按名字过滤只取callback.name name的那批见 callbacks_observer.rb每个回调针对同一个实例只执行一次通过#{instance.object_id}-#{callback.object_id}作为完成标记去重见 callbacks_observer.rb。因此当JsonStrategy#result调用evaluation.notify(:before_json, result)时observer 会找出工厂里所有名字为:before_json的回调并逐个执行。3. Callback#run —— 按 arity 决定块参数Callback 类 根据块的参数个数决定如何调用def run(instance, evaluator) case block.arity when 1, -1, -2 then syntax_runner.instance_exec(instance, block) when 2 then syntax_runner.instance_exec(instance, evaluator, block) else syntax_runner.instance_exec(block) end endarity 为 1或可变参数块收到一个参数即notify传入的result_instance——对应示例中before(:json) { |user| ... }和after(:json) { |user_json| ... }的用法arity 为 2块同时收到实例与 evaluator可以访问工厂上下文与内置回调after(:build) { |user, context| ... }的写法一致arity 为 0块不接收参数直接以 SyntaxRunner 作为执行上下文运行。五、before / after / callback 三个 DSL 的语义差异在 Definition 类 中可以看到它们的真实关系def before(*names, block) callback(*names.map { |name| before_#{name} }, block) end def after(*names, block) callback(*names.map { |name| after_#{name} }, block) end def callback(*names, block) names.each do |name| add_callback(Callback.new(name, block)) end end三个方法的本质区别只在于名字的拼装方式DSL 写法实际注册的回调名before(:json):before_jsonafter(:json):after_jsoncallback(:make_json_awesome):make_json_awesome原名使用不做前缀这意味着三件事名字是唯一的匹配凭据。策略里notify(:before_json, ...)才会命中before(:json)如果你在策略里写notify(:json, ...)则只有callback(:json)能收到。callback是最底层的原语before/after只是它之上加前缀的语法糖。自定义名字不受前缀约束你可以注册任意语义的通知例如示例中的:make_json_awesome。同时要注意内置的before(:build)之所以能工作也正是因为 Create 策略 发出了:before_build通知——自定义回调与内置回调在机制上完全同构。六、与内置回调体系的关系与对照内置回调的触发完全由内置策略的notify调用驱动。当JsonStrategy#result第一行执行strategy.result(evaluation)时被包装的:create策略会先发出:before_build/:after_build/:before_create/:after_create四个内置通知随后你的自定义策略再发出:before_json/:after_json/:make_json_awesome三个自定义通知。因此在一个FactoryBot.json(:user)调用中回调的实际触发顺序是before_build → after_build → before_create → after_create → before_json → after_json → make_json_awesome这带来一个实用结论自定义策略中的自定义回调是内置回调的外层补充。若某个工厂同时声明了after(:create)与after(:json)前者先于后者执行两者各自拿到正确的载荷模型实例 vs JSON 字符串。你可以在 回调文档 的时序表中看到内置回调的完整生命周期作为对照。七、实战要点与注意事项别忘了调用notify自定义回调能否触发完全取决于策略的result方法是否在正确时机发出对应名字的通知。遗漏notify是自定义回调不生效最常见的根因。载荷类型要前后一致before(:json)收到的是模型实例after(:json)收到的是 JSON 字符串。块内使用错误的载荷类型会导致隐性 bug建议在工厂注释中写明各回调的入参类型。使用callback声明任意名字before/after会自动拼接before_/after_前缀只有callback(:xxx)能精确控制最终回调名。复用内置策略而不是重写像示例那样把:create策略包装进自定义策略即可免费获得关联构建association委托与完整的属性赋值/持久化逻辑自定义策略只需专注附加行为。注册一次即可获得完整方法族register_strategy(:json, JsonStrategy)之后json、json_list、json_pair三个入口自动可用见 StrategySyntaxMethodRegistrar。同名回调对同一实例只执行一次即便通知被重复发出CallbacksObserver 也会按实例去重无需担心幂等问题。结语自定义回调的本质是工厂 DSLbefore/after/callback与策略实现notify之间以符号名为契约的松耦合协作。理解了notify→CallbacksObserver按名分发 →Callback#run按 arity 执行这条链路后你不仅可以为 JSON 输出这类序列化策略挂接钩子还能为任意自定义构建流程设计出语义清晰的回调体系。相关的配套材料可继续阅读 自定义策略文档、回调文档以及本仓库中的核心实现 Evaluation、CallbacksObserver、Callback 与 Definition。赞分享测试开发工具【免费下载链接】factory_botA library for setting up Ruby objects as test data.项目地址https://gitcode.com/gh_mirrors/fa/factory_bot点击查看免费下载相关推荐如何用 Rake 编写 Capistrano 自定义任务before/after 钩子完全指南如何用 Rake 编写 Capistrano 自定义任务before/after 钩子完全指南 Capistrano 是一款基于 Ruby、Rake 和 SSDevOpsCLIfactory_bot 自定义策略Custom Strategies完全指南注册与组合构建自己的测试数据构建器factory_bot 自定义策略Custom Strategies完全指南注册与组合构建自己的测试数据构建器 factory_bot 内置的 build测试开发工具Sails 自定义 Hook 的 .routes 特性基于 before/after 的路由绑定完全指南Sails 自定义 Hook 的 .routes 特性基于 before/after 的路由绑定完全指南 导读 在 Sails 框架中每个自定义 Hook后端上一篇3个场景4步操作PDF补丁丁让你的PDF处理效率翻倍下一篇如何快速上手Ascend Boost Comm10分钟搭建算子开发环境创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
STM32H7串口屏HMI框架:DMA双缓冲与事件驱动设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 15:30:52
JL-35温湿度记录仪:超限报警闭环与边缘告警实战指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 15:30:45
MATLAB贝叶斯优化调参实战:高斯过程与采集函数案例解析 简介:一份基于MATLAB的贝叶斯优化示例代码,面向机器学习调参、仿真优化及工程试验设计等人群,针对目标函数评估昂贵、解析表达未知的黑盒问题提供高效求解方案。代码清晰演示了如何调用MATLAB内置的bayesopt函数,以高斯过程作为代… · 2026/9/26 16:01:05
金属表面缺陷检测VOC数据集转YOLO训练全流程解析 简介:面向金属表面缺陷检测与工业视觉质检场景,这套目标检测数据集以VOC标注格式组织,图像与XML标签一一对应,经测试可直接用于主流检测模型的训练,省去手动标注成本。压缩包采用7z格式,共2000个文件&#… · 2026/9/26 16:01:05
abogen 完整指南:把 EPUB、PDF 和纯文本变成带同步字幕的音频 abogen 完整指南:把 EPUB、PDF 和纯文本变成带同步字幕的音频 【免费下载链接】abogen Generate audiobooks from EPUBs, PDFs and text with synchronized captions. 项目地址: https://gitcode.com/GitHub_Trending/ab/abogen
想把一本书或长文章变成带同步… · 2026/9/26 16:00:53
对标 Cursor:JetBrains 官方 Junie 的 AI 编码代理配置与验证 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 16:00:47
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46