Examples
Contents of linefeed:examples/, concatenated here for easy consumption.
01_logger.rb
# frozen_string_literal: true require_relative 'demo' require 'linefeed' # Simplest possible example module Demo class Logger include Linefeed def initialize(output) line_no = 0 linefeed do |line| line_no += 1 output << format('%<line_no>03d => %<line>s', line_no: line_no, line: line) end end end end
02_canonicalize.rb
# frozen_string_literal: true require_relative 'demo' require 'linefeed' # Per-line processing with headers & trailers module Demo class Canonicalize include Linefeed def initialize(output) @output = output @output << "---------- START\r\n" @output << "Canonicalized: yes\r\n" @output << "\r\n" linefeed do |line| output << process_line(line) end end def process_line(line) canonicalize(line) end def canonicalize(line) "#{line.chomp.sub(/[ \t]+$/, '')}\r\n" end def close super @output << "---------- END\r\n" @output.close end end end
03_escaped.rb
# frozen_string_literal: true require_relative 'demo' require 'linefeed' # Handling the protocol via super module Demo class Escaped include Linefeed def initialize(output) @output = output end def escape(line) line.sub(/^(-|From )/, '- \\1') end def <<(chunk) super do |line| @output << escape(line) end end def close super do |line| @output << "#{escape(line)}\n" end end end end
04_line_digest.rb
# frozen_string_literal: true require_relative 'demo' require 'linefeed' require 'digest' # Only outputs at close module Demo class LineDigest include Linefeed def initialize(output) @output = output @line_digest = Digest('SHA256').new linefeed do |line| @line_digest.update(line) end end def close super @output << @line_digest.hexdigest end end end
05_chunk_digest.rb
# frozen_string_literal: true require_relative 'demo' require 'linefeed' require 'digest' # Not actually using Linefeed, but speaking the same protocol, # consuming entire chunks. # # Should give the same digest as LineDigest. module Demo class ChunkDigest def initialize(output) @output = output @digest = Digest('SHA256').new end def <<(chunk) @digest << chunk end def close @output << @digest.hexdigest end end end
06_canonicalized_digest.rb
# frozen_string_literal: true require_relative '02_canonicalize' require_relative '04_line_digest' require 'delegate' # Easy chaining module Demo class CanonicalizedDigest < DelegateClass(Canonicalize) def initialize(output) super(Canonicalize.new(LineDigest.new(output))) end end end
07_null.rb
# frozen_string_literal: true require_relative 'demo' require 'linefeed' # Intentionally fails to setup the feed and suffers for it. # Don't do this. module Demo class Null include Linefeed def initialize(output) @output = output @count = 0 end def <<(*) super rescue ArgumentError @count += 1 end def close super rescue ArgumentError @output << "rescued #{@count += 1} time(s)" end end end
demo.rb
# frozen_string_literal: true require_relative 'demo_helper' if $0 == __FILE__ example_files = Dir[File.join(__dir__, '[0-9][0-9]_*.rb')] example_files.each do |path| require_relative File.basename(path) end end def run recipients = Demo.setup_examples input = Demo.input_pipe(ARGF) maxlen = 8192 chunk = ''.b while input.read(maxlen, chunk) && !chunk.empty? recipients.each do |r| r << chunk end end recipients.each(&:close) end Demo.launcher { run unless $! }
demo_helper.rb
# frozen_string_literal: true module Demo # IO trap class Output def initialize(klass)=@prefix = klass.to_s def <<(obj)=puts "#{@prefix}: #{obj.inspect}" def close()=puts "#{@prefix} closed." end # decouple from tty if demo run interactively def self.input_pipe(source) return source unless $stdin.tty? reader, writer = IO.pipe Thread.new do IO.copy_stream(source, writer) ensure writer.close unless writer.closed? end reader end # one-shot method def self.launcher(&)=at_exit(&) && def self.launcher()=?? # Example registry @example_classes = [] class << self def const_added(const_name) super return unless const_get(const_name, false) in Class => klass register(klass) end def register(klass) @example_classes << klass end def setup_examples @example_classes.map { |k| k.new(Output.new(k)) } end end end