Often we have Integer’s but don’t have a clue what the unit is unless we encode it in a varible name. How can we prevent adding two numbers which are of different or incompatible units…

class IntegerWithUnit < SimpleDelegator
  attr_reader :unit
  
  def initialize(integer, unit)   
    @unit = unit.to_sym.freeze
    super(integer)
  end 
  
  def unit?(unit)
    @unit == unit.to_sym
  end
  
  def to_int
    self
  end
  
  def to_s
    super + " " + unit.to_s
  end
  
  def to_str
    to_s
  end
  
  def inspect
    to_s
  end
end
num = IntegerWithUnit.new(1, :byte)
# => 1 byte

"Remaining: #{num}"
# => "Remaining: 1 byte"

num + 2
# => 3

(num + 2).class
# => Integer
# oops, we need to return an IntegerWithUnit

IntegerWithUnit.new(1, :byte) == IntegerWithUnit.new(1, :mile)
# => true
# oops, no it doesn't...
class Byte < IntegerWithUnit
  def initialize(number)
    super(number, :byte)
  end
end

We can then allow certain types to be involved in arithmetic, e.g.

n1 = Byte.new(1)
n2 = Megabyte.new(2)

n1 + n2 
# => 1000001 byte

Do we need to add pluralization, so:

Byte.new(2)
# => 2 bytes

This would need an inflection library, or it is an optional arguments, e.g.:

IntegerWithUnit.new(2, [:byte, :bytes])

What about Floats?

FloatWithUnit.new(2.0, :miles)
# => 2.0 mile

IntegerWithUnit.new(2, :bytes).to_f 
#=> 2.0 byte

And sugar:

Byte.new(2).to_megabyte
Mile.new(2).to_kilometers

Where do we store the conversion ratios between units?

In a chosen base unit, e.g. Byte has all conversions to Megabyte, Gigabyte etc. What about going from say kilometers to miles…

Maybe we have to convert to a base unit, do addition, and then back to the target unit. Each class has a conversion to the base unit, so Byte::ToByte = 1, Kilobyte::ToByte = 1000 etc.

If this where to be gemified then we would want to wrap all class in a module instead of poluting the root namespace. In which case we could have:

module Unit
  class Integer < SimpleDelegator
    attr_reader :unit
  
    def initialize(integer, unit)   
      @unit = unit.to_sym.freeze
      super(integer)
    end 
    
    # ...
  end
  
  class Byte < Integer
  end
end

But then does a Unit::Byte inherit from Unit::Integer or Unit::Float?

Would we need a base class, ValueWithUnit, which picks the correct class, Integer versus Float… Is it even needed since we are delegating to the passed in value…

IntegerWithUnit.new(1.2, :byte)
# => 1.2 byte

IntegerWithUnit.new({}, :byte)
# => {} byte

So what about:

module Unit
  class Object < SimpleDelegator
    attr_reader :unit
  
    def initialize(obj, unit)   
      @unit = unit.to_sym.freeze
      super(obj)
    end 
    
    # ...
  end
  
  class Byte < Object
    def initialize(number)   
      super(number, :byte)
    end
  end
end

There are so many different units they might need to be in seperate gems, e.g. bitcoin denominations.

From here it is proberbly best to write a README showing the desired API and then copy the examples to form the basis of the feature specs.