Class: BasicTemperature

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/basic_temperature.rb,
lib/basic_temperature/version.rb

Overview

Temperature is a simple Value Object for basic temperature operations like conversions from Celsius to Fahrenhait or Kelvin etc.

Supported scales: Celsius, Fahrenheit, Kelvin and Rankine.

Creating Temperatures

A new temperature can be created in multiple ways:

  • Using keyword arguments:

    Temperature.new(degrees: 0, scale: :celsius)
    
  • Using positional arguments:

    Temperature.new(0, :celsius)
    
  • Even more concise way using Temperature.[] (an alias of Temperature.new):

    Temperature[0, :celsius]
    

Creating Temperatures from already existing temperature objects

Sometimes it is useful to create a new temperature from already existing one.

For such cases, there are #set_degrees[BasicTemperature#set_degrees and set_scale.

Since temperatures are Value Objects, both methods returns new instances.

Examples:

temperature = Temperature[0, :celsius]
# => 0 °C

new_temperature = temperature.set_degrees(15)
# => 15 °C

temperature = Temperature[0, :celsius]
# => 0 °C

new_temperature = temperature.set_scale(:kelvin)
# => 0 K

Conversions

Temperatures can be converted to diffirent scales.

Currently, the following scales are supported: Celsius, Fahrenheit, Kelvin and Rankine.

Temperature[20, :celsius].to_celsius
# => 20 °C

Temperature[20, :celsius].to_fahrenheit
# => 68 °F

Temperature[20, :celsius].to_kelvin
# => 293.15 K

Temperature[20, :celsius].to_rankine
# => 527.67 °R

If it is necessary to convert scale dynamically, to_scale method is available.

Temperature[20, :celsius].to_scale(scale)

All conversion formulas are taken from RapidTables.

Conversion precision: 2 accurate digits after the decimal dot.

Comparison

Temperature implements idiomatic <=> spaceship operator and mixes in Comparable module.

As a result, all methods from Comparable are available, e.g:

Temperature[20, :celsius] < Temperature[25, :celsius]
# => true

Temperature[20, :celsius] <= Temperature[25, :celsius]
# => true

Temperature[20, :celsius] == Temperature[25, :celsius]
# => false

Temperature[20, :celsius] > Temperature[25, :celsius]
# => false

Temperature[20, :celsius] >= Temperature[25, :celsius]
# => false

Temperature[20, :celsius].between?(Temperature[15, :celsius], Temperature[25, :celsius])
# => true

# Starting from Ruby 2.4.6
Temperature[20, :celsius].clamp(Temperature[20, :celsius], Temperature[25, :celsius])
# => 20 °C

Please note, if other temperature has a different scale, temperature is automatically converted to that scale before comparison.

Temperature[20, :celsius] == Temperature[293.15, :kelvin]
# => true

IMPORTANT !!!

degrees are rounded to the nearest value with a precision of 2 decimal digits before comparison.

This means the following temperatures are considered as equal:

Temperature[20.020, :celsius] == Temperature[20.024, :celsius]
# => true

Temperature[20.025, :celsius] == Temperature[20.029, :celsius]
# => true

while these ones are treated as NOT equal:

Temperature[20.024, :celsius] == Temperature[20.029, :celsius]
# => false

Math

Addition/Subtraction.

Temperature[20, :celsius] + Temperature[10, :celsius]
# => 30 °C

Temperature[20, :celsius] - Temperature[10, :celsius]
# => 10 °C

If second temperature has a different scale, first temperature is automatically converted to that scale before degrees addition/subtraction.

Temperature[283.15, :kelvin] + Temperature[10, :celsius]
# => 10 °C

Returned temperature will have the same scale as the second temperature.

It is possible to add/subtract numerics.

Temperature[20, :celsius] + 10
# => 30 °C

Temperature[20, :celsius] - 10
# => 10 °C

In such cases, returned temperature will have the same scale as the first temperature.

Also Ruby coersion mechanism is supported.

10 + Temperature[20, :celsius]
# => 30 °C

10 - Temperature[20, :celsius]
# => -10 °C

Negation

-Temperature[20, :celsius]
# => -20 °C

Defined Under Namespace

Classes: InitializationArgumentsError, InvalidDegreesError, InvalidNumericOrTemperatureError, InvalidScaleError

Constant Summary collapse

CELSIUS =
'celsius'
FAHRENHEIT =
'fahrenheit'
KELVIN =
'kelvin'
RANKINE =
'rankine'
SCALES =

A list of all currently supported scale values.

[CELSIUS, FAHRENHEIT, KELVIN, RANKINE].freeze
VERSION =
'0.2.2'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*positional_arguments, **keyword_arguments) ⇒ BasicTemperature

Creates a new instance of Temperature. Is aliased as [].

:call-seq:

new(degrees:, scale:)
new(degrees, scale)


226
227
228
229
230
231
232
233
234
# File 'lib/basic_temperature.rb', line 226

