class Lingo::Attendee::TextReader

Der TextReader ist eine klassische Datenquelle. Er liest eine oder mehrere Dateien und gibt sie Zeilenweise in den Ausgabekanal. Der Start bzw. Wechsel einer Datei wird dabei über den Kommandokanal angekündigt, ebenso wie das Ende.

Der TextReader kann ebenfalls ein spezielles Dateiformat verarbeiten, welches zum Austausch mit dem LIR-System dient. Dabei enthält die Datei Record-basierte Informationen, die wie mehrere Dateien verarbeitet werden.

Mögliche Verlinkung

Erzeugt

Daten des Typs String (Textzeile) z.B. für Tokenizer, Textwriter

Parameter

Kursiv dargestellte Parameter sind optional (ggf. mit Angabe der Voreinstellung). Alle anderen Parameter müssen zwingend angegeben werden.

out

siehe allgemeine Beschreibung des Attendee

files

Es können eine oder mehrere Dateien angegeben werden, die nacheinander eingelesen und zeilenweise weitergeleitet werden. Die Dateien werden mit Komma voneinander getrennt, z.B.

files: 'readme.txt'
files: 'readme.txt,lingo.cfg'
records

Mit diesem Parameter wird angegeben, woran der Anfang eines neuen Records erkannt werden kann und wie die Record-Nummer identifiziert wird. Das Format einer LIR-Datei ist z.B.

[00001.]
020: ¬Die Aufgabenteilung zwischen Wortschatz und Grammatik.

[00002.]
020: Nicht-konventionelle Thesaurusrelationen als Orientierungshilfen.

Mit der Angabe von

records: "^\[(\d+)\.\]"

werden die Record-Zeilen erkannt und jeweils die Record-Nummer 00001, bzw. 00002 erkannt.

Generierte Kommandos

Damit der nachfolgende Datenstrom einwandfrei verarbeitet werden kann, generiert der TextReader Kommandos, die mit in den Datenstrom eingefügt werden.

*FILE(<dateiname>)

Kennzeichnet den Beginn der Datei <dateiname>

*EOF(<dateiname>)

Kennzeichnet das Ende der Datei <dateiname>

