类Unix管道

时间:2014-06-10 14:13:15

标签: ruby enums

我遇到了关于creating memory-efficient Ruby pipe class with lazy evaluation.的问题。有些代码用于使用惰性枚举器有效地创建命令管道。我一直在搞乱它,并实现了如下所示的tee命令。我觉得有更好的方法可以做到这一点。

class Pipe

  class Tee

    def initialize(pipe)
      @pipe = pipe
      @buffer = []
      @pipe.source = self.to_enum
    end

    def <<(item)
      @buffer << item
      # puts "Added '#{item}' to buffer: #{@buffer.inspect}"
    end

    def to_enum
      Enumerator.new do |yielder|
        item = @buffer.shift
        yielder << item if item
      end.lazy
    end

  end

  attr_writer :source

  def initialize
    @commands = []
    @source = nil
  end

  def add(command, opts = {})
    @commands << [command, opts]
    self
  end

  def run
    enum = @source

    @commands.each do |command, options|
      enum = method(command).call(enum, options)
    end

    enum.each {}

    enum
  end

  def cat(enum, options)
    Enumerator.new do |yielder|
      enum.map { |line| yielder << line } if enum

      options[:input].tap do |ios|
        ios.each { |line| yielder << line }
      end
    end.lazy
  end

  def out(enum, options)
    Enumerator.new do |yielder|
      enum.each do |line|
        puts line
        yielder << line
      end
    end.lazy
  end

  def cut(enum, options)
    Enumerator.new do |yielder|
      enum.each do |line|
        fields = line.chomp.split(%r{#{options[:delimiter]}})

        yielder << fields[options[:field]]
      end
    end.lazy
  end

  def upcase(enum, options)
    Enumerator.new do |yielder|
      enum.each do |line|
        yielder << line.upcase
      end
    end
  end

  def tee(enum, options)

    teed = Tee.new(options.fetch(:other))
    Enumerator.new do |yielder|
      enum.each do |line|
        yielder << line
        teed << line
      end
    end
  end

  def grep(enum, options)
    Enumerator.new do |yielder|
      enum.each do |line|
        yielder << line if line.match(options[:pattern])
      end
    end.lazy
  end

end

another_pipe = Pipe.new
another_pipe.add(:grep, pattern: /testing/i)
another_pipe.add(:out)

pipe = Pipe.new
pipe.add(:cat, :input => StringIO.new("testing\na new\nline"))
# pipe.add(:grep, pattern: /line/)
pipe.add(:tee, :other => another_pipe)
pipe.add(:upcase)
pipe.add(:out)
pipe.run

puts "================================================="
another_pipe.run

这是输出:

TESTING
A NEW
LINE
=================================================
testing

有没有人看到更聪明的方法呢?

0 个答案:

没有答案