Class: Horologium::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/horologium/configuration.rb,
sig/horologium/configuration.rbs

Overview

Holds the library's settings: the default precision new instants and durations take when none is asked for, and the time scales an instant can be read in. Both are set once, inside configure, and frozen afterwards, so behaviour does not depend on when in the process' life an object is read.

Constant Summary collapse

BUILT_IN_SCALES =

The scales the library ships with. They are registered before the library is configured.

Returns:

{
  tai: Scales::TAI,
  tt: Scales::TT,
  tdb: Scales::TDB,
  utc: Scales::UTC
}.freeze
LEAP_SECOND_HORIZONS =

The recognised ways to handle a leap second past the data's horizon.

Returns:

  • (Array[Symbol])
%i[extrapolate raise].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfiguration

Returns a new instance of Configuration.



45
46
47
48
49
50
# File 'lib/horologium/configuration.rb', line 45

def initialize
  @default_precision = :standard
  @scales = BUILT_IN_SCALES.dup
  @leap_second_source = Data::LeapSeconds
  @leap_second_horizon = :extrapolate
end

Instance Attribute Details

#default_precisionSymbol

Returns the default precision, :standard until configured.

Returns:

  • (Symbol)

    the default precision, :standard until configured



20
21
22
# File 'lib/horologium/configuration.rb', line 20

def default_precision
  @default_precision
end

#leap_second_horizonSymbol

What UTC does with a date past the point the leap second data vouches for. :extrapolate, the default, reads it in UTC with the last known offset and marks the reading :extrapolated. :raise refuses it with OutOfDataRangeError, for a pipeline that must not lean on an offset a new leap second could overturn.

Returns:

  • (Symbol)

    :extrapolate or :raise



43
44
45
# File 'lib/horologium/configuration.rb', line 43

def leap_second_horizon
  @leap_second_horizon
end

#leap_second_source#tai_utc_at

The source UTC reads its leap seconds from. It answers tai_utc_at with TAI - UTC at a point in UTC, given a Julian Day Number: a day's 0h for a whole number, or part way through a day where a fraction is added, which UTC asks for to read the pre-1972 drift within a day. A source that steps only at whole days can answer a fraction with the offset at its 0h. Data::LeapSeconds, over the iers gem, is the default; a caller with its own leap second data can set another here.

Returns:

  • (#tai_utc_at)


34
35
36
# File 'lib/horologium/configuration.rb', line 34

def leap_second_source
  @leap_second_source
end

Instance Method Details

#freezeself

Freezes the configuration and the scales with it, so neither changes once the library is configured.

Returns:

  • (self)


169
170
171
172
# File 'lib/horologium/configuration.rb', line 169

def freeze
  @scales.freeze
  super
end

#register_scale(name, scale) ⇒ Class

Registers a time scale under a name, so an instant can be read in it with Instant#to. The scale is a class implementing Scales::Base: it says how to read TAI in the scale, and how to read the scale back in TAI. Registering a name that is already taken replaces the scale under it, so a scale the library ships can be swapped for another model.

A scale that does not implement both of them is refused here, at boot, rather than when an instant is first read in it.

Examples:

class MyScale < Horologium::Scales::Base
  # .from_reference and .to_reference
end

Horologium.configure do |c|
  c.register_scale(:my_scale, MyScale)
end

Parameters:

  • name (Symbol)

    the name to read the scale under

  • scale (Class)

    a subclass of Scales::Base

Returns:

  • (Class)

    the scale that was registered

Raises:



139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/horologium/configuration.rb', line 139

def register_scale(name, scale)
  if @scales.frozen?
    raise ConfigurationError, "the configuration is already frozen"
  end

  unless name.is_a?(Symbol)
    raise ConfigurationError,
      "a scale is registered under a Symbol, got #{name.inspect}"
  end

  validate_scale!(scale)

  @scales[name] = scale
end

#scale(name) ⇒ Class

The scale registered under a name.

Parameters:

  • name (Symbol)

    the name of the scale, such as :tt

Returns:

  • (Class)

    the scale

Raises:



159
160
161
162
163
# File 'lib/horologium/configuration.rb', line 159

def scale(name)
  @scales.fetch(name) do
    raise UnknownScaleError.new(name, scale_names)
  end
end

#scale_namesArray<Symbol>

The names an instant can be read in, the built-in scales and any scale #register_scale added.

Returns:

  • (Array<Symbol>)


56
57
58
# File 'lib/horologium/configuration.rb', line 56

def scale_names
  @scales.keys
end

#validate_scale!(scale) ⇒ void

This method returns an undefined value.

Checks that a scale can be read in: a class implementing both halves of Scales::Base. A subclass that inherits either one from Scales::Base would raise NotImplementedError on the first conversion, so it is refused here instead.

Parameters:

  • scale (Object)

    the scale to check

Raises:



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/horologium/configuration.rb', line 184

def validate_scale!(scale)
  unless scale.is_a?(Class) && scale < Scales::Base
    raise ConfigurationError,
      "a scale must be a subclass of Horologium::Scales::Base, " \
      "got #{scale.inspect}"
  end

  missing = %i[from_reference to_reference].select do |method|
    scale.method(method).owner == Scales::Base.singleton_class
  end
  return if missing.empty?

  raise ConfigurationError,
    "#{scale} does not implement #{missing.join(" or ")}"
end