def initialize(*positional_arguments, **keyword_arguments)
  assert_either_positional_arguments_or_keyword_arguments!(positional_arguments, keyword_arguments)

  if keyword_arguments.any?
    initialize_via_keywords_arguments(keyword_arguments)
  else # positional_arguments.any?
    initialize_via_positional_arguments(positional_arguments)
  end
end

Instance Attribute Details

#degreesObject (readonly)

Degrees of the temperature.



203
204
205
# File 'lib/basic_temperature.rb', line 203

def degrees
  @degrees
end

#scaleObject (readonly)

Scale of the temperature. Look at SCALES for possible values.



206
207
208
# File 'lib/basic_temperature.rb', line 206

def scale
  @scale
end

Class Method Details

.[](*args, **kwargs) ⇒ Object

Creates a new instance of Temperature. Alias for new.

:call-seq:

[](degrees:, scale:)
[](degrees, scale)


215
216
217
# File 'lib/basic_temperature.rb', line 215

def self.[](*args, **kwargs)
  new(*args, **kwargs)
end

Instance Method Details

#+(other) ⇒ Object

Performs addition. Returns a new Temperature.

Temperature[20, :celsius] + Temperature[10, :celsius]
# => 30 °C

If the second temperature has a different scale, the first temperature is automatically converted to that scale before degrees addition.

Temperature[283.15, :kelvin] + Temperature[20, :celsius]
# => 30 °C

Returned temperature will have the same scale as the second temperature.

It is possible to add numerics.

Temperature[20, :celsius] + 10
# => 30 °C

In such cases, returned temperature will have the same scale as the first temperature.

Also Ruby coersion mechanism is supported.

10 + Temperature[20, :celsius]
# => 30 °C

:call-seq:

+(temperature)
+(numeric)


493
494
495
496
497
498
499
500
501
502
503
504
505
# File 'lib/basic_temperature.rb', line 493

def +(other)
  assert_numeric_or_temperature!(other)

  degrees, scale =
    case other
    when Numeric
      [self.degrees + other, self.scale]
    when BasicTemperature
      [self.to_scale(other.scale).degrees + other.degrees, other.scale]
    end

  BasicTemperature.new(degrees, scale)
end

#-(other) ⇒ Object

Performs subtraction. Returns a new Temperature.

Temperature[20, :celsius] - Temperature[10, :celsius]
# => 10 °C

If the second temperature has a different scale, the first temperature is automatically converted to that scale before degrees subtraction.

Temperature[283.15, :kelvin] + Temperature[10, :celsius]
# => 10 °C

Returned temperature will have the same scale as the second temperature.

It is possible to subtract numerics.

Temperature[20, :celsius] - 10
# => 10 °C

In such cases, returned temperature will have the same scale as the first temperature.

Also Ruby coersion mechanism is supported.

10 - Temperature[20, :celsius]
# => -10 °C

:call-seq:

-(temperature)
-(numeric)


537
538
539
# File 'lib/basic_temperature.rb', line 537

def -(other)
  self + -other
end

#-@Object

Returns a new Temperature with negated degrees.

-Temperature[20, :celsius]
# => -20 °C


547
548
549
# File 'lib/basic_temperature.rb', line 547

def -@
  BasicTemperature.new(-self.degrees, self.scale)
end

#<=>(other) ⇒ Object

Compares temperture with other temperature.

Returns 0 if they are considered as equal.

Two temperatures are considered as equal when they have the same amount of degrees.

Returns -1 if temperature is lower than other temperature.

Returns 1 if temperature is higher than other temperature.

If other temperature has a different scale, temperature is automatically converted to that scale before degrees comparison.

Temperature[20, :celsius] <=> Temperature[20, :celsius]
# => 0

Temperature[20, :celsius] <=> Temperature[293.15, :kelvin]
# => 0

IMPORTANT!!!

This method rounds degrees to the nearest value with a precision of 2 decimal digits.

This means the following:

Temperature[20.020, :celsius] <=> Temperature[20.024, :celsius]
# => 0

Temperature[20.025, :celsius] <=> Temperature[20.029, :celsius]
# => 0

Temperature[20.024, :celsius] <=> Temperature[20.029, :celsius]
# => -1


457
458
459
460
461
# File 'lib/basic_temperature.rb', line 457

def <=>(other)
  return unless assert_temperature(other)

  compare_degrees(self.to_scale(other.scale).degrees, other.degrees)
end

#coerce(numeric) ⇒ Object

Is used by + and - for Ruby coersion mechanism.



553
554
555
556
557
# File 'lib/basic_temperature.rb', line 553

def coerce(numeric) #:nodoc:
  assert_numeric!(numeric)

  [BasicTemperature.new(numeric, self.scale), self]
end

#inspectObject

Returns a string containing a human-readable representation of temperature.



560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'lib/basic_temperature.rb', line 560

def inspect #:nodoc:
  rounded_degrees = round_degrees(degrees)

  printable_degrees = degrees_without_decimal?(rounded_degrees) ? rounded_degrees.to_i : rounded_degrees

  scale_symbol =
    case self.scale
    when CELSIUS
      '°C'
    when FAHRENHEIT
      '°F'
    when KELVIN
      'K'
    when RANKINE
      '°R'
    end

  "#{printable_degrees} #{scale_symbol}"
