Какая разница между sub и sub!?

В sub генерируется и возвращается копия самого объекта. В sub! сам объект меняется и возвращается в том случае, если была произведена замена. Иначе возвращается nil. Методы, подобные sub!, называются деструктивными - они изменяют атрибут самого объекта. Если есть два похожих метода, и один из них деструктивный, он имеет суффикс !.

  def foo(str)
      str = str.sub(/foo/, "baz")
  end

  obj = "foo"
  puts foo(obj)     #=> baz
  puts obj          #=> foo

  def foo(str)
      str = str.sub!(/foo/, "baz")
  end

  puts foo(obj)     #=> baz
  puts obj          #=> baz