Class: Opencdd::Entity

Inherits:
Lutaml::Model::Serializable
  • Object
show all
Includes:
ParseHelpers
Defined in:
lib/opencdd/entity.rb,
lib/opencdd/entity/yaml.rb,
lib/opencdd/entity/field_reader.rb,
lib/opencdd/entity/field_registry.rb,
lib/opencdd/entity/version_history.rb,
lib/opencdd/entity/name_synthesizer.rb

Defined Under Namespace

Modules: FieldReader, FieldRegistry, NameSynthesizer Classes: Dates, VersionHistory, Yaml

Constant Summary collapse

META_CLASS_CODE =
nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ParseHelpers

brace_wrapped?, paren_wrapped?, parse_irdi_list, parse_pair_list, parse_string_list, parse_synonym_tuples, unwrap_delimiters, unwrap_parens

Constructor Details

#initialize(irdi: nil, properties: nil, schema: nil, meta_class_irdi: nil) ⇒ Entity

Returns a new instance of Entity.



98
99
100
101
102
103
104
105
# File 'lib/opencdd/entity.rb', line 98

def initialize(irdi: nil, properties: nil, schema: nil, meta_class_irdi: nil)
  super({})
  @irdi = irdi
  @properties = properties || {}
  @schema = schema
  @meta_class_irdi = meta_class_irdi
  @version_history = Opencdd::Entity::VersionHistory.new
end

Instance Attribute Details

#irdiObject (readonly)

Returns the value of attribute irdi.



68
69
70
# File 'lib/opencdd/entity.rb', line 68

def irdi
  @irdi
end

#meta_class_irdiObject (readonly)

Returns the value of attribute meta_class_irdi.



68
69
70
# File 'lib/opencdd/entity.rb', line 68

def meta_class_irdi
  @meta_class_irdi
end

#propertiesObject (readonly)

Returns the value of attribute properties.



68
69
70
# File 'lib/opencdd/entity.rb', line 68

def properties
  @properties
end

#schemaObject (readonly)

Returns the value of attribute schema.



68
69
70
# File 'lib/opencdd/entity.rb', line 68

def schema
  @schema
end

#source_locationObject (readonly)

Returns the value of attribute source_location.



68
69
70
# File 'lib/opencdd/entity.rb', line 68

def source_location
  @source_location
end

Class Method Details

.default_code_property_id(meta_class_irdi) ⇒ Object



94
95
96
# File 'lib/opencdd/entity.rb', line 94

def self.default_code_property_id(meta_class_irdi)
  Opencdd::MetaClasses.code_property_id_for(meta_class_irdi&.code)
end

.field(name, property_id = nil, value_kind = nil, multilingual: nil, synthetic: false, reader: nil, as: nil, &block) ⇒ Object

Declare a typed field on this entity class. Defaults for value_kind and multilingual are resolved from Opencdd::PropertyIds::REGISTRY when the property_id is known there — the REGISTRY is the SSOT for wire-format metadata, and re-declaring it here would violate DRY.

