Class: Measure

Inherits:
Object
  • Object
show all
Defined in:
lib/measure.rb,
lib/measure/version.rb

Overview

Author

Kenta Murata

Copyright

Copyright © 2008 Kenta Murata

License

LGPL version 3.0

Defined Under Namespace

Modules: VERSION Classes: CompatibilityError, InvalidUnitError, UnitRedefinitionError

Constant Summary collapse

@@units =
[]
@@dimension_map =
{}
@@conversion_map =
{}
@@alias_map =
{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value, unit) ⇒ Measure

class << self



138
139
140
141
# File 'lib/measure.rb', line 138

def initialize(value, unit)
  @value, @unit = value, unit
  return nil
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object



329
330
331
332
333
334
335
# File 'lib/measure.rb', line 329

def method_missing(name, *args)
  if /^as_(\w+)/.match(name.to_s)
    unit = $1.to_sym
    return convert(unit)
  end
  return saved_method_missing(name, *args)
end

Instance Attribute Details

#unitObject (readonly)

Returns the value of attribute unit.



143
144
145
# File 'lib/measure.rb', line 143

def unit
  @unit
end

#valueObject (readonly)

Returns the value of attribute value.



143
144
145
# File 'lib/measure.rb', line 143

def value
  @value
end

Class Method Details

.clear_unitsObject



39
40
41
42
43
44
45
# File 'lib/measure.rb', line 39

def clear_units
  @@units.clear
  @@dimension_map.clear
  @@conversion_map.clear
  @@alias_map.clear
  return nil
end

.compatible?(u1, u2) ⇒ Boolean

Returns:

  • (Boolean)

Raises:



28
29
30
31
32
33
34
35
36
37
# File 'lib/measure.rb', line 28

def compatible?(u1, u2)
  u1 = resolve_alias u1
  raise InvalidUnitError, "unknown unit: #{u1}" unless self.defined? u1
  u2 = resolve_alias u2
  raise InvalidUnitError, "unknown unit: #{u2}" unless self.defined? u2
  return true if u1 == u2
  return true if @@conversion_map.has_key? u1 and @@conversion_map[u1].has_key? u2
  return true if @@conversion_map.has_key? u2 and @@conversion_map[u2].has_key? u1
  return false
end

.conversion_mapObject



18
19
20
# File 'lib/measure.rb', line 18

def conversion_map
  @@conversion_map
end

.define_alias(unit, base) ⇒ Object

Raises:



67
68
69
70
71
72
73
# File 'lib/measure.rb', line 67

def define_alias(unit, base)
  if self.defined?(unit)
    raise UnitRedefinitionError, "unit [#{unit}] is already defined"
  end
  raise InvalidUnitError, "unknown unit: #{base}" unless self.defined? base
  @@alias_map[unit] = base
end

.define_conversion(base, conversion) ⇒ Object

Raises:



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/measure.rb', line 75

def define_conversion(base, conversion)
  base = resolve_alias base
  raise InvalidUnitError, "unknown unit: #{base}" unless self.defined? base
  @@conversion_map[base] ||= {}
  conversion.each {|unit, factor|
    unit = resolve_alias unit
    raise InvalidUnitError, "unknown unit: #{unit}" unless self.defined? unit
    @@conversion_map[base][unit] = factor
  }
  return nil
end

.define_unit(unit, dimension = 1) ⇒ Object



56
57
58
59
60
61
62
63
64
65
# File 'lib/measure.rb', line 56

def define_unit(unit, dimension=1)
  if @@units.include?(unit)
    if self.dimension(unit) != dimension
      raise UnitRedefinitionError, "unit [#{unit}] is already defined"
    end
  else
    @@units << unit
    @@dimension_map[unit] = dimension
  end
end

.dimension(unit) ⇒ Object Also known as: dim



87
88
89
# File 'lib/measure.rb', line 87

def dimension(unit)
  return @@dimension_map[resolve_alias unit]
end

.find_multi_hop_conversion(u1, u2) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/measure.rb', line 99

def find_multi_hop_conversion(u1, u2)
  visited = []
  queue = [[u1]]
  while route = queue.shift
    next if visited.include? route.last
    visited.push route.last
    return route if route.last == u2
    neighbors(route.last).each{|u|
      queue.push(route + [u]) unless visited.include? u }
  end
  return nil
end

.has_unit?(unit) ⇒ Boolean Also known as: defined?

Returns:

  • (Boolean)


22
23
24
25
# File 'lib/measure.rb', line 22

def has_unit?(unit)
  unit = resolve_alias unit
  return @@units.include? unit
end

.num_unitsObject



52
53
54
# File 'lib/measure.rb', line 52

def num_units
  return @@units.length
end

.resolve_alias(unit) ⇒ Object



92
93
94
95
96
97
# File 'lib/measure.rb', line 92

def resolve_alias(unit)
  while @@alias_map.has_key? unit
    unit = @@alias_map[unit]
  end
  return unit
end

.units(dimension = nil) ⇒ Object



47
48
49
50
# File 'lib/measure.rb', line 47

def units(dimension=nil)
  return @@units.dup if dimension.nil?
  @@dimension_map.select {|k, v| v == dimension }.collect{|k, v| k }
end

.with_short_formObject Also known as: form



34
35
36
37
38
39
40
41
# File 'lib/measure/support.rb', line 34

def with_short_form
  begin
    MeasureSupport.enable
    return yield
  ensure
    MeasureSupport.disable
  end
end

Instance Method Details

#*(other) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/measure.rb', line 226

