So, a couple bugs:

Phrogz wrote:
>   # Low-order bytes appear first in the string.
Not necessarily
>     if pack_code = { 1=>'C', 2=>'S', 4=>'L' }[ num_chars ]
in Array.pack, 'S', and 'L', use system-endian order.
To use little endian order use 'v' and 'V', respectively.

Personally, I think it's cleaner to just leave out the clause
altogether.
[...]
>     unless num_chars
>       bits_needed = Math.log( self ) / Math::LOG2
>       num_chars = ( bits_needed / 8.0 ).ceil
>     end
You're ignoring a bunch of edge cases here.
0 and 1, for example.

For 0, Math.log(0) == -Infinity, which will throw an error when 'ceil'
is called.
For 1, Math.log(1) == 0, so num_chars == 0, which isn't quite right
either.

You'll run into the same issue at other powers of 256, as well.

What you should actually do, instead of taking the ceiling, is round
down and add one.

You're also ignoring negative integers.  Math.log doesn't like negative
numbers either, so you'll need to take the absolute value.  This
doesn't solve the problem when you're reading back in, of
differentiating between positive and negative integers that have the
same byte code.

Here's my code.  I ignore the positive/negative reading back in
problem.

#!/usr/bin/env ruby -w
# The natural log of 256 (for base-256 logarithms)
Math::LOG256 = Math.log( 256 )

class Integer
  # Converts the integer into a string, where the value of each
  # character is a byte in the bit representation of the integer,
  # low order bytes first.
  #
  # If the number of characters is not specified, the fewest number of
  # characters that can represent the integer is used.
  def to_bytestring( num_octets=nil )

    # find out how many octets are required to encode it
    num_octets ||=
      (self == 0) ? 1 : ( Math.log(self.abs) / Math::LOG256 ).to_i + 1

    # encode the low num_octets bytes of the integer.
    shifted = (self << 8)
    Array.new(num_octets).map do
      ((shifted >>= 8) & 0xFF).chr
    end.join
  end

  # Creates an integer from a byte string.
  # See Integer#to_bytestring for more information.
  # NOTE: reads in negative integers as positive
  def self.from_bytestring( str )
    str.reverse.split(//).inject(0) do |val, char|
      (val << 8) | char[0]
    end
  end
end

[
  -1,
  0x0,
  0x1,
  255,
  256,
  -255,
  -256,
  0x48,
  26952,
  0x6948,
  0x646c726f57206f6c6c6548,
  0x217962755220666f207463657073612074656577732061207369206d756e676942
].each{ |i|
  begin
    p [i, i.to_bytestring, Integer.from_bytestring(i.to_bytestring)]
  rescue StandardError => err
    p err
  end
}