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

Direct Known Subclasses

Klass, Property, Relation, Unit, ValueList, ValueTerm, ViewControl

Defined Under Namespace

Modules: FieldReader, FieldRegistry 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.



86
87
88
89
90
91
92
93
# File 'lib/opencdd/entity.rb', line 86

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.



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

def irdi
  @irdi
end

#meta_class_irdiObject (readonly)

Returns the value of attribute meta_class_irdi.



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

def meta_class_irdi
  @meta_class_irdi
end

#propertiesObject (readonly)

Returns the value of attribute properties.



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

def properties
  @properties
end

#schemaObject (readonly)

Returns the value of attribute schema.



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

def schema
  @schema
end

#source_locationObject (readonly)

Returns the value of attribute source_location.



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

def source_location
  @source_location
end

Class Method Details

.default_code_property_id(meta_class_irdi) ⇒ Object



82
83
84
# File 'lib/opencdd/entity.rb', line 82

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.



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

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



69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/opencdd/entity.rb', line 69

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]
  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



274
275
276
# File 'lib/opencdd/entity.rb', line 274

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

Instance Method Details

#[](key) ⇒ Object



166
167
168
# File 'lib/opencdd/entity.rb', line 166

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).



250
251
252
253
# File 'lib/opencdd/entity.rb', line 250

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.



241
242
243
244
# File 'lib/opencdd/entity.rb', line 241

def attach_version_history(version_history)
  @version_history = version_history
  self
end

#codeObject Also known as: short



99
100
101
# File 'lib/opencdd/entity.rb', line 99

def code
  @irdi&.code
end

#each_propertyObject



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

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)


183
184
185
186
187
# File 'lib/opencdd/entity.rb', line 183

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

#hashObject



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

def hash
  [irdi, type].hash
end

#inspectObject



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

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

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


170
171
172
# File 'lib/opencdd/entity.rb', line 170

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

#keysObject



174
175
176
# File 'lib/opencdd/entity.rb', line 174

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.



222
223
224
# File 'lib/opencdd/entity.rb', line 222

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

#replace_code_value!(code_property_id, new_code) ⇒ Object



204
205
206
207
208
# File 'lib/opencdd/entity.rb', line 204

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



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

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.



270
271
272
# File 'lib/opencdd/entity.rb', line 270

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

#typeObject



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

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.



230
231
232
233
234
235
# File 'lib/opencdd/entity.rb', line 230

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