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
endnum = 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
endWe can then allow certain types to be involved in arithmetic, e.g.
n1 = Byte.new(1)
n2 = Megabyte.new(2)
n1 + n2
# => 1000001 byteDo we need to add pluralization, so:
Byte.new(2)
# => 2 bytesThis 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 byteAnd sugar:
Byte.new(2).to_megabyte
Mile.new(2).to_kilometersWhere 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
endBut 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)
# => {} byteSo 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
endThere 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.