Class: Metaschema::ModelGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/metaschema/model_generator.rb,
lib/metaschema/model_generator/utils.rb,
lib/metaschema/model_generator/field_factory.rb,
lib/metaschema/model_generator/assembly_factory.rb,
lib/metaschema/model_generator/services/field_serializer.rb,
lib/metaschema/model_generator/services/field_deserializer.rb,
lib/metaschema/model_generator/services/collapsibles_collapser.rb

Overview

Generates Ruby classes (Lutaml::Model::Serializable subclasses) from NIST Metaschema definitions. The generated classes support XML and JSON round-tripping with full fidelity.

Delegates field class creation to FieldFactory and assembly class creation to AssemblyFactory. This class handles import resolution, augment application, and shared utilities.

Defined Under Namespace

Modules: Services, Utils Classes: AssemblyFactory, FieldFactory

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#assembly_defsObject (readonly)

Shared state — accessed by FieldFactory and AssemblyFactory via @g



43
44
45
# File 'lib/metaschema/model_generator.rb', line 43

def assembly_defs
  @assembly_defs
end

#classesObject (readonly)

Shared state — accessed by FieldFactory and AssemblyFactory via @g



43
44
45
# File 'lib/metaschema/model_generator.rb', line 43

def classes
  @classes
end

#current_assembly_nameObject

Returns the value of attribute current_assembly_name.



44
45
46
# File 'lib/metaschema/model_generator.rb', line 44

def current_assembly_name
  @current_assembly_name
end

#field_defsObject (readonly)

Shared state — accessed by FieldFactory and AssemblyFactory via @g



43
44
45
# File 'lib/metaschema/model_generator.rb', line 43

def field_defs
  @field_defs
end

#flag_defsObject (readonly)

Shared state — accessed by FieldFactory and AssemblyFactory via @g



43
44
45
# File 'lib/metaschema/model_generator.rb', line 43

def flag_defs
  @flag_defs
end

Class Method Details

.generate_from_file(metaschema_path, base_path: nil) ⇒ Object



20
21
22
23
# File 'lib/metaschema/model_generator.rb', line 20

def generate_from_file(metaschema_path, base_path: nil)
  base_path ||= File.dirname(File.expand_path(metaschema_path))
  generate_from_xml(File.read(metaschema_path), base_path: base_path)
end

.generate_from_metaschema(metaschema, base_path: nil) ⇒ Object



30
31
32
# File 'lib/metaschema/model_generator.rb', line 30

def generate_from_metaschema(metaschema, base_path: nil)
  new.generate(metaschema, base_path: base_path)
end

.generate_from_xml(xml_string, base_path: nil) ⇒ Object



25
26
27
28
# File 'lib/metaschema/model_generator.rb', line 25

def generate_from_xml(xml_string, base_path: nil)
  metaschema = Metaschema::Root.from_xml(xml_string)
  new.generate(metaschema, base_path: base_path)
end

.to_ruby_source(metaschema_path, module_name:, base_path: nil, split: false) ⇒ Object



34
35
36
37
38
39
# File 'lib/metaschema/model_generator.rb', line 34

def to_ruby_source(metaschema_path, module_name:, base_path: nil,
split: false)
  classes = generate_from_file(metaschema_path, base_path: base_path)
  emitter = RubySourceEmitter.new(classes, module_name, self)
  split ? emitter.emit_split : emitter.emit
end

Instance Method Details

#add_flag_reference(klass, flag_ref) ⇒ Object



117
118
119
120
121
122
123
124
125
# File 'lib/metaschema/model_generator.rb', line 117

def add_flag_reference(klass, flag_ref)
  return unless flag_ref.ref

  flag_name = flag_ref.ref
  flag_def = @flag_defs[flag_name]
  attr_name = Utils.safe_attr(flag_name)
  type = flag_def ? TypeMapper.map(flag_def.as_type) : :string
  klass.attribute attr_name, type
end

#add_inline_flag(klass, flag_def) ⇒ Object

── Shared Utilities (used by both factories) ──────────────────────



109
110
111
112
113
114
115
# File 'lib/metaschema/model_generator.rb', line 109

def add_inline_flag(klass, flag_def)
  return unless flag_def.name

  attr_name = Utils.safe_attr(flag_def.name)
  type = TypeMapper.map(flag_def.as_type)
  klass.attribute attr_name, type
end

#apply_constraint_validation(klass, constraint_def) ⇒ Object

── Constraint Validation Integration ──────────────────────────────



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/metaschema/model_generator.rb', line 139

def apply_constraint_validation(klass, constraint_def)
  return unless constraint_def

  klass.instance_variable_set(:@metaschema_constraints, constraint_def)
  klass.define_singleton_method(:metaschema_constraints) do
    @metaschema_constraints
  end

  klass.define_method(:validate_constraints) do
    validator = ConstraintValidator.new
    validator.validate(self, self.class.metaschema_constraints)
  end
end

#assembly_xml_element_name(assembly_ref) ⇒ Object

── XML Element Name Resolution ──────────────────────────────────



83
84
85
86
87
88
89
90
91
92
93
# File 'lib/metaschema/model_generator.rb', line 83

def assembly_xml_element_name(assembly_ref)
  ref_name = assembly_ref.ref
  return ref_name unless ref_name

  return assembly_ref.use_name.content if assembly_ref.use_name&.content

  defn = @assembly_defs[ref_name]
  return defn.use_name.content if defn&.use_name&.content

  ref_name
end

#create_placeholder_assembly(name) ⇒ Object



132
133
134
135
# File 'lib/metaschema/model_generator.rb', line 132

def create_placeholder_assembly(name)
  key = "Assembly_#{name.gsub('-', '_')}"
  @classes[key] ||= Class.new(Lutaml::Model::Serializable)
end

#field_xml_element_name(field_ref) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
# File 'lib/metaschema/model_generator.rb', line 95

def field_xml_element_name(field_ref)
  ref_name = field_ref.ref
  return ref_name unless ref_name

  return field_ref.use_name.content if field_ref.use_name&.content

  defn = @field_defs[ref_name]
  return defn.use_name.content if defn&.use_name&.content

  ref_name
end

#generate(metaschema, base_path: nil) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/metaschema/model_generator.rb', line 46

def generate(metaschema, base_path: nil)
  @classes = {}
  @flag_defs = {}
  @assembly_defs = {}
  @field_defs = {}
  @namespace = metaschema.namespace
  @current_assembly_name = nil

  # Resolve imports — merge definitions from imported modules
  resolve_and_merge_imports(metaschema, base_path)

  collect_flag_definitions(metaschema)
  collect_definition_registries(metaschema)

  # Apply augments — add docs/flags to imported definitions
  apply_augments(metaschema)

  # Phase 1: Create field classes for all definitions (top-level + imported)
  @field_defs.each_value do |fd|
    next if @classes.key?("Field_#{Utils.safe_attr(fd.name)}")

    FieldFactory.new(fd, self).create
  end

  # Phase 1: Create assembly placeholders for all definitions
  # Phase 2: Populate assembly classes for all definitions
  @assembly_defs.each_value do |ad|
    factory = AssemblyFactory.new(ad, self)
    factory.create_placeholder
    factory.populate
  end

  @classes
end

#scoped_field_name(field_name) ⇒ Object



127
128
129
130
# File 'lib/metaschema/model_generator.rb', line 127

def scoped_field_name(field_name)
  base = "Field_#{field_name.gsub('-', '_')}"
  @current_assembly_name ? "#{base}_in_#{@current_assembly_name}" : base
end