Pass an explicit value_kind to override (used when REGISTRY's kind doesn't match how the field is consumed — e.g. MDC_P023 is :identifier_ref in REGISTRY but Unit#structure uses it as a raw string).

synthetic: true for computed fields with no MDC_P### — requires a block (preferred) that returns the value. The block is evaluated via instance_exec on the entity, so it has access to private helpers without send dispatch.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/opencdd/entity.rb', line 36

def field(name, property_id = nil, value_kind = nil,
          multilingual: nil, synthetic: false, reader: nil,
          as: nil, &block)
  resolved_kind, resolved_ml = resolve_from_registry(property_id)
  Opencdd::Entity::FieldRegistry.register(
    entity_class: self,
    name: name,
    property_id: property_id,
    value_kind: value_kind || resolved_kind || :string,
    multilingual: multilingual.nil? ? resolved_ml : multilingual,
    synthetic: synthetic,
    reader: reader,
    block: block,
    json_key: as,
  )
  define_method(name) do |lang = nil|
    Opencdd::Entity::FieldReader.read(self, name, lang: lang)
  end
end

.from_row(row, schema:, meta_class_irdi:, code_property_id: nil) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/opencdd/entity.rb', line 70

def self.from_row(row, schema:, meta_class_irdi:, code_property_id: nil)
  code_property_id ||= default_code_property_id(meta_class_irdi)
  raw_code = code_property_id && row[code_property_id]
  # Fall back to other known code property IDs when the entity-
  # specific one is absent (happens with name-only header sheets
  # where "Code" synthesizes to MDC_P001_5 regardless of type).
  if raw_code.nil? || raw_code.to_s.strip.empty?
    Opencdd::MetaClasses::CODE_PROPERTY_IDS.each_value do |alt_id|
      val = row[alt_id]
      next if val.nil? || val.to_s.strip.empty?
      raw_code = val
      break
    end
  end
  irdi = raw_code && Opencdd::IRDI.parse(raw_code)

  props = row.each_with_object({}) do |(k, v), h|
    next if k == "__row_index__"
    h[k] = v if v
  end

  new(irdi: irdi, properties: props, schema: schema, meta_class_irdi: meta_class_irdi)
end

.from_yaml(yaml_str) ⇒ Object



286
287
288
# File 'lib/opencdd/entity.rb', line 286

def self.from_yaml(yaml_str)
  Opencdd::Entity::Yaml.from_yaml(yaml_str).to_entity
end

Instance Method Details

#[](key) ⇒ Object



178
179
180
# File 'lib/opencdd/entity.rb', line 178

def [](key)
  @properties[key.to_s]
end

#attach_source_location(loc) ⇒ Object

Attach source location (file:line) where this entity was declared. Set by Opencdd::Cddal::Builder from import/instantiation context. Nil for entities constructed from Parcel readers (which don't have a single source file).



262
263
264
265
# File 'lib/opencdd/entity.rb', line 262

def attach_source_location(loc)
  @source_location = loc
  self
end

#attach_version_history(version_history) ⇒ Object

Attach per-version provenance captured in _entity.json. Called by Opencdd::Parcel::ShardedDirReader after entity creation — the constructor doesn't take it because FlatDirReader creates entities from .xls rows without version context.



253
254
255
256
# File 'lib/opencdd/entity.rb', line 253

def attach_version_history(version_history)
  @version_history = version_history
  self
end

#codeObject Also known as: short



111
112
113
# File 'lib/opencdd/entity.rb', line 111

def code
  @irdi&.code
end

#each_propertyObject



190
191
192
193
# File 'lib/opencdd/entity.rb', line 190

def each_property
  return enum_for(:each_property) unless block_given?
  @properties.each { |k, v| yield k, v }
end

#eql?(other) ⇒ Boolean Also known as: ==

Returns:

  • (Boolean)


195
196
197
198
199
# File 'lib/opencdd/entity.rb', line 195

def eql?(other)
  other.is_a?(Opencdd::Entity) &&
    irdi == other.irdi &&
    type == other.type
end

#hashObject



201
202
203
# File 'lib/opencdd/entity.rb', line 201

def hash
  [irdi, type].hash
end

#inspectObject



207
208
209
# File 'lib/opencdd/entity.rb', line 207

def inspect
  "#<#{self.class.name} #{irdi}>"
end

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


182
183
184
# File 'lib/opencdd/entity.rb', line 182

def key?(key)
  @properties.key?(key.to_s)
end

#keysObject



186
187
188
# File 'lib/opencdd/entity.rb', line 186

def keys
  @properties.keys
end

#read_field(name, lang: nil) ⇒ Object

Read a declared field by name. Accepts an optional lang: for multilingual fields. Returns the typed value (parsed IRDI, Array, etc.) per the field's declared value_kind.



234
235
236
# File 'lib/opencdd/entity.rb', line 234

def read_field(name, lang: nil)
  Opencdd::Entity::FieldReader.read(self, name, lang: lang)
end

#replace_code_value!(code_property_id, new_code) ⇒ Object



216
217
218
219
220
# File 'lib/opencdd/entity.rb', line 216

def replace_code_value!(code_property_id, new_code)
  return self unless code_property_id
  @properties[code_property_id.to_s] = new_code.to_s
  self
end

#replace_irdi!(new_irdi) ⇒ Object



211
212
213
214
# File 'lib/opencdd/entity.rb', line 211

def replace_irdi!(new_irdi)
  @irdi = Opencdd::IRDI === new_irdi ? new_irdi : Opencdd::IRDI.parse(new_irdi.to_s)
  self
end

#to_yaml(*args) ⇒ Object

── YAML persistence via Entity::Yaml ────────────────────── Entity extends Lutaml::Model::Serializable but its own attribute set is empty — the typed YAML model lives in Entity::Yaml (the deepened adapter). This avoids name conflicts between the field DSL getters (which read from @properties and return coerced values like IRDI objects, source-language Strings, parsed Arrays) and lutaml-model serialization attrs (which must return flat types: String, Hash, Array).

lutaml-model serialization calls public_send(attr_name) during to_format, so any getter with the same name as a YAML attr is invoked. Keeping the YAML model in Entity::Yaml lets both worlds coexist: field DSL for domain access, Entity::Yaml for serialization.



282
283
284
# File 'lib/opencdd/entity.rb', line 282

def to_yaml(*args)
  Opencdd::Entity::Yaml.from_entity(self).to_yaml(*args)
end

#typeObject



107
108
109
# File 'lib/opencdd/entity.rb', line 107

def type
  Opencdd::Parcel::META_CLASS_TYPES[meta_class_irdi&.code]
end

#write_property!(id, value) ⇒ Object

Write a value to a property ID, routing through canonical-id alias resolution so writes are consistent with reads. Use this in preference to entity.properties[id] = value from non-infrastructure callers.



242
243
244
245
246
247
# File 'lib/opencdd/entity.rb', line 242

def write_property!(id, value)
  canonical = Opencdd::PropertyIds.canonical_id(id.to_s) || id.to_s
  base = canonical.to_s.split(".").first
  @properties[base] = value.to_s
  self
end