Class: Glossarist::V3::AbstractHyperedge

Inherits:
Lutaml::Model::Serializable
  • Object
show all
Defined in:
lib/glossarist/v3/abstract_hyperedge.rb

Overview

AbstractHyperedge — abstract base shape for all n-ary concept-system relations (PartitiveHyperedge, GenericHyperedge, future AssociativeRelation, SequentialRelation).

Carries the shared fields: comprehensive, members, completeness, criterion, sources, notes, status.

Concrete leaves override members to specify the typed member class. Shared validations live here.

Constant Summary collapse

DEFAULT_COMPLETENESS =
"complete"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeAbstractHyperedge

Returns a new instance of AbstractHyperedge.



48
49
50
51
52
53
54
55
56
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 48

def initialize(*)
  if instance_of?(AbstractHyperedge)
    raise NotImplementedError,
          "AbstractHyperedge is abstract; instantiate " \
          "PartitiveHyperedge or GenericHyperedge instead"
  end

  super
end

Instance Attribute Details

#file_idObject

Per-file identity — set by RelationLoader on parse, used by HyperedgeWriter on serialize. The wire field is $id (JSON Schema convention); the value is <comp-id>/<criterion-slug>. Not in key_value because lutaml::Model key_value does not natively express $-prefixed keys; handled via custom round-trip in HyperedgeWriter / RelationLoader.



36
37
38
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 36

def file_id
  @file_id
end

Instance Method Details

#complete?Boolean

Returns:

  • (Boolean)


66
67
68
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 66

def complete?
  completeness == "complete"
end

#comprehensive_dir_nameObject

Comprehensive concept as a directory-safe name: <source>-<id> lowercased, kebab-case, special chars (including dots) replaced with dashes. Matches the concept-model fixtures (vim-112-02-09, oiml-5-1, example-116-01-01).



159
160
161
162
163
164
165
166
167
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 159

def comprehensive_dir_name
  return nil unless comprehensive.is_a?(ConceptRef)

  parts = [comprehensive.source, comprehensive.id].compact.reject(&:empty?)
  return nil if parts.empty?

  parts.join("-").downcase.gsub(/[^a-z0-9-]/, "-")
    .gsub(/-{2,}/, "-").gsub(/\A-|-\z/, "")
end

#coordinate?Boolean

ISO 704: a rake connects to two or more members. A single binary edge is not an n-ary relation.

Returns:

  • (Boolean)


76
77
78
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 76

def coordinate?
  members.length >= 2
end

#criterion_slugObject

Kebab-case slug derived from the English criterion. Falls back to "decomposition-N" when no English criterion exists (N is derived from the criterion hash for stability).



172
173
174
175
176
177
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 172

def criterion_slug
  eng = criterion.is_a?(Hash) ? (criterion["eng"] || criterion[:eng]) : nil
  return "decomposition-#{criterion_hash}" if eng.nil? || eng.to_s.empty?

  eng.to_s.downcase.gsub(/[^a-z0-9]+/, "-").gsub(/\A-|-\z/, "")
end

#dangling_externals?(resolver = nil) ⇒ Boolean

True if any external comprehensive or member lacks a provided_by edge — i.e., the decomposition dangles. The resolver is used twice: once to detect externals, once to check each external's related edges.

Returns:

  • (Boolean)


119
120
121
122
123
124
125
126
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 119

def dangling_externals?(resolver = nil)
  return false unless resolver

  candidates = []
  candidates << comprehensive if external_comprehensive?(resolver)
  candidates.concat(external_members(resolver).map(&:ref))
  candidates.any? { |ref| !has_provided_by?(ref, resolver) }
end

#derived_file_idObject

Per-file identity derived from comprehensive + criterion. Format: <comprehensive-id-dir>/<criterion-slug> where the dir is <source>-<id> (downcased, kebab-case) and the slug is the English criterion (kebab-case) or a structural fallback. Stable across runs for the same content.

Returns nil if comprehensive is empty.



135
136
137
138
139
140
141
142
143
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 135

def derived_file_id
  return nil unless comprehensive.is_a?(ConceptRef) && comprehensive.id

  comp_dir = comprehensive_dir_name
  return nil unless comp_dir

  slug = criterion_slug
  "#{comp_dir}/#{slug}"
end

#external_comprehensive?(resolver = nil) ⇒ Boolean

True if the comprehensive resolves (via the supplied resolver) to a concept with status: external.

Returns:

  • (Boolean)


93
94
95
96
97
98
99
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 93

def external_comprehensive?(resolver = nil)
  return false unless comprehensive.is_a?(ConceptRef)
  return false unless resolver

  concept = resolver.call(comprehensive)
  concept_is_external?(concept)
end

#external_members(resolver = nil) ⇒ Object

Array of members whose refs resolve to status: external. Empty when no resolver is supplied (detection is opt-in).



103
104
105
106
107
108
109
110
111
112
113
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 103

def external_members(resolver = nil)
  return [] unless resolver

  members.select do |m|
    next false unless m.is_a?(HyperedgeMember)
    next false unless m.ref.is_a?(ConceptRef)

    concept = resolver.call(m.ref)
    concept_is_external?(concept)
  end
end

#file_path(relations_dir) ⇒ Object

File path under relations/ directory. Uses #derived_file_id unless #file_id was explicitly set (e.g., by RelationLoader preserving the source path on parse).



148
149
150
151
152
153
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 148

def file_path(relations_dir)
  id = file_id || derived_file_id
  return nil unless id

  File.join(relations_dir, "#{id}.yaml")
end

#partial?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 70

def partial?
  completeness == "partial"
end

#validate!Object



58
59
60
61
62
63
64
# File 'lib/glossarist/v3/abstract_hyperedge.rb', line 58

def validate!
  validate_comprehensive!
  validate_members!
  validate_self_loop!
  validate_completeness!
  self
end