Class: Unitsdb::Database

Inherits:
Lutaml::Model::Serializable
  • Object
show all
Defined in:
lib/unitsdb/database.rb,
lib/unitsdb/database/loader.rb,
lib/unitsdb/database/reference_validator.rb,
lib/unitsdb/database/uniqueness_validator.rb

Defined Under Namespace

Modules: LookupStrategies, Reference Classes: IdRegistry, Loader, ReferenceChecker, ReferencePair, ReferenceValidator, UniquenessValidator

Constant Summary collapse

COLLECTIONS =
Loader::DATABASE_FILES.keys.map(&:to_sym).freeze
SYMBOL_COLLECTIONS =

Collections whose entities carry a symbols attribute. Used by symbol-based search/match to narrow iteration.

i[units prefixes].freeze
DATABASE_FILES =

Backwards-compat aliases — external callers (and the spec) read these constants off Database directly.

Loader::DATABASE_FILES
SUPPORTED_SCHEMA_VERSION =
Loader::SUPPORTED_SCHEMA_VERSION

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.empty_for_results(entities) ⇒ Object

Build an empty Database with entities partitioned into their typed collections. Used by CLI commands that need to serialize a subset of search results.



38
39
40
41
42
43
44
45
# File 'lib/unitsdb/database.rb', line 38

def self.empty_for_results(entities)
  Database.new.tap do |db|
    COLLECTIONS.each do |name|
      klass = collection_element_class(name)
      db.public_send("#{name}=", entities.grep(klass))
    end
  end
end

.from_db(dir_path, context: Unitsdb::Config.context_id) ⇒ Object

Load every YAML file under dir_path and deserialize into a Database instance scoped to context. The default context is auto-created via Config.ensure_default_context!; custom contexts must be created by the caller first.



221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/unitsdb/database.rb', line 221

def from_db(dir_path, context: Unitsdb::Config.context_id)
  context_id = context.to_sym
  Unitsdb::Config.ensure_default_context! if context_id == Unitsdb::Config.context_id

  combined_hash = Loader.load(dir_path)

  Lutaml::Model::GlobalContext.with_context(context_id) do
    if Unitsdb::Config.register_id_for(context_id)
      from_hash(combined_hash, register: context_id)
    else
      from_hash(combined_hash)
    end
  end
end

Instance Method Details

#collection(name) ⇒ Object

Resolve a collection name (String or Symbol) to its typed Array. Validates against COLLECTIONS so caller-supplied names can never dispatch to arbitrary methods (e.g. schema_version, to_yaml).



26
27
28
29
30
31
32
33
# File 'lib/unitsdb/database.rb', line 26

def collection(name)
  sym = name.to_sym
  unless COLLECTIONS.include?(sym)
    raise ArgumentError, "unknown collection: #{name.inspect}"
  end

  public_send(sym)
end

#find_by_symbol(symbol, entity_type = nil) ⇒ Array

Find entities by symbol

Parameters:

  • symbol (String)

    the symbol to search for (exact match, case-insensitive)

  • entity_type (String, Symbol, nil) (defaults to: nil)

    the entity type to search (units or prefixes)

Returns:

  • (Array)

    entities with matching symbol



108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/unitsdb/database.rb', line 108

def find_by_symbol(symbol, entity_type = nil)
  return [] unless symbol

  needle = symbol.downcase
  scope = scope_for(entity_type, SYMBOL_COLLECTIONS)

  scope.each_with_object([]) do |name, results|
    collection(name).each do |entity|
      results << entity if entity.symbols.any? do |sym|
        sym.ascii.to_s.downcase == needle
      end
    end
  end
end

#find_by_type(id:, type:) ⇒ Object?

Find an entity by its specific identifier and type

Parameters:

  • id (String)

    the identifier value to search for

  • type (String, Symbol)

    the entity type (units, prefixes, quantities, etc.)

Returns:

  • (Object, nil)

    the first entity with matching identifier or nil if not found



61
62
63
64
65
# File 'lib/unitsdb/database.rb', line 61

def find_by_type(id:, type:)
  collection(type).find do |entity|
    entity.identifiers.any? { |identifier| identifier.id == id }
  end
end

#get_by_id(id:, type: nil) ⇒ Object?

Find an entity by its identifier id across all entity types

Parameters:

  • id (String)

    the identifier value to search for

  • type (String, nil) (defaults to: nil)

    optional identifier type to match

Returns:

  • (Object, nil)

    the first entity with matching identifier or nil if not found



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/unitsdb/database.rb', line 71

def get_by_id(id:, type: nil)
  COLLECTIONS.each do |name|
    entity = collection(name).find do |e|
      e.identifiers.any? do |identifier|
        identifier.id == id && (type.nil? || identifier.type == type)
      end
    end
    return entity if entity
  end

  nil
end

#match_entities(params = {}) ⇒ Hash

Match entities by name, short, or symbol with different match types

Parameters:

  • params (Hash) (defaults to: {})

    match parameters

Options Hash (params):

  • :value (String)

    The value to match against

  • :match_type (String, Symbol)

    The type of match to perform (exact, symbol)

  • :entity_type (String, Symbol, nil)

    Optional entity type to limit search scope

Returns:

  • (Hash)

    matches grouped by match type (exact, symbol_match) with match details



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/unitsdb/database.rb', line 129

def match_entities(params = {})
  value = params[:value]
  return {} unless value

  match_type = params[:match_type]&.to_s || "exact"
  result = { exact: [], symbol_match: [] }

  scope_for(params[:entity_type], COLLECTIONS).each do |name|
    collection(name).each do |entity|
      if %w[exact all].include?(match_type)
        match_exact(entity, value, result)
      end
      next unless %w[symbol all].include?(match_type) && SYMBOL_COLLECTIONS.include?(name)

      match_symbol(entity, value, result)
    end
  end

  result.delete_if { |_, v| v.empty? }
  result
end

#search(params = {}) ⇒ Array

Search for entities containing the given text in identifiers, names, or short description.

Parameters:

  • params (Hash) (defaults to: {})

    search parameters

Options Hash (params):

  • :text (String)

    The text to search for

  • :type (String, Symbol, nil)

    Optional entity type to limit search scope

Returns:

  • (Array)

    all entities matching the search criteria



90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/unitsdb/database.rb', line 90

def search(params = {})
  text = params[:text]
  return [] unless text

  needle = text.downcase
  scope = scope_for(params[:type], COLLECTIONS)

  scope.each_with_object([]) do |name, results|
    collection(name).each do |entity|
      results << entity if matches_text?(entity, needle)
    end
  end
end

#validate_referencesObject

Validates references between entities. Delegates to ReferenceValidator.



158
159
160
# File 'lib/unitsdb/database.rb', line 158

def validate_references
  ReferenceValidator.validate(self)
end

#validate_uniquenessObject

Checks for uniqueness of identifiers and short names. Delegates to UniquenessValidator.



153
154
155
# File 'lib/unitsdb/database.rb', line 153

def validate_uniqueness
  UniquenessValidator.validate(self)
end