def *(other)
  case other
  when Measure
    # TODO: dimension
    return other * self.value if self.unit == 1
    raise NotImplementedError, "this feature has not implemented yet"
#       if self.unit == other.unit
#         return Measure(self.value * other.value, self.unit)
#       elsif Measure.dim(self.unit) == Measure.dim(other.unit)
#         return Measure(self.value - other.convert(self.unit).value, self.unit)
#       else
#         return Measure(self.value * other.convert(self.unit).value, self.unit)
#       end
  when Numeric
    return Measure(self.value * other, self.unit)
  else
    check_coercable other
    a, b = other.coerce self
    return a * b
  end
end

#+(other) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/measure.rb', line 186

def +(other)
  case other
  when Measure
    if self.unit == other.unit
      return Measure(self.value + other.value, self.unit)
    elsif Measure.dim(self.unit) == Measure.dim(other.unit)
      return Measure(self.value + other.convert(self.unit).value, self.unit)
    else
      raise TypeError, "incompatible dimensions: " +
        "#{Measure.dim(self.unit)} and #{Measure.dim(other.unit)}"
    end
  when Numeric
    return Measure(self.value + other, self.unit)
  else
    check_coercable other
    a, b = other.coerce self
    return a + b
  end
end

#-(other) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/measure.rb', line 206

def -(other)
  case other
  when Measure
    if self.unit == other.unit
      return Measure(self.value - other.value, self.unit)
    elsif Measure.dim(self.unit) == Measure.dim(other.unit)
      return Measure(self.value - other.convert(self.unit).value, self.unit)
    else
      raise TypeError, "incompatible dimensions: " +
        "#{Measure.dim(self.unit)} and #{Measure.dim(other.unit)}"
    end
  when Numeric
    return Measure(self.value - other, self.unit)
  else
    check_coerecable other
    a, b = other.coerce self
    return a - b
  end
end

#/(other) ⇒ Object



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/measure.rb', line 248

def /(other)
  case other
  when Measure
    # TODO: dimension
    raise NotImplementedError, "this feature has not implemented yet"
#       if self.unit == other.unit
#         return Measure(self.value / other.value, self.unit)
#       else
#         return Measure(self.value / other.convert(self.unit).value, self.unit)
#       end
  when Numeric
    return Measure(self.value / other, self.unit)
  else
    check_coercable other
    a, b = other.coerce self
    return a / b
  end
end

#<(other) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/measure.rb', line 145

def <(other)
  case other
  when Measure
    if self.unit == other.unit
      return self.value < other.value
    else
      return self < other.convert(self.value)
    end
  when Numeric
    return self.value < other
  else
    raise ArgumentError, 'unable to compare with #{other.inspect}'
  end
end

#==(other) ⇒ Object



175
176
177
178
179
180
181
182
183
184
# File 'lib/measure.rb', line 175

def ==(other)
  return self.value == other.value if self.unit == other.unit
  if Measure.compatible? self.unit, other.unit
    return self == other.convert(self.unit)
  elsif Measure.compatible? other.unit, self.unit
    return self.convert(other.unit) == other
  else
    return false
  end
end

#>(other) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/measure.rb', line 160

def >(other)
  case other
  when Measure
    if self.unit == other.unit
      return self.value > other.value
    else
      return self > other.convert(self.value)
    end
  when Numeric
    return self.value > other
  else
    raise ArgumentError, 'unable to compare with #{other.inspect}'
  end
end

#absObject



276
277
278
# File 'lib/measure.rb', line 276

def abs
  return Measure(self.value.abs, self.unit)
end

#coerce(other) ⇒ Object



267
268
269
270
271
272
273
274
# File 'lib/measure.rb', line 267

def coerce(other)
  case other
  when Numeric
    return [Measure(other, 1), self]
  else
    raise TypeError, "#{other.class} can't convert into #{self.class}"
  end
end

#convert(unit) ⇒ Object

Raises:



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/measure.rb', line 296

def convert(unit)
  return self if unit == self.unit
  to_unit = Measure.resolve_alias unit
  raise InvalidUnitError, "unknown unit: #{unit}" unless Measure.defined? unit
  from_unit = Measure.resolve_alias self.unit
  if Measure.compatible? from_unit, to_unit
    # direct conversion
    if @@conversion_map.has_key? from_unit and @@conversion_map[from_unit].has_key? to_unit
      value = self.value * @@conversion_map[from_unit][to_unit]
    else
      value = self.value / @@conversion_map[to_unit][from_unit].to_f
    end
  elsif route = Measure.find_multi_hop_conversion(from_unit, to_unit)
    u1 = route.shift
    value = self.value
    while u2 = route.shift
      if @@conversion_map.has_key? u1 and @@conversion_map[u1].has_key? u2
        value *= @@conversion_map[u1][u2]
      else
        value /= @@conversion_map[u2][u1].to_f
      end
      u1 = u2
    end
  else
    raise CompatibilityError, "units not compatible: #{self.unit} and #{unit}"
  end
  # Second 
  return Measure.new(value, unit)
end

#saved_method_missingObject



326
# File 'lib/measure.rb', line 326

alias saved_method_missing method_missing

#to_aObject



292
293
294
# File 'lib/measure.rb', line 292

def to_a
  return [self.value, self.unit]
end

#to_fObject



288
289
290
# File 'lib/measure.rb', line 288

def to_f
  return self.value.to_f
end

#to_iObject



284
285
286
# File 'lib/measure.rb', line 284

def to_i
  return self.value.to_i
end

#to_sObject



280
281
282
# File 'lib/measure.rb', line 280

def to_s
  return "#{self.value} [#{self.unit}]"
end