如何实现可通过方法链动态创建新方法的Ruby DSL
实现方案
你现有代码无法支持is_the场景的核心问题是:is_a、is_not_a返回的代理对象在第一次触发method_missing时就直接完成方法定义、终止调用链,而is_the需要多一层调用才能拿到目标方法的返回值。
可以通过拆分不同职责的代理类实现所有DSL需求,同时消除原有代码中重复定义匿名类的冗余:
class Thing attr_reader :name def initialize(name) @name = name end # is_a/is_not_a 通用布尔代理 class BooleanProxy def initialize(base, return_value) @base = base @return_value = return_value end def method_missing(method_name) @base.define_singleton_method(:"#{method_name}?") { @return_value } end end # is_the 第一层代理:收集要定义的方法名 class TheProxy def initialize(base) @base = base end def method_missing(method_name) TheValueProxy.new(@base, method_name) end end # is_the 第二层代理:接收方法返回值,完成方法定义 class TheValueProxy def initialize(base, method_name) @base = base @method_name = method_name end def method_missing(value_name) @base.define_singleton_method(@method_name) { value_name } end end def is_a BooleanProxy.new(self, true) end def is_not_a BooleanProxy.new(self, false) end def is_the TheProxy.new(self) end end
测试验证
运行以下测试代码,所有场景均符合预期:
jane = Thing.new('Jane') # 测试is_a逻辑 jane.is_a.person puts jane.person? #=> true jane.is_a.woman puts jane.woman? #=> true # 测试is_not_a逻辑 jane.is_not_a.man puts jane.man? #=> false # 测试is_the逻辑 jane.is_the.parent_of.joe puts jane.parent_of #=> :joe
如果需要返回字符串格式的值而不是符号,只需要把TheValueProxy中的定义改为@base.define_singleton_method(@method_name) { value_name.to_s }即可。如果需要支持更长的方法名链式定义(比如jane.is_the.grand_parent_of.father_of.tom定义grand_parent_of_father_of方法返回对应值),只需要修改TheProxy逻辑增加方法名片段累积能力,持续返回代理实例直到最后一步完成方法定义即可,核心逻辑和上述实现一致。
内容的提问来源于stack exchange,提问作者Kiran Kumawat
相关产品推荐
相关产品推荐

