class Sixword::CLI

The Sixword::CLI class implements all of the complex processing needed for the sixword Command Line Interface.

Attributes

filename[R]

@return [String] Input filename

mode[R]

@return [:encode, :decode]

options[R]

@return [Hash] Options hash

stream[R]

@return [File, IO] Stream opened from filename

Public Class Methods

new(filename, options) click to toggle source

Create a Sixword CLI to operate on filename with options

@param filename [String] Input file name (or '-' for stdin) @param options [Hash]

@option options [:encode, :decode] :mode (:encode) @option options [Boolean] :pad (false) @option options [String] :hex_style @option options [Integer] :line_width (1) In encode mode, the number of

sentences to output per line.
# File lib/sixword/cli.rb, line 33
def initialize(filename, options)
  @filename = filename
  @options = {mode: :encode, pad: false}.merge(options)

  if filename == '-'
    # dup stdin and put it into binary mode
    @stream = $stdin.dup
    @stream.binmode
  else
    # read in binary mode even on unix so ruby yields binary encoding
    @stream = File.open(filename, 'rb')
  end


  @mode = @options.fetch(:mode)
  unless [:encode, :decode].include?(mode)
    raise ArgumentError.new("Invalid mode: #{mode.inspect}")
  end
end

Public Instance Methods

encoding?() click to toggle source

Return true if we are in encoding mode, false otherwise (decoding). @return [Boolean]

# File lib/sixword/cli.rb, line 61
def encoding?
  mode == :encode
end
hex_style() click to toggle source

Return the value of the :hex_style option. @return [String, nil]

# File lib/sixword/cli.rb, line 67
def hex_style
  options[:hex_style]
end
pad?() click to toggle source

Return the value of the :pad option. @return [Boolean]

# File lib/sixword/cli.rb, line 55
def pad?
  options.fetch(:pad)
end
print_hex(data, chunk_index, cols=80) click to toggle source

Format data as hex in various styles.

run!() click to toggle source

Run the encoding/decoding operation, printing the result to stdout.

# File lib/sixword/cli.rb, line 96
def run!
  if encoding?
    do_encode! do |encoded|
      puts encoded
    end
  else
    chunk_index = 0
    do_decode! do |decoded|
      if hex_style
        print_hex(decoded, chunk_index)
        chunk_index += 1
      else
        print decoded
      end
    end

    # add trailing newline for hex output
    puts if hex_style
  end
end

Private Instance Methods

accumulate_hex_input() { |buf| ... } click to toggle source
# File lib/sixword/cli.rb, line 213
def accumulate_hex_input
  unless block_given?
    raise ArgumentError.new("must pass block")
  end

  # these are actually treated the same at the moment
  case hex_style
  when 'lower', 'lowercase'
  when 'finger', 'fingerprint'
  else
    raise CLIError.new("unknown hex style: #{hex_style.inspect}")
  end

  while true
    buf = ''

    # try to accumulate 8 bytes (16 chars) before yielding the hex
    while buf.length < 16
      char = stream.getc
      if char.nil?
        # EOF, so yield whatever we have if it's non-empty
        yield buf unless buf.empty?
        return
      end

      if Sixword::Hex.valid_hex?(char)
        buf << char
        next
      elsif Sixword::Hex.strip_char?(char)
        next
      end

      raise CLIError.new("invalid hex character: #{char.inspect}")
    end

    yield buf
  end
end
do_decode!() { |decode(arr, padding_ok: pad?)| ... } click to toggle source
# File lib/sixword/cli.rb, line 119
def do_decode!
  unless block_given?
    raise ArgumentError.new("block is required")
  end

  read_input_by_6_words do |arr|
    yield Sixword.decode(arr, padding_ok: pad?)
  end
end
do_encode!() { |join(' ')| ... } click to toggle source
# File lib/sixword/cli.rb, line 129
def do_encode!
  sentences_per_line = options.fetch(:line_width, 1)
  if sentences_per_line <= 0
    sentences_per_line = 1 << 32
  end

  sentences = []

  process_encode_input do |chunk|
    Sixword.encode_iter(chunk, words_per_slice:6, pad:pad?) do |encoded|
      sentences << encoded

      # yield sentences once we reach sentences_per_line of them
      if sentences.length >= sentences_per_line
        yield sentences.join(' ')
        sentences.clear
      end
    end
  end

  # yield any leftover sentences
  unless sentences.empty?
    yield sentences.join(' ')
  end
end
process_encode_input() { |data| ... } click to toggle source
# File lib/sixword/cli.rb, line 155
def process_encode_input
  unless block_given?
    raise ArgumentError.new("block is required")
  end

  if hex_style
    # yield data in chunks from accumulate_hex_input until EOF
    accumulate_hex_input do |hex|
      begin
        data = Sixword::Hex.decode(hex)
      rescue ArgumentError => err
        # expose hex decoding failures to user
        raise CLIError.new(err.message)
      else
        yield data
      end
    end
  else
    # yield data 8 bytes at a time until EOF
    while true
      buf = stream.read(8)
      if buf
        yield buf
      else
        # EOF
        break
      end
    end
  end
end
read_input_by_6_words() { |word_arr| ... } click to toggle source

Yield data 6 words at a time until EOF

# File lib/sixword/cli.rb, line 187
def read_input_by_6_words
  word_arr = []

  while true
    line = stream.gets
    if line.nil?
      break # EOF
    end

    line.scan(/\S+/) do |word|
      word_arr << word

      # return the array if we have accumulated 6 words
      if word_arr.length == 6
        yield word_arr
        word_arr.clear
      end
    end
  end

  # yield whatever we have left, if anything
  if !word_arr.empty?
    yield word_arr
  end
end