State machine for handling Telnet sequences for an FTP server
As suggested by @m_x, This solution uses a parser of sorts, driven by the StringScanner built-in class.  This is very compact, pretty readable, and gets rid of the state machine altogether:

Some methods to handle actions:

def accept(scanner)   
  @plain &lt;&lt; scanner[1]
end

def reply_dont(scanner)
  @reply &lt;&lt; IAC + DONT + scanner[1]
end

def reply_wont(scanner)
  @reply &lt;&lt; IAC + WONT + scanner[1]
end

def ignore(scanner)
end


A list of telnet sequences:

# Telnet sequences to handle, and how to handle them

SEQUENCES = [
  [/#{IAC}(#{IAC})/, :accept],
  [/#{IAC}#{WILL}(.)/m, :reply_dont],
  [/#{IAC}#{WONT}(.)/m, :ignore],
  [/#{IAC}#{DO}(.)/m, :reply_wont],
  [/#{IAC}#{DONT}(.)/m, :ignore],
  [/#{IAC}#{IP}/, :ignore],
  [/#{IAC}#{DM}/, :ignore],
  [/(.)/m, :accept],
]


And the parser that uses them:

# Parse the the command.  Sets @plain and @reply

def parse_command(command)
  @plain = ''
  @reply = ''
  scanner = StringScanner.new(command)
  while !scanner.eos?
    SEQUENCES.each do |regexp, method|
      if scanner.scan(regexp)
        send method, scanner
        break
      end
    end
  end
end