Ruby Units

repository·master·Indexed 19 days ago

https://github.com/olbrich/ruby-units

A Ruby gem for scientific calculations that provides a system for automatic unit conversions and manipulations. It supports simple, compound, and complex units, as well as integration with Ruby's Time, Date, DateTime, and Math modules. The library includes specialized handling for temperature scales, a DSL for defining new units, and configurable output formatting. Requires Ruby 3.2 or later.

Tokens
11.7K
Snippets
56
Records
59
Agent score
67%

What's inside ruby-units

  1. Overview of Ruby Units

    master
    The ruby-units gem simplifies scientific calculations by handling unit conversions and manipulations automatically. When a Unit object is created, its units are specified, and the class manages all subsequent operations to ensure accurate results and prevent manual conversion errors.
  2. Handle Temperature units

    master

    Ruby-units distinguishes between a temperature (a property/point on a scale, e.g., tempC) and degrees of temperature (a differential/interval, e.g., degC).

    • Temperature (tempX): Can be converted between scales (e.g., tempC to tempF) taking zero-point differences into account. Math is limited: you can subtract two temperatures to get a differential, but you cannot add two temperatures together.
    • Differential (degX): Behaves like standard units.

    Warning: Attempting to create a temperature below absolute zero will raise an exception.

    Unit.new('37 tempC').convert_to('tempF')   #=> 98.6 tempF
    Unit.new('100 tempC') - Unit.new('10 tempC') #=> '90 tempC'.to_unit
  3. Use Unit math with Time, Date, and DateTime

    master

    Ruby-units extends Time, Date, and DateTime to allow natural duration arithmetic.

    Adding/Subtracting Durations:

    • Time.now + Unit.new("10 min")
    • Date.today - Unit.new("30 days")

    Note on Large Units: When using years, decades, or centuries, the duration is converted to days and rounded to maintain calendar accuracy (e.g., 1 year $\approx$ 365 days). For precision, use hours or minutes.

    Converting Time to Units:

    • Time.now.to_unit: Returns duration in seconds since epoch.
    • Date.today.to_unit('week'): Returns duration in weeks since Julian calendar start.

    Creating Time from Units:

    • Time.at(Unit.new("1 hour"))
    • Time.in('5 min'): Shorthand for future times.
    Time.now + Unit.new("10 min")   #=> 10 minutes from now
    Time.now.to_unit('hours')       #=> Duration in hours since epoch
    Time.in('2 hours')               #=> 2 hours from now
  4. Use namespaced Unit class to avoid conflicts

    master

    If the global Unit class conflicts with other gems, you can use the RubyUnits namespace instead. To do this, require ruby_units/namespaced instead of the standard library.

    Note: When using the namespaced version, the Unit.new('unit string') helper is not defined; you must use RubyUnits::Unit.new.

    # In your code
    require 'ruby_units/namespaced'
    
    # In your Gemfile
    gem 'ruby-units', require: 'ruby_units/namespaced'
  5. Configure RubyUnits settings

    master

    Global configuration can be managed via RubyUnits.configure. This affects how units are formatted and displayed.

    Options:

    • format: Output format. :rational (default, e.g., 3 m/s^2) or :exponential (e.g., 3 m*s^-2).
    • separator: Space between number and unit. :space (default) or :none (e.g., 3m/s).
    • default_precision: Precision for rationalizing fractional values (default 0.0001).

    Use RubyUnits.reset to return to defaults.

    RubyUnits.configure do |config|
      config.format = :exponential
      config.separator = :none
      config.default_precision = 0.001
    end
  6. Handle temperature validation

    master

    When working with temperature units, the library ensures that values do not fall below absolute zero. If a unit is identified as a temperature unit and the base scalar is negative, an ArgumentError is raised.

    Error message: "Temperatures must not be less than absolute zero"

  7. How time math handles large calendar units

    master

    When performing arithmetic with Time and RubyUnits::Unit, the library distinguishes between 'exact' durations and 'calendar' durations to prevent precision errors caused by varying year lengths.

    1. Exact Durations: Units like second, minute, and hour are converted directly to seconds.
    2. Calendar Durations: Units like year, decade, and century are converted to days first, rounded to the nearest whole day, and then converted to seconds. This ensures that adding '1 year' behaves more like a calendar year than a fixed number of seconds (e.g., 31,536,000s).
  8. How unit-aware math functions work

    master

    The RubyUnits::Math module extends Ruby's standard Math singleton class to support RubyUnits::Unit objects. This allows you to perform mathematical operations directly on units while maintaining dimensional correctness.

    Key behaviors include:

    • Trigonometric functions (sin, cos, tan, etc.): Automatically convert angular units (like deg) to radians before calculation and return a dimensionless Numeric result.
    • Inverse trigonometric functions (asin, acos, atan, etc.): Return the result as a RubyUnits::Unit in radians if the input was a unit.
    • Root functions (sqrt, cbrt): Preserve dimensional analysis (e.g., the square root of m^2 is m).
    • Logarithmic functions (log, log10): Extract the scalar value from the unit to perform the calculation, returning a dimensionless Numeric result.
    • Vector/Distance functions (hypot, atan2): Handle unit conversions and dimensional compatibility for geometric calculations.
    Math.sin(Unit.new("90 deg"))      #=> 1.0
    Math.sqrt(Unit.new("4 m^2"))      #=> Unit.new("2 m")
    Math.hypot(Unit.new("3 m"), Unit.new("4 m"))  #=> Unit.new("5 m")
  9. Configure RubyUnits global settings

    master

    You can customize the global behavior of the RubyUnits library, such as how units are formatted and spaced, using the RubyUnits.configure block or by accessing the RubyUnits.configuration object directly. This allows you to control output representation without modifying individual unit instances.

    RubyUnits.configure do |config|
      config.separator = :none
      config.format = :exponential
      config.default_precision = 0.0001
    end
  10. Convert and compare units

    master

    Units can be converted to different scales or compared against each other.

    Conversion:

    • unit.convert_to('ft'): Returns a new converted unit.
    • unit >> "ft": Converts to 'feet'.
    • unit >>= "ft": Converts and overwrites the original object.

    Comparison:

    • unit1 <=> unit2: Compares quantities in base units (throws exception if incompatible).
    • unit1 === unit2: Returns true only if units and quantity are identical.
    unit.convert_to('ft')             # convert
    unit1 = unit >> "ft"              # convert to 'feet'
    unit1 <=> unit2                   # comparison on quantities in base units