Method: Kernel#number_to_human_size

Defined in:
lib/quality_extensions/helpers/numbers.rb

#number_to_human_size(number, *args) ⇒ Object

Formats the bytes in size into a more understandable representation (e.g., giving it 1500 yields 1.5 KB). This method is useful for reporting file sizes to users. This method returns nil if size cannot be converted into a number. You can customize the format in the options hash.

Options

  • :base - Pass in 2 (or 1024) to use binary units (KiB, MiB),

    pass in 10 (or 1000) to use SI (decimal) units (KB, MB)
    (defaults to base 10).
    
  • :precision - Sets the level of precision (defaults to 1).

  • :separator - Sets the separator between the units (defaults to “.”).

  • :delimiter - Sets the thousands delimiter (defaults to “”).

Examples

number_to_human_size(123)                                          # => 123 Bytes
number_to_human_size(1234)                                         # => 1.2 KB
number_to_human_size(12345)                                        # => 12.3 KB
number_to_human_size(1234567)                                      # => 1.2 MB
number_to_human_size(1234567890)                                   # => 1.2 GB
number_to_human_size(1234567890123)                                # => 1.2 TB
number_to_human_size(1234567, :precision => 2)                     # => 1.23 MB
number_to_human_size(1234567, :precision => 2, :base => 2)         # => 1.18 MiB
number_to_human_size(483989, :precision => 0)                      # => 484 KB
number_to_human_size(483989, :precision => 0, :base => 2)          # => 473 KiB
number_to_human_size(1234567, :precision => 2, :separator => ',')  # => 1,23 MB

Differences from ActiveSupport version

The ActiveSupport version defaults to binary (base 2) units, while this one
defaults to SI (base 10) units.

The ActiveSupport incorrectly uses KB to refer to binary units, when the correct
abbreviation would be KiB (see http://en.wikipedia.org/wiki/Binary_prefix).

This version has a :base option to let you change the base; the ActiveSupport
version does not.


142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/quality_extensions/helpers/numbers.rb', line 142

def number_to_human_size(number, *args)
  return nil if number.nil?

  options = args.extract_options!
  options.symbolize_keys!

  precision ||= (options[:precision] || 1)
  separator ||= (options[:separator] || '.')
  delimiter ||= (options[:delimiter] || ',')
  base      ||= (options[:base]      || 10)

  # http://en.wikipedia.org/wiki/Binary_prefix
  if base == 10 || base == 1000
    storage_units = %w( Bytes KB MB GB TB ).freeze
    base = 1000
  elsif base == 2 || base == 1024
    storage_units = %w( Bytes KiB MiB GiB TiB ).freeze
    base = 1024
  else
    raise ArgumentError, "base must be 1000 or 1024"
  end
  storage_units_format = '%n %u'

  if number.to_i < base
    unit = number.to_i == 1 ? 'byte' : 'bytes'
    storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
  else
    max_exp  = storage_units.size - 1
    number   = Float(number)
    exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base 1024
    exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
    number  /= base ** exponent

    unit = storage_units[exponent]

    begin
      escaped_separator = Regexp.escape(separator)
      formatted_number = number_with_precision(number,
        :precision => precision,
        :separator => separator,
        :delimiter => delimiter
      ).sub(/(\d)(#{escaped_separator}[1-9]*)?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
      storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
    #rescue
    #  number
    end
  end
end