十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

藏在MemoWise源码里的10个Ruby冷僻技巧:单例类、private_constant与attached_object寻宝图

藏在MemoWise源码里的10个Ruby冷僻技巧:单例类、private_constant与attached_object寻宝图 藏在MemoWise源码里的10个Ruby冷僻技巧单例类、private_constant与attached_object寻宝图【免费下载链接】memo_wiseThe wise choice for Ruby memoization项目地址: https://gitcode.com/gh_mirrors/me/memo_wiseMemoWise是一个主打聪明的 Ruby 记忆化memoization的开源 gem把昂贵方法的计算结果缓存起来同样的参数第二次调用时直接返回缓存还能支持重置、预设、冻结对象甚至类方法。它的主实现只有几百行却密集使用了private_constant、attached_object、单例类操作等冷僻 Ruby 技巧。这篇文章带你在 lib/memo_wise.rb 和 lib/memo_wise/internal_api.rb 里寻宝10 个技巧逐一拆解新手也能看懂。技巧 1class target动态注入单例类方法Ruby 的类方法本质上是单例类singleton class上的实例方法。MemoWise 在prepended(target)里这样写def self.prepended(target) class target def allocate ... end end end见 lib/memo_wise.rb#L110-L127。class target ... end打开的是当前这个 target 对象的单例类在里面定义的allocate就只属于它而不会污染其他类。这是给传入的参数打补丁的标准手法。技巧 2attached_object反向寻宝单例类的主人拿到单例类后怎么找回它服务于哪个对象Ruby 3.2 新增了Class#attached_object一行搞定def self.find_attached_object(klass) klass.attached_object end见 lib/memo_wise/internal_api.rb#L201-L212。而在低版本 Ruby 上作者用ObjectSpace.each_object(Module)全量遍历所有 Module 来找cls.singleton_class klass并用original_class_from_singleton ||把结果缓存起来——遍历一次终身受益。技巧 3private_constant给内部常量上锁源码里藏着两个脚手架模块module CreateMemoWiseStateOnExtended def extended(base) MemoWise::InternalAPI.create_memo_wise_state!(base) super end end private_constant(:CreateMemoWiseStateOnExtended)见 lib/memo_wise.rb#L80-L94。private_constant让这个常量只能在MemoWise模块内部访问外部代码写MemoWise::CreateMemoWiseStateOnExtended会直接报NameError。这是保护实现细节、防止用户误用的轻量手段。技巧 4heredoc module_eval的字符串元编程记忆化方法不是写死的而是根据参数形态现场生成klass.module_eval ~HEREDOC, __FILE__, __LINE__ 1 def #{method_name} _memo_wise.fetch(:#{method_name}) do _memo_wise[:#{method_name}] #{original_memo_wised_name} end end HEREDOC见 lib/memo_wise.rb#L192-L254。三个细节值得学~HEREDOCsquiggly heredoc自动去掉缩进__FILE__和__LINE__作为module_eval的参数让报错信息指向定义处而不是执行处调试体验大幅提升字符串插值把方法名缝进方法体。技巧 5alias_methodprivate悄悄给原方法改名替换原方法前先把旧方法备份klass.send(:alias_method, original_memo_wised_name, method_name) klass.send(:private, original_memo_wised_name)见 lib/memo_wise.rb#L187-L188。别名统一叫_memo_wise_original_方法名见 internal_api.rb#L141-L143并立刻设为private。原方法就此隐身成为只有 MemoWise 自己能触发的私有副本——这就是缓存能安全复用的基础。技巧 6define_singleton_method替代module_function为了让用户类也能调用preset_memo_wise/reset_memo_wise源码这样定义类方法%i[preset_memo_wise reset_memo_wise].each do |method_name| target.define_singleton_method( method_name, MemoWise.instance_method(method_name) ) end见 lib/memo_wise.rb#L262-L268。注释一针见血Like calling module_function, but original method stays public。module_function会把原方法转成私有而define_singleton_method绑一个UnboundMethod即可互不干扰。技巧 7tap链式顺手改一下prepended里重写了instance_method让反射拿到的参数信息保持原样。核心是super.tap do |curr_method| ... end见 lib/memo_wise.rb#L278-L303。tap对返回值做点事再原样交出非常适合这种拿到 UnboundMethod 后偷偷重写它的parameters的场景省掉临时变量。技巧 8instance_variable_set安全初始化缓存每个被记忆化对象都要有一块可变缓存区但不能覆盖已有值obj.instance_variable_set(:_memo_wise, {}) unless obj.instance_variable_defined?(:_memo_wise)见 lib/memo_wise/internal_api.rb#L12-L26。用先检查再设置的双保险保证幂等而且这行代码在initialize和allocate两个入口都会执行因此冻结对象frozen object也能被记忆化——这是 MemoWise 相对其他 gem 的杀手锏。技巧 9|| 类级变量把慢操作变成一次性操作回到技巧 2 的低版本回退实现internal_api.rb#L220-L233original_class_from_singleton || {} original_class_from_singleton[klass] || ObjectSpace.each_object(Module).find { ... }||是不存在就计算存在就跳过的最简缓存。作者亲测这一处缓存把测试套件从5 分钟降到几秒。冷僻技巧 朴素缓存就是性能优化的日常。技巧 10method.parameters.map(:first)参数类型解构如何判断一个方法是单必填参数还是带 splat看 internal_api.rb#L46-L64parameters method.parameters.map(:first) if parameters [:req] then ONE_REQUIRED_POSITIONAL elsif parameters [:keyreq] then ONE_REQUIRED_KEYWORD ...UnboundMethod#parameters返回[[:req, :x], [:keyreq, :y], ...]这样的元组数组map(:first)只取类型标签再用和all?做模式匹配。符号常量NONE、SPLAT等internal_api.rb#L28-L34代替魔法字符串可读性拉满。顺手再学一手跨版本兼容的...参数文件开头还藏着一个元技巧lib/memo_wise.rb#L56-L78all_args RUBY_VERSION 2.7 ? * : ... class_eval ~HEREDOC, __FILE__, __LINE__ 1 def initialize(#{all_args}) ... end HEREDOCRuby 2.7 才引入...转发参数低版本语法都不存在。作者干脆连方法签名本身都用 heredoc 动态生成让同一份源码横跨 Ruby 2.6 到 3.3。小结这10个技巧能用在哪| 技巧 | 一句话场景 | | -- | -- | |class obj| 给任意对象外挂专属方法 | |attached_object| 从单例类反查主人对象 | |private_constant| 隐藏实现细节常量 | | heredoc module_eval| 运行时生成方法 | |alias_methodprivate| 备份并隐藏原方法 | |define_singleton_method| 定义不污染原方法的类方法 | |tap| 链式修改中间结果 | |instance_variable_set| 安全注入实例变量 | |\|\|类级缓存 | 把昂贵查询变成一次性操作 | |parameters.map(:first)| 用类型标签做参数模式匹配 |这套组合拳正是 MemoWise 又快又稳的秘密方法生成只用一次缓存命中只查一个 Hash。想要动手验证可以跑 spec/ 下的测试如 spec/memo_wise_spec.rb或在 benchmarks/ 目录执行 benchmarks.rb 亲测速度。打开 lib/memo_wise.rb 对照本文逐行读一遍这 10 个冷僻技巧就都是你自己的了。【免费下载链接】memo_wiseThe wise choice for Ruby memoization项目地址: https://gitcode.com/gh_mirrors/me/memo_wise创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表