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 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

Queries

Temperature[0, :celsius].boil_water?
# => false

Temperature[0, :celsius].freeze_water?
# => true
Namespace
Methods
#
B
F
N
S
T
Included Modules
Constants
CELSIUS = 'celsius'
 
FAHRENHEIT = 'fahrenheit'
 
KELVIN = 'kelvin'
 
RANKINE = 'rankine'
 
SCALES = [CELSIUS, FAHRENHEIT, KELVIN, RANKINE].freeze
 

A list of all currently supported scale values.

Attributes
[R] degrees

Degrees of the temperature.

[R] scale

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

Class Public methods
[](degrees:, scale:)
[](degrees, scale)

Creates a new instance of Temperature. Alias for new.

# File lib/basic_temperature/temperature.rb, line 224
def self.[](*args, **kwargs)
  new(*args, **kwargs)
end
new(degrees:, scale:)
new(degrees, scale)

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

# File lib/basic_temperature/temperature.rb, line 235
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 Public methods
<=>(other)

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
# File lib/basic_temperature/temperature.rb, line 466
def <=>(other)
  return unless assert_temperature(other)

  round_degrees(self.to_scale(other.scale).degrees) <=> round_degrees(other.degrees)
end
boil_water?()

Returns true when temperature boils water (is greater than or equal to 100 °C), false otherwise.

# File lib/basic_temperature/temperature.rb, line 476
def boil_water?
  self.to_celsius.degrees >= 100
end
freeze_water?()

Returns true when temperature freezes water (is less than or equal to 0 °C), false otherwise.

# File lib/basic_temperature/temperature.rb, line 484
def freeze_water?
  self.to_celsius.degrees <= 0
end
set_degrees(degrees)

Returns a new Temperature with updated degrees.

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

new_temperature = temperature.set_degrees(15)
# => 15 °C
# File lib/basic_temperature/temperature.rb, line 255
def set_degrees(degrees)
  Temperature.new(degrees, scale)
end
set_scale(scale)

Returns a new Temperature with updated scale.

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

new_temperature = temperature.set_scale(:kelvin)
# => 0 K
# File lib/basic_temperature/temperature.rb, line 270
def set_scale(scale)
  Temperature.new(degrees, scale)
end
to_celsius()

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
# File lib/basic_temperature/temperature.rb, line 317
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

    Temperature.new(degrees, CELSIUS)
  })
end
to_fahrenheit()

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
# File lib/basic_temperature/temperature.rb, line 349
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

    Temperature.new(degrees, FAHRENHEIT)
  })
end
to_kelvin()

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
# File lib/basic_temperature/temperature.rb, line 381
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

    Temperature.new(degrees, KELVIN)
  })
end
to_rankine()

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
# File lib/basic_temperature/temperature.rb, line 413
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

    Temperature.new(degrees, RANKINE)
  })
end
to_scale(scale)

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
# File lib/basic_temperature/temperature.rb, line 286
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