Class: Lutaml::ModelTransformations::FormatRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/lutaml/model_transformations/format_registry.rb

Overview

Format Registry manages parser registration and discovery.

This class implements the Registry pattern to provide a centralized location for managing format parsers. It follows the Open/Closed Principle by allowing new parsers to be registered without modifying existing code.

Examples:

Register a parser

registry = FormatRegistry.new
registry.register(".custom", MyCustomParser)

Get parser for extension

parser_class = registry.parser_for_extension(".xmi")
parser = parser_class.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeFormatRegistry

Returns a new instance of FormatRegistry.



22
23
24
25
26
# File 'lib/lutaml/model_transformations/format_registry.rb', line 22

def initialize
  @parsers = {}
  @extensions = []
  @default_parsers_loaded = false
end

Instance Attribute Details

#parsersHash<String, Class> (readonly)

Returns Map of extensions to parser classes.

Returns:

  • (Hash<String, Class>)

    Map of extensions to parser classes



20
21
22
# File 'lib/lutaml/model_transformations/format_registry.rb', line 20

def parsers
  @parsers
end

Class Method Details

.with_defaultsFormatRegistry

Create a new instance with default parsers loaded

Returns:



209
210
211
212
213
# File 'lib/lutaml/model_transformations/format_registry.rb', line 209

def self.with_defaults
  registry = new
  registry.load_default_parsers
  registry
end

Instance Method Details

#all_extensionsArray<String>

Get all registered extensions

Returns:

  • (Array<String>)

    List of registered extensions



139
140
141
# File 'lib/lutaml/model_transformations/format_registry.rb', line 139

def all_extensions
  @extensions.dup
end

#all_parsersHash<String, Class>

Get all registered parsers

Returns:

  • (Hash<String, Class>)

    Map of extensions to parser classes



118
119
120
121
# File 'lib/lutaml/model_transformations/format_registry.rb', line 118

def all_parsers
  # ensure_default_parsers_loaded!
  @parsers.dup
end

#auto_register_from_parser(parser_class) ⇒ void

This method returns an undefined value.

Auto-register a parser class based on its supported extensions

Parameters:

  • parser_class (Class)

    Parser class inherited from BaseParser



64
65
66
67
68
69
70
# File 'lib/lutaml/model_transformations/format_registry.rb', line 64

def auto_register_from_parser(parser_class)
  supported_extensions = ""
  if parser_class.new.respond_to?(:supported_extensions)
    supported_extensions = parser_class.new.supported_extensions
  end
  register(supported_extensions, parser_class)
end

#best_parser_for_file(file_path, use_content_detection: true) ⇒ Class?

Get the best parser for a file using multiple detection methods

Parameters:

  • file_path (String)

    Path to the file

  • use_content_detection (Boolean) (defaults to: true)

    Whether to use content detection

Returns:

  • (Class, nil)

    Best parser class for the file



289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/lutaml/model_transformations/format_registry.rb', line 289

def best_parser_for_file(file_path, use_content_detection: true)
  # First try extension-based detection
  parser = parser_for_file(file_path)
  return parser if parser

  # Fall back to content detection if enabled
  if use_content_detection
    parser = detect_by_content(file_path)
    return parser if parser
  end

  nil
end

#clearvoid

This method returns an undefined value.

Clear all registered parsers



185
186
187
188
189
# File 'lib/lutaml/model_transformations/format_registry.rb', line 185

def clear
  @extensions.clear
  @parsers.clear
  @default_parsers_loaded = false
end

#detect_by_content(file_path) ⇒ Class?

Detect format by file content (magic bytes/signatures)

Parameters:

  • file_path (String)

    Path to the file

Returns:

  • (Class, nil)

    Parser class based on content detection



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/lutaml/model_transformations/format_registry.rb', line 244

def detect_by_content(file_path) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
  # ensure_default_parsers_loaded!
  unless File.exist?(file_path)
    raise ArgumentError, "#{file_path} does not exist!"
  end

  # Read first few bytes to detect format
  File.open(file_path, "rb") do |file|
    header = file.read(1024) # Read first 1KB

    return nil if header.nil? || header.empty?

    # Check header match for content patterns
    @parsers.each do |ext, parser_class|
      if parser_class.new.respond_to? :content_patterns
        parser_klass = parser_class.new
        parser_klass.content_patterns.each do |pattern|
          if header.match?(pattern)
            return parser_for_extension(ext)
          end
        end
      end
    end

    # Check for XML/XMI signatures
    if header.include?("<?xml") && header.include?("xmi:")
      ensure_default_parsers_loaded!
      return parser_for_extension(".xmi")
    end

    # Check for SQLite database signature (QEA files)
    if header[0..15] == "SQLite format 3\0"
      ensure_default_parsers_loaded!
      return parser_for_extension(".qea")
    end
  end

  nil
end

#export_configurationObject

rubocop:disable Metrics/MethodLength



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/lutaml/model_transformations/format_registry.rb', line 166

def export_configuration # rubocop:disable Metrics/MethodLength
  {
    exported_at: Time.now,
    parsers: @parsers.map do |parser|
      _ext, parser_class = parser

      {
        parser_class: parser_class,
        extensions: parser_class.new.supported_extensions,
        priority: parser_class.new.priority,
        format: parser_class.new.format_name,
      }
    end,
  }
end

#load_default_parsersvoid

This method returns an undefined value.

Load default parsers (called automatically when needed)



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/lutaml/model_transformations/format_registry.rb', line 218

def load_default_parsers # rubocop:disable Metrics/MethodLength
  return if @default_parsers_loaded

  # Load XMI parser if available
  begin
    require_relative "parsers/xmi_parser"
    register(".xmi", Parsers::XmiParser)
  rescue LoadError
    # XMI parser not available, skip
  end

  # Load QEA parser if available
  begin
    require_relative "parsers/qea_parser"
    register(".qea", Parsers::QeaParser)
  rescue LoadError
    # QEA parser not available, skip
  end

  @default_parsers_loaded = true
end

#load_from_configuration(configuration) ⇒ void

This method returns an undefined value.

Load parsers from configuration

definitions

Parameters:



196
197
198
199
200
201
202
203
204
# File 'lib/lutaml/model_transformations/format_registry.rb', line 196

def load_from_configuration(configuration)
  configuration.enabled_parsers.each do |parser_config|
    parser_class = constantize_parser_class(parser_config.parser_class)
    register(parser_config.extension, parser_class)
  rescue NameError => e
    warn "Warning: Could not load parser " \
         "#{parser_config.parser_class}: #{e.message}"
  end
end

#parser_for_extension(extension) ⇒ Class?

Get parser class for a file extension

Parameters:

  • extension (String)

    File extension (e.g., “.xmi”, “.qea”)

Returns:

  • (Class, nil)

    Parser class, or nil if not found



76
77
78
79
80
# File 'lib/lutaml/model_transformations/format_registry.rb', line 76

def parser_for_extension(extension)
  # ensure_default_parsers_loaded!
  normalized_ext = normalize_extension(extension)
  @parsers[normalized_ext]
end

#parser_for_file(file_path) ⇒ Class?

Get parser class for a file path

Parameters:

  • file_path (String)

    Path to the file

Returns:

  • (Class, nil)

    Parser class, or nil if not found



86
87
88
89
# File 'lib/lutaml/model_transformations/format_registry.rb', line 86

def parser_for_file(file_path)
  extension = File.extname(file_path)
  parser_for_extension(extension)
end

#parsers_by_priorityArray<Array(String, Class)>

Get parsers sorted by priority (highest first)

Returns:

  • (Array<Array(String, Class)>)

    List of [extension, parser_class]



126
127
128
129
130
131
132
133
134
# File 'lib/lutaml/model_transformations/format_registry.rb', line 126

def parsers_by_priority
  @parsers.sort_by do |_ext, parser_class|
    if parser_class.method_defined?(:priority)
      parser_class.new.priority
    else
      100
    end
  end.reverse
end

#register(extension, parser_class) ⇒ Object

Register a parser for a file extension

interface

Parameters:

  • extension (String)

    File extension (e.g., “.xmi”, “.qea”)

  • parser_class (Class)

    Parser class implementing BaseParser

Raises:

  • (ArgumentError)

    if extension or parser_class is invalid



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/lutaml/model_transformations/format_registry.rb', line 34

def register(extension, parser_class) # rubocop:disable Metrics/MethodLength
  if extension.is_a?(Array)
    extension.each { |ext| register(ext, parser_class) }
    return
  end

  validate_extension!(extension)
  validate_parser_class!(parser_class)

  normalized_ext = normalize_extension(extension)
  @parsers[normalized_ext] = parser_class
  unless @extensions.include?(normalized_ext)
    @extensions << normalized_ext
  end
end

#statisticsHash

Get statistics about registered parsers

Returns:

  • (Hash)

    Statistics hash



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/lutaml/model_transformations/format_registry.rb', line 146

def statistics # rubocop:disable Metrics/MethodLength
  parsers = @parsers.values.uniq
  total_parsers = parsers.size
  ext_size = @extensions.size

  {
    total_parsers: total_parsers,
    total_extensions: ext_size,
    extensions_per_parser: (ext_size.to_f / total_parsers).round(2),
    parser_details: parsers.map do |parser_class|
      {
        parser: parser_class,
        extensions: parser_class.new.supported_extensions,
        priority: parser_class.new.priority,
        format_name: parser_class.new.format_name,
      }
    end,
  }
end

#supported_extensionsArray<String>

Get all supported extensions

Returns:

  • (Array<String>)

    List of supported file extensions



110
111
112
113
# File 'lib/lutaml/model_transformations/format_registry.rb', line 110

def supported_extensions
  # ensure_default_parsers_loaded!
  @parsers.keys.sort
end

#supports_extension?(extension) ⇒ Boolean

Check if an extension is supported

Parameters:

  • extension (String)

    File extension to check

Returns:

  • (Boolean)

    true if extension is supported



95
96
97
# File 'lib/lutaml/model_transformations/format_registry.rb', line 95

def supports_extension?(extension)
  parser_for_extension(extension) != nil
end

#supports_file?(file_path) ⇒ Boolean

Check if a file is supported

Parameters:

  • file_path (String)

    Path to the file

Returns:

  • (Boolean)

    true if file format is supported



103
104
105
# File 'lib/lutaml/model_transformations/format_registry.rb', line 103

def supports_file?(file_path)
  parser_for_file(file_path) != nil
end

#unregister(extension) ⇒ Class?

Unregister a parser for a file extension

Parameters:

  • extension (String)

    File extension to unregister

Returns:

  • (Class, nil)

    The unregistered parser class, or nil if not found



54
55
56
57
58
# File 'lib/lutaml/model_transformations/format_registry.rb', line 54

def unregister(extension)
  normalized_ext = normalize_extension(extension)
  @extensions.delete(normalized_ext)
  @parsers.delete(normalized_ext)
end