Class: Measurement

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby-measurement/unit.rb,
lib/ruby-measurement/version.rb,
lib/ruby-measurement/measurement.rb

Defined Under Namespace

Classes: Unit

Constant Summary collapse

VERSION =
'1.0.0'
UNIT_REGEX =
/([a-zA-Z].*)/.freeze
SCIENTIFIC_NUMBER =
/([+-]?\d*\.?\d+(?:[Ee][+-]?)?\d*)/.freeze
SCIENTIFIC_REGEX =
/\A#{SCIENTIFIC_NUMBER}\s*#{UNIT_REGEX}?\z/.freeze
RATIONAL_REGEX =
/\A([+-]?\d+\s+)?((\d+)\/(\d+))?\s*#{UNIT_REGEX}?\z/.freeze
COMPLEX_REGEX =
/\A#{SCIENTIFIC_NUMBER}?#{SCIENTIFIC_NUMBER}i\s*#{UNIT_REGEX}?\z/.freeze

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(quantity, unit_name = :count) ⇒ Measurement

Returns a new instance of Measurement.

Raises:

  • (ArgumentError)


16
17
18
19
20
21
22
23
24
25
# File 'lib/ruby-measurement/measurement.rb', line 16

def initialize(quantity, unit_name = :count)
  unit = unit_name
  unit = self.class.units[unit_name.to_s] if unit_name.kind_of?(Symbol) || unit_name.kind_of?(String)
  
  raise ArgumentError, "Invalid quantity: #{quantity}" unless quantity.kind_of?(Numeric)
  raise ArgumentError, "Invalid unit: #{unit_name}" unless unit.kind_of?(Unit)
  
  @quantity = quantity
  @unit = unit
end

Class Attribute Details

.unitsObject

Returns the value of attribute units.



13
14
15
# File 'lib/ruby-measurement/measurement.rb', line 13

def units
  @units
end

Instance Attribute Details

#quantityObject (readonly)

Returns the value of attribute quantity.



11
12
13
# File 'lib/ruby-measurement/measurement.rb', line 11

def quantity
  @quantity
end

#unitObject (readonly)

Returns the value of attribute unit.



11
12
13
# File 'lib/ruby-measurement/measurement.rb', line 11

def unit
  @unit
end

Class Method Details

.define(unit_name, &block) ⇒ Object



161
162
163
164
# File 'lib/ruby-measurement/measurement.rb', line 161

def self.define(unit_name, &block)
  unit = Unit.new(unit_name, &block)
  unit.aliases.each { |a| @units[a.downcase] = unit }
end

.parse(str = '0') ⇒ Object

Raises:

  • (ArgumentError)


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/ruby-measurement/measurement.rb', line 131

def self.parse(str = '0')
  str = str.strip
  
  if str =~ COMPLEX_REGEX
    real, imaginary, unit_name = str.scan(COMPLEX_REGEX).first
    quantity = Complex(real.to_f, imaginary.to_f).to_f
  elsif str =~ SCIENTIFIC_REGEX
    whole, unit_name = str.scan(SCIENTIFIC_REGEX).first
    quantity = whole.to_f
  elsif str =~ RATIONAL_REGEX
    whole, _, numerator, denominator, unit_name = str.scan(RATIONAL_REGEX).first

    if numerator && denominator
      numerator = numerator.to_f + (denominator.to_f * whole.to_f)
      denominator = denominator.to_f
      quantity = Rational(numerator, denominator).to_f
    else
      quantity = whole.to_f
    end
  else
    raise ArgumentError, "Unable to parse: '#{str}'"
  end
  
  unit_name ||= 'count'
  unit = units[unit_name.strip.downcase]
  raise ArgumentError, "Invalid unit: '#{unit_name}'" unless unit
  
  new(quantity, unit)
end

.unit(unit_name) ⇒ Object



126
127
128
129
# File 'lib/ruby-measurement/measurement.rb', line 126

def self.unit(unit_name)
  unit_name = unit_name.to_s
  @units.values.find { |unit| unit.aliases.include?(unit_name) }
end

Instance Method Details

#*(obj) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/ruby-measurement/measurement.rb', line 65

def *(obj)
  case obj
  when Numeric
    self.class.new(quantity * obj.to_f, unit)
  when self.class
    if obj.unit == unit
      self.class.new(quantity * obj.quantity, unit)
    else
      self * obj.convert_to(unit)
    end
  else
    raise ArgumentError, "Invalid arithmetic: #{self} * #{obj}"
  end
end

#**(obj) ⇒ Object



95
96
97
98
99
100
101
102
# File 'lib/ruby-measurement/measurement.rb', line 95

def **(obj)
  case obj
  when Numeric
    self.class.new(quantity ** obj.to_f, unit)
  else
    raise ArgumentError, "Invalid arithmetic: #{self} ** #{obj}"
  end
end

#+(obj) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/ruby-measurement/measurement.rb', line 35

def +(obj)
  case obj
  when Numeric
    self.class.new(quantity + obj.to_f, unit)
  when self.class
    if obj.unit == unit
      self.class.new(quantity + obj.quantity, unit)
    else
      self + obj.convert_to(unit)
    end
  else
    raise ArgumentError, "Invalid arithmetic: #{self} + #{obj}"
  end
end

#-(obj) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/ruby-measurement/measurement.rb', line 50

def -(obj)
  case obj
  when Numeric
    self.class.new(quantity - obj.to_f, unit)
  when self.class
    if obj.unit == unit
      self.class.new(quantity - obj.quantity, unit)
    else
      self - obj.convert_to(unit)
    end
  else
    raise ArgumentError, "Invalid arithmetic: #{self} - #{obj}"
  end
end

#/(obj) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/ruby-measurement/measurement.rb', line 80

def /(obj)
  case obj
  when Numeric
    self.class.new(quantity / obj.to_f, unit)
  when self.class
    if obj.unit == unit
      self.class.new(quantity / obj.quantity, unit)
    else
      self / obj.convert_to(unit)
    end
  else
    raise ArgumentError, "Invalid arithmetic: #{self} / #{obj}"
  end
end

#==(obj) ⇒ Object



104
105
106
# File 'lib/ruby-measurement/measurement.rb', line 104

def ==(obj)
  obj.kind_of?(self.class) && quantity == obj.quantity && unit == obj.unit
end

#convert_to(unit_name) ⇒ Object

Raises:

  • (ArgumentError)


108
109
110
111
112
113
114
115
116
117
118
# File 'lib/ruby-measurement/measurement.rb', line 108

def convert_to(unit_name)
  unit = self.class.unit(unit_name)
  raise ArgumentError, "Invalid unit: '#{unit_name}'" unless unit
  
  return dup if unit == @unit
  
  conversion = @unit.conversion(unit.name)
  raise ArgumentError, "Invalid conversion: '#@unit' to '#{unit.name}'" unless conversion
  
  self.class.new(conversion.call(@quantity), unit.name)
end

#convert_to!(unit_name) ⇒ Object



120
121
122
123
124
# File 'lib/ruby-measurement/measurement.rb', line 120

def convert_to!(unit_name)
  measurement = convert_to(unit_name)
  @unit, @quantity = measurement.unit, measurement.quantity
  self
end

#inspectObject



27
28
29
# File 'lib/ruby-measurement/measurement.rb', line 27

def inspect
  to_s
end

#to_sObject



31
32
33
# File 'lib/ruby-measurement/measurement.rb', line 31

def to_s
  "#{quantity} #{unit}"
end