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}[rdoc-ref: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
Methods
#
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.

VERSION = '0.2.1'
 
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.rb, line 215
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.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 Public methods
+(temperature)
+(numeric)

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
# 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
-(temperature)
-(numeric)

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
# File lib/basic_temperature.rb, line 537
def -(other)
  self + -other
end
-@()

Returns a new Temperature with negated degrees.

-Temperature[20, :celsius]
# => -20 °C
# File lib/basic_temperature.rb, line 547
def -@
  BasicTemperature.new(-self.degrees, self.scale)
end
<=>(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.rb, line 457
def <=>(other)
  return unless assert_temperature(other)

  compare_degrees(self.to_scale(other.scale).degrees, other.degrees)
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.rb, line 246
def set_degrees(degrees)
  BasicTemperature.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.rb, line 261
def set_scale(scale)
  BasicTemperature.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.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_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.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_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.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_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.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)

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