end

#set_degrees(degrees) ⇒ Object

Returns a new Temperature with updated degrees.

temperature = Temperature[0, :celsius]
# => 0 °C

new_temperature = temperature.set_degrees(15)
# => 15 °C


246
247
248
# File 'lib/basic_temperature.rb', line 246

def set_degrees(degrees)
  BasicTemperature.new(degrees, scale)
end

#set_scale(scale) ⇒ Object

Returns a new Temperature with updated scale.

temperature = Temperature[0, :celsius]
# => 0 °C

new_temperature = temperature.set_scale(:kelvin)
# => 0 K


261
262
263
# File 'lib/basic_temperature.rb', line 261

def set_scale(scale)
  BasicTemperature.new(degrees, scale)
end

#to_celsiusObject

Converts temperature to Celsius scale. If temperature is already in Celsius, returns current temperature object.

Memoizes subsequent calls.

Conversion formulas are taken from RapidTables:

  1. Celsius to Fahrenheit.

  2. Celsius to Kelvin.

  3. Celsius to Rankine.

Temperature[0, :fahrenheit].to_celsius
# => -17.78 °C


308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/basic_temperature.rb', line 308

def to_celsius
  memoized(:to_celsius) || memoize(:to_celsius, -> {
    return self if self.scale == CELSIUS

    degrees =
      case self.scale
      when FAHRENHEIT
        (self.degrees - 32) * (5 / 9r)
      when KELVIN
        self.degrees - 273.15
      when RANKINE
        (self.degrees - 491.67) * (5 / 9r)
      end

    BasicTemperature.new(degrees, CELSIUS)
  })
end

#to_fahrenheitObject

Converts temperature to Fahrenheit scale. If temperature is already in Fahrenheit, returns current temperature object.

Memoizes subsequent calls.

Conversion formulas are taken from RapidTables:

  1. Fahrenheit to Celsius.

  2. Fahrenheit to Kelvin.

  3. Fahrenheit to Rankine.

Temperature[0, :celsius].to_fahrenheit
# => 32 °F


340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/basic_temperature.rb', line 340

def to_fahrenheit
  memoized(:to_fahrenheit) || memoize(:to_fahrenheit, -> {
    return self if self.scale == FAHRENHEIT

    degrees =
      case self.scale
      when CELSIUS
        self.degrees * (9 / 5r) + 32
      when KELVIN
        self.degrees * (9 / 5r) - 459.67
      when RANKINE
        self.degrees - 459.67
      end

    BasicTemperature.new(degrees, FAHRENHEIT)
  })
end

#to_kelvinObject

Converts temperature to Kelvin scale. If temperature is already in Kelvin, returns current

temperature object.

Memoizes subsequent calls.

Conversion formulas are taken from RapidTables:

  1. Kelvin to Celsius.

  2. Kelvin to Fahrenheit.

  3. Kelvin to Rankine.

Temperature[0, :kelvin].to_rankine
# => 0 °R


372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/basic_temperature.rb', line 372

def to_kelvin
  memoized(:to_kelvin) || memoize(:to_kelvin, -> {
    return self if self.scale == KELVIN

    degrees =
      case self.scale
      when CELSIUS
        self.degrees + 273.15
      when FAHRENHEIT
        (self.degrees + 459.67) * (5 / 9r)
      when RANKINE
        self.degrees * (5 / 9r)
      end

    BasicTemperature.new(degrees, KELVIN)
  })
end

#to_rankineObject

Converts temperature to Rankine scale. If temperature is already in Rankine, returns current temperature object.

Memoizes subsequent calls.

Conversion formulas are taken from RapidTables:

  1. Rankine to Celsius.

  2. Rankine to Fahrenheit.

  3. Rankine to Kelvin.

Temperature[0, :rankine].to_kelvin
# => 0 K


404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/basic_temperature.rb', line 404

def to_rankine
  memoized(:to_rankine) || memoize(:to_rankine, -> {
    return self if self.scale == RANKINE

    degrees =
      case self.scale
      when CELSIUS
        (self.degrees + 273.15) * (9 / 5r)
      when FAHRENHEIT
        self.degrees + 459.67
      when KELVIN
        self.degrees * (9 / 5r)
      end

    BasicTemperature.new(degrees, RANKINE)
  })
end

#to_scale(scale) ⇒ Object

Converts temperature to specific scale. If temperature is already in desired scale, returns current temperature object.

Raises InvalidScaleError when scale can not be casted to any possible scale value (see SCALES).

Temperature[60, :fahrenheit].to_scale(:celsius)
# => 15.56 °C


277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/basic_temperature.rb', line 277

def to_scale(scale)
  casted_scale = cast_scale(scale)

  assert_valid_scale!(casted_scale)

  case casted_scale
  when CELSIUS
    to_celsius
  when FAHRENHEIT
    to_fahrenheit
  when KELVIN
    to_kelvin
  when RANKINE
    to_rankine
  end
end