Class: BasicTemperature::Temperature

Inherits:
Object
  • Object
show all
Includes:
AdditionalHelpers, Assertions, Casting, Errors, Initialization, Memoization, Rounding, Comparable
Defined in:
lib/basic_temperature/temperature.rb,
lib/basic_temperature/temperature/errors.rb,
lib/basic_temperature/temperature/casting.rb,
lib/basic_temperature/temperature/rounding.rb,
lib/basic_temperature/temperature/assertions.rb,
lib/basic_temperature/temperature/memoization.rb,
lib/basic_temperature/temperature/initialization.rb,
lib/basic_temperature/temperature/additional_helpers.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 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

Defined Under Namespace

Modules: AdditionalHelpers, Assertions, Casting, Errors, Initialization, Memoization, Rounding

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

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

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

:call-seq:

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


235
236
237
238
239
240
241
242
243
# 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 Attribute Details

#degreesObject (readonly)

Degrees of the temperature.



212
213
214
# File 'lib/basic_temperature/temperature.rb', line 212

def degrees
  @degrees
end

#scaleObject (readonly)

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



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

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)


224
225
226
# File 'lib/basic_temperature/temperature.rb', line 224

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

Instance Method Details

#<=>(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


466
467
468
469
470
# 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?Boolean

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

Returns:

  • (Boolean)


476
477
478
# File 'lib/basic_temperature/temperature.rb', line 476

def boil_water?
  self.to_celsius.degrees >= 100
end

#coerce(numeric) ⇒ Object

Is used by + and - for Ruby coersion mechanism.



490
491
492
493
494
# File 'lib/basic_temperature/temperature.rb', line 490

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

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

#freeze_water?Boolean

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

Returns:

  • (Boolean)


484
485
486
# File 'lib/basic_temperature/temperature.rb', line 484

def freeze_water?
  self.to_celsius.degrees <= 0
end

#inspectObject

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



497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
# File 'lib/basic_temperature/temperature.rb', line 497

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


255
256
257
# File 'lib/basic_temperature/temperature.rb', line 255

def set_degrees(degrees)
  Temperature.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


270
271
272
# File 'lib/basic_temperature/temperature.rb', line 270

def set_scale(scale)
  Temperature.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


317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# 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_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


349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# 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_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


381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# 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_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


413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# 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) ⇒ 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


286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# 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