如何在Ruby中实现类Elixir的管道操作符以处理集合链式调用
Ruby 链式管道操作最优实现方案
最轻量化且完全符合需求的实现是直接扩展核心类的pipe方法,无需引入第三方依赖,实现代码不足20行,完全适配你期望的语法和所有使用场景:
核心实现
class Object def pipe(callable, *args, **kwargs, &block) result = case callable when Class callable.new(self, *args, **kwargs, &block) when Method, Proc callable.call(self, *args, **kwargs, &block) else raise ArgumentError, "pipe仅支持传入类、Method实例、Proc类型的可调用对象" end # 自动适配集合类场景,响应each的对象直接返回枚举器,无需手动调用 result.respond_to?(:each) ? result.each : result end end
效果验证
自定义去重场景
使用你给出的Dedup类测试,完全符合你期望的语法:
class Dedup def initialize(obj) @obj = obj end def each Enumerator.new do |y| prev = nil @obj.each do |el| if el != prev y << el prev = el end end end end end # 测试用例直接通过 expect( "1 1 1 2 2 3" .split .pipe(Dedup) .map(&:to_i) .to_a ).to eq [1, 2, 3]
多类型管道调用场景
你理想中的管道写法可以直接运行:
require 'uri' require 'base64' # 示例GZIPped类 class GZIPped def initialize(str) @str = str end # 此处省略压缩逻辑,直接返回原内容做演示 def to_s @str end end text = "https://example.com https://ruby-lang.org" result = text .split .pipe(URI.method(:parse)) .map(&:to_s) .join(" ") .pipe(GZIPped) .pipe(Base64.method(:encode))
可选无全局污染实现
如果不想污染全局Object类,可以使用Ruby的细化(Refinement)特性,仅在需要使用的文件中生效:
module PipeExtension refine Object do def pipe(callable, *args, **kwargs, &block) # 和上文核心实现逻辑一致 result = case callable when Class callable.new(self, *args, **kwargs, &block) when Method, Proc callable.call(self, *args, **kwargs, &block) else raise ArgumentError, "pipe仅支持传入类、Method实例、Proc类型的可调用对象" end result.respond_to?(:each) ? result.each : result end end end # 在需要使用pipe方法的文件顶部引入即可 using PipeExtension
方案优势
- 完全无第三方依赖,实现足够轻量,性能开销可以忽略
- 支持所有你需要的调用场景:类初始化、方法调用、Proc执行,同时支持传入额外参数
- 自动适配集合类返回值,无需额外手动调用方法
- 可自由扩展实现逻辑,不受第三方库的功能约束
内容的提问来源于stack exchange,提问作者Sergei O. Udalov
相关产品推荐
相关产品推荐