*LIR_FORMAT(")

Kennzeichnet die Verarbeitung einer Datei im LIR-Format (nur bei LIR-Format).

*RECORD(<nummer>)

Kennzeichnet den Beginn eines neuen Records (nur bei LIR-Format).

Beispiele

Bei der Verarbeitung einer normalen Textdatei mit der Ablaufkonfiguration t1.cfg

meeting:
  attendees:
    - text_reader: { out: lines, files: '$(files)' }
    - debugger:    { in: lines, prompt: 'out>' }

ergibt die Ausgabe über den Debugger: lingo -c t1 test.txt

out> *FILE('test.txt')
out> "Dies ist eine Zeile."
out> "Dies ist noch eine."
out> *EOF('test.txt')

Bei der Verarbeitung einer LIR-Datei mit der Ablaufkonfiguration t2.cfg

meeting:
  attendees:
    - text_reader: { out: lines,  files: '$(files)', records: "^\[(\d+)\.\]" }
    - debugger:    { in: lines, prompt: 'out>'}

ergibt die Ausgabe mit lingo -c t2 lir.txt

out> *LIR-FORMAT('')
out> *FILE('lir.txt')
out> *RECORD('00001')
out> "020: \254Die Aufgabenteilung zwischen Wortschatz und Grammatik."
out> *RECORD('00002')
out> "020: Nicht-konventionelle Thesaurusrelationen als Orientierungshilfen."
out> *EOF('lir.txt')

Protected Instance Methods

control(cmd, param) click to toggle source
# File lib/lingo/attendee/text_reader.rb, line 122
def control(cmd, param)
  if cmd == STR_CMD_TALK
    forward(STR_CMD_LIR, '') if @lir
    @files.each(&method(:spool))
  end
end
init() click to toggle source

TODO: FILE und LIR-FILE (?)

# File lib/lingo/attendee/text_reader.rb, line 110
def init
  get_files

  @chomp    = get_key('chomp', true)
  @filter   = get_key('filter', false)
  @progress = get_key('progress', false)

  if @lir = get_key('records', get_key('lir-record-pattern', nil))  # DEPRECATE lir-record-pattern

    @lir = @lir == true ? %r{^\[(\d+)\.\]} : Regexp.new(@lir)
  end
end

Private Instance Methods

add_files(path, glob, recursive = false) click to toggle source
# File lib/lingo/attendee/text_reader.rb, line 227
def add_files(path, glob, recursive = false)
  Dir[path].sort!.each { |match|
    File.directory?(match) ? recursive ? Find.find(match) { |entry|
      @files << entry if File.file?(entry) && File.fnmatch?(glob, entry)
    } : add_files(File.join(match, glob), glob) : @files << match
  }.empty? and raise FileNotFoundError.new(path)
end
file_type(path, io) click to toggle source
# File lib/lingo/attendee/text_reader.rb, line 195
def file_type(path, io)
  if Object.const_defined?(:FileMagic) && io.respond_to?(:rewind)
    FileMagic.fm(:mime, simplified: true).buffer(io.read(256)).tap {
      io.rewind
    }
  elsif Object.const_defined?(:MIME) && MIME.const_defined?(:Types)
    MIME::Types.of(path).first.tap { |type| type ? type.content_type :
      warn('Filters not available. File type could not be determined.')
    }
  else
    warn "Filters not available. Please install `ruby-filemagic' or `mime-types'."
    nil
  end
end
filter(path, stdin = stdin?(path)) { |line, 0| ... } click to toggle source
# File lib/lingo/attendee/text_reader.rb, line 160
def filter(path, stdin = stdin?(path))
  io, block = stdin ? [
    @lingo.config.stdin.set_encoding(ENC),
    lambda { |line| yield line, 0 }
  ] : [
    File.open(path, 'rb', encoding: ENC),
    lambda { |line| yield line, io.pos }
  ]

  case @filter == true ? file_type(path, io) : @filter.to_s
    when %rhtml/ then io = filter_html(io)
    when %rxml/  then io = filter_html(io, true)
    when %rpdf/  then filter_pdf(io, &block); return
  end

  io.each_line(&block) if io
end
filter_html(io, xml = false) click to toggle source
# File lib/lingo/attendee/text_reader.rb, line 186
def filter_html(io, xml = false)
  if Object.const_defined?(:Hpricot)
    Hpricot(io, xml: xml).inner_text
  else
    warn "#{xml ? 'X' : 'HT'}ML filter not available. Please install `hpricot'."
    nil
  end
end
filter_pdf(io, &block) click to toggle source
# File lib/lingo/attendee/text_reader.rb, line 178
def filter_pdf(io, &block)
  if Object.const_defined?(:PDF) && PDF.const_defined?(:Reader)
    PDFFilter.filter(io, &block)
  else
    warn "PDF filter not available. Please install `pdf-reader'."
  end
end
get_files() click to toggle source
# File lib/lingo/attendee/text_reader.rb, line 214
def get_files
  args = [get_key('glob', '*.txt'), get_key('recursive', false)]

  @files = []

  Array(get_key('files', '-')).each { |path|
    stdin?(path) ? @files << path : add_files(path, *args)
  }

  @files.map!(&File.method(:expand_path))
  @files.uniq!
end
spool(path) click to toggle source

Gibt eine Datei zeilenweise in den Ausgabekanal

# File lib/lingo/attendee/text_reader.rb, line 132
def spool(path)
  unless stdin = stdin?(path)
    inc('Anzahl Dateien')
    add('Anzahl Bytes', size = File.size(path))

    size = nil unless @progress
  end

  forward(STR_CMD_FILE, path)

  ShowProgress.new(self, size, path) { |progress|
    filter(path, stdin) { |line, pos|
      inc('Anzahl Zeilen')
      progress[pos]

      line.chomp! if @chomp

      if line =~ @lir
        forward(STR_CMD_RECORD, $1)
      else
        forward(line) unless line.empty?
      end
    }
  }

  forward(STR_CMD_EOF, path)
end
stdin?(path) click to toggle source
# File lib/lingo/attendee/text_reader.rb, line 210
def stdin?(path)
  %w[STDIN -].include?(path)
end