Skip to content
Merged
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ source "https://rubygems.org"
gemspec

gem "canon"
gem "lutaml-model", github: "lutaml/lutaml-model", branch: "main"
gem "lutaml-model", github: "lutaml/lutaml-model", branch: "fix/global-context-register-lookup-fallback"
gem "nokogiri"
gem "rake"
gem "rspec"
Expand Down
90 changes: 45 additions & 45 deletions lib/unitsdb.rb
Original file line number Diff line number Diff line change
@@ -1,52 +1,45 @@
# frozen_string_literal: true

require "lutaml/model"
require "unitsdb/config"
require "unitsdb/identifier"
require "unitsdb/localized_string"
require "unitsdb/symbol_presentations"
require "unitsdb/scale_properties"
require "unitsdb/unit_reference"
require "unitsdb/prefix_reference"
require "unitsdb/quantity_reference"
require "unitsdb/dimension_reference"
require "unitsdb/unit_system_reference"
require "unitsdb/scale_reference"
require "unitsdb/external_reference"
require "unitsdb/root_unit_reference"
require "unitsdb/si_derived_base"
require "unitsdb/dimension_details"
require "unitsdb/dimension"
require "unitsdb/prefix"
require "unitsdb/unit_system"
require "unitsdb/quantity"
require "unitsdb/scale"
require "unitsdb/unit"
require "unitsdb/dimensions"
require "unitsdb/prefixes"
require "unitsdb/quantities"
require "unitsdb/scales"
require "unitsdb/unit_systems"
require "unitsdb/units"
require "unitsdb/database"
require "unitsdb/qudt"
require "unitsdb/ucum"

module Unitsdb
autoload :Cli, "unitsdb/cli"
autoload :Config, "unitsdb/config"
autoload :Commands, "unitsdb/commands"
autoload :Database, "unitsdb/database"
autoload :Dimension, "unitsdb/dimension"
autoload :DimensionDetails, "unitsdb/dimension_details"
autoload :DimensionReference, "unitsdb/dimension_reference"
autoload :Dimensions, "unitsdb/dimensions"
# Core models are eagerly loaded so type registrations are complete before
# any context or database loading happens.
unless RUBY_ENGINE == "opal"
autoload :Cli, "unitsdb/cli"
autoload :Commands, "unitsdb/commands"
end
autoload :Errors, "unitsdb/errors"
autoload :ExternalReference, "unitsdb/external_reference"
autoload :Identifier, "unitsdb/identifier"
autoload :LocalizedString, "unitsdb/localized_string"
autoload :Prefix, "unitsdb/prefix"
autoload :PrefixReference, "unitsdb/prefix_reference"
autoload :Prefixes, "unitsdb/prefixes"
autoload :Quantities, "unitsdb/quantities"
autoload :Quantity, "unitsdb/quantity"
autoload :QuantityReference, "unitsdb/quantity_reference"
autoload :QudtUnit, "unitsdb/qudt"
autoload :QudtQuantityKind, "unitsdb/qudt"
autoload :QudtDimensionVector, "unitsdb/qudt"
autoload :QudtSystemOfUnits, "unitsdb/qudt"
autoload :QudtPrefix, "unitsdb/qudt"
autoload :QudtVocabularies, "unitsdb/qudt"
autoload :RootUnitReference, "unitsdb/root_unit_reference"
autoload :Scale, "unitsdb/scale"
autoload :ScaleProperties, "unitsdb/scale_properties"
autoload :ScaleReference, "unitsdb/scale_reference"
autoload :Scales, "unitsdb/scales"
autoload :SiDerivedBase, "unitsdb/si_derived_base"
autoload :SymbolPresentations, "unitsdb/symbol_presentations"
autoload :UcumBaseUnit, "unitsdb/ucum"
autoload :UcumPrefixValue, "unitsdb/ucum"
autoload :UcumPrefix, "unitsdb/ucum"
autoload :UcumUnitValueFunction, "unitsdb/ucum"
autoload :UcumUnitValue, "unitsdb/ucum"
autoload :UcumUnit, "unitsdb/ucum"
autoload :UcumFile, "unitsdb/ucum"
autoload :Unit, "unitsdb/unit"
autoload :UnitReference, "unitsdb/unit_reference"
autoload :UnitSystem, "unitsdb/unit_system"
autoload :UnitSystemReference, "unitsdb/unit_system_reference"
autoload :UnitSystems, "unitsdb/unit_systems"
autoload :Units, "unitsdb/units"
autoload :Utils, "unitsdb/utils"

class << self
Expand All @@ -56,12 +49,19 @@ def data_dir
end

# Returns a pre-loaded Database instance from the bundled data
def database
@database ||= Database.from_db(data_dir)
def database(context: Config.context_id)
context_id = context.to_sym
Config.context(context_id) if context_id == Config.context_id && Config.find_context(context_id).nil?
klass = Config.resolve_type(:database, context: context_id)
databases[context_id] ||= klass.from_db(data_dir, context: context_id)
end

private

def databases
@databases ||= {}
end

def gem_dir
@gem_dir ||= File.dirname(__dir__)
end
Expand Down
116 changes: 114 additions & 2 deletions lib/unitsdb/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,129 @@

module Unitsdb
class Config
CONTEXT_ID = :unitsdb_v2

class << self
def context_id
@context_id ||= CONTEXT_ID
end

def register_model(klass, id:)
registered_models[id.to_sym] = klass
klass
end

def registered_models
@registered_models ||= {}
end

def models
@models ||= {}
end

def models=(user_models)
models.merge!(user_models)
normalized_models = user_models.each_with_object({}) do |(id, klass), result|
model_id = id.to_sym
result[model_id] = register_model(klass, id: model_id)
end

models.merge!(normalized_models)
end

def model_for(model_name)
models[model_name]
model_id = model_name.to_sym
models[model_id] || registered_models[model_id]
end

def register(id = context_id)
explicit_registers[id.to_sym]
end

def populate_register(id: context_id, fallback_to: [:default], substitutions: [])
register_id = id.to_sym
context(register_id)

model_register = Lutaml::Model::Register.new(register_id, fallback: fallback_to)
resolve_substitutions(
substitutions,
registry: build_registry,
fallback_to: fallback_to,
id: "#{register_id}_register",
).each do |substitution|
model_register.register_global_type_substitution(**substitution)
end

explicit_registers[register_id] = Lutaml::Model::GlobalRegister.register(model_register)
end

def find_context(id)
Lutaml::Model::GlobalContext.context(id.to_sym)
end

def resolve_type(type_name, context: context_id)
Lutaml::Model::GlobalContext.resolve_type(type_name, context.to_sym)
end

def context(id = context_id, force_populate: false)
existing = find_context(id)
return existing if existing && !force_populate && populated?(id)

populate_context(id: id)
end

def populate_context(id: context_id, fallback_to: [:default], substitutions: [])
Lutaml::Model::GlobalContext.unregister_context(id) if find_context(id)

opts = { registry: build_registry, fallback_to: fallback_to, id: id }
context = Lutaml::Model::GlobalContext.create_context(
substitutions: resolve_substitutions(substitutions, **opts),
**opts,
)
mark_populated!(id)
context
end

def resolve_substitutions(substitutions, registry:, fallback_to:, id:)
resolution_context = Lutaml::Model::TypeContext.derived(
id: "#{id}_substitution_resolution",
registry: registry,
fallback_to: fallback_to,
)

Array(substitutions).map do |substitution|
from_key = substitution[:from_type] || substitution[:from]
to_key = substitution[:to_type] || substitution[:to]

{
from_type: resolve_substitution_type(from_key, resolution_context),
to_type: resolve_substitution_type(to_key, resolution_context),
}
end
end

def resolve_substitution_type(value, resolution_context)
return value if value.is_a?(Class)

Lutaml::Model::TypeResolver.resolve(value, resolution_context)
end

def build_registry
registry = Lutaml::Model::TypeRegistry.new
registered_models.each { |model_id, klass| registry.register(model_id, klass) }
registry
end

def populated?(context_id)
@populated_for&.[](context_id.to_sym)
end

def mark_populated!(context_id)
@populated_for ||= {}
@populated_for[context_id.to_sym] = true
end

def explicit_registers
@explicit_registers ||= {}
end
end
end
Expand Down
Loading
Loading