Class: Ea::Transformations::FormatRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/ea/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/ea/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/ea/transformations/format_registry.rb', line 20

def parsers
  @parsers
end

Class Method Details

.with_defaultsFormatRegistry

Create a new instance with default parsers loaded

Returns:



211
212
213
214
215
# File 'lib/ea/transformations/format_registry.rb', line 211

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



141
142
143
# File 'lib/ea/transformations/format_registry.rb', line 141

def all_extensions
  @extensions.dup
end

#all_parsersHash<String, Class>

Get all registered parsers

Returns:

  • (Hash<String, Class>)

    Map of extensions to parser classes



124
125
126
127
# File 'lib/ea/transformations/format_registry.rb', line 124

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

Raises:

  • (ArgumentError)


64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/ea/transformations/format_registry.rb', line 64

def auto_register_from_parser(parser_class)
  unless parser_class.is_a?(Class) &&
         parser_class <= Ea::Transformations::Parsers::BaseParser
    raise ArgumentError,
          "#{parser_class} must inherit from " \
          "Ea::Transformations::Parsers::BaseParser"
  end

  extensions = parser_class.new.supported_extensions
  raise ArgumentError, "Extension cannot be nil or empty" if extensions.nil? || extensions.empty?

  register(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



287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/ea/transformations/format_registry.rb', line 287

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



187
188
189
190
191
# File 'lib/ea/transformations/format_registry.rb', line 187

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
# File 'lib/ea/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|
      parser_klass = parser_class.new
      parser_klass.content_patterns.each do |pattern|
        if header.match?(pattern)
          return parser_for_extension(ext)
        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



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/ea/transformations/format_registry.rb', line 168

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)



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

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

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

  # Load QEA parser if available
  begin
    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:



198
199
200
201
202
203
204
205
206
# File 'lib/ea/transformations/format_registry.rb', line 198

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



82
83
84
85
86
# File 'lib/ea/transformations/format_registry.rb', line 82

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



92
93
94
95
# File 'lib/ea/transformations/format_registry.rb', line 92

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]



132
133
134
135
136
# File 'lib/ea/transformations/format_registry.rb', line 132

def parsers_by_priority
  @parsers.sort_by do |_ext, parser_class|
    parser_class.new.priority
  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/ea/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



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/ea/transformations/format_registry.rb', line 148

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



116
117
118
119
# File 'lib/ea/transformations/format_registry.rb', line 116

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



101
102
103
# File 'lib/ea/transformations/format_registry.rb', line 101

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



109
110
111
# File 'lib/ea/transformations/format_registry.rb', line 109

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/ea/transformations/format_registry.rb', line 54

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