Class: Expressir::Model::Repository

Inherits:
ModelElement
  • Object
show all
Defined in:
lib/expressir/model/repository.rb

Overview

Multi-schema global scope with enhanced repository features Focuses on schema management and delegates indexing/validation to specialized classes

A Repository contains multiple ExpFile instances (one per parsed .exp file).

Structure:

Repository
└── files: [ExpFile, ...]
    └── ExpFile (path: "a.exp")
        ├── untagged_remarks: [...]  # file-level preamble
        └── schemas: [SchemaA]

Constant Summary

Constants inherited from ModelElement

ModelElement::POLYMORPHIC_CLASS_MAP, ModelElement::SKIP_ATTRIBUTES

Instance Attribute Summary collapse

Attributes inherited from ModelElement

#parent

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ModelElement

#add_remark, #children_by_id, #find, #path, #reset_children_by_id, #source, #to_s

Constructor Details

#initialize(base_dir: nil, schemas: nil) ⇒ Repository

Returns a new instance of Repository.



47
48
49
50
51
52
53
54
55
56
57
# File 'lib/expressir/model/repository.rb', line 47

def initialize(*, base_dir: nil, schemas: nil, **)
  super(*, **)
  @base_dir = base_dir
  @entity_index = nil
  @type_index = nil
  @reference_index = nil
  @_schemas = []

  # Support direct schemas initialization
  schemas&.each { |schema| @_schemas << schema }
end

Instance Attribute Details

#_schemasObject (readonly)

Internal schema storage for direct manipulation



36
37
38
# File 'lib/expressir/model/repository.rb', line 36

def _schemas
  @_schemas
end

#base_dirObject

Base directory for schema files



22
23
24
# File 'lib/expressir/model/repository.rb', line 22

def base_dir
  @base_dir
end

#entity_indexObject (readonly)

Index instances (lazy-loaded)



25
26
27
# File 'lib/expressir/model/repository.rb', line 25

def entity_index
  @entity_index
end

#reference_indexObject (readonly)

Index instances (lazy-loaded)



25
26
27
# File 'lib/expressir/model/repository.rb', line 25

def reference_index
  @reference_index
end

#type_indexObject (readonly)

Index instances (lazy-loaded)



25
26
27
# File 'lib/expressir/model/repository.rb', line 25

def type_index
  @type_index
end

Class Method Details

.from_files(file_paths, base_dir: nil) ⇒ Repository

Build repository from list of schema files

Parameters:

  • file_paths (Array<String>)

    Schema file paths

  • base_dir (String, nil) (defaults to: nil)

    Base directory for path resolution

Returns:

  • (Repository)

    Built repository with all schemas



295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/expressir/model/repository.rb', line 295

def self.from_files(file_paths, base_dir: nil)
  repo = new(base_dir: base_dir)

  file_paths.each do |path|
    parsed = Expressir::Express::Parser.from_file(path)
    next unless parsed

    repo.files << parsed if parsed.is_a?(ExpFile)
  end

  repo.resolve_all_references
  repo
end

.from_package(package_path) ⇒ Repository

Load repository from LER package

Parameters:

  • package_path (String)

    Path to .ler package file

Returns:



312
313
314
# File 'lib/expressir/model/repository.rb', line 312

def self.from_package(package_path)
  Package::Reader.load(package_path)
end

Instance Method Details

#add_schema(schema) ⇒ Object

Add a schema to the repository

Parameters:



74
75
76
77
78
# File 'lib/expressir/model/repository.rb', line 74

def add_schema(schema)
  return unless schema

  @_schemas << schema
end

#all_schemasArray<Declarations::Schema>

Alias for schemas

Returns:



68
69
70
# File 'lib/expressir/model/repository.rb', line 68

def all_schemas
  schemas
end

#build_indexesvoid

This method returns an undefined value.

Build indexes for entities, types, and references



87
88
89
90
91
92
# File 'lib/expressir/model/repository.rb', line 87

def build_indexes
  target_schemas = all_schemas
  @entity_index = Indexes::EntityIndex.new(target_schemas)
  @type_index = Indexes::TypeIndex.new(target_schemas)
  @reference_index = Indexes::ReferenceIndex.new(target_schemas)
end

#childrenArray<Declarations::Schema>

Returns:



81
82
83
# File 'lib/expressir/model/repository.rb', line 81

def children
  schemas
end

#dependency_statisticsHash

Get dependency statistics

Returns:

  • (Hash)

    Statistics about interface dependencies



243
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
# File 'lib/expressir/model/repository.rb', line 243

def dependency_statistics
  stats = {
    total_interfaces: 0,
    use_from_count: 0,
    reference_from_count: 0,
    most_referenced: [],
    most_dependent: [],
  }

  reference_counts = Hash.new(0)
  dependency_counts = Hash.new(0)

  all_schemas.each do |schema|
    next unless schema.interfaces&.any?

    stats[:total_interfaces] += schema.interfaces.size
    dependency_counts[schema.id] = schema.interfaces.size

    schema.interfaces.each do |interface|
      stats[:use_from_count] += 1 if interface.kind == Declarations::Interface::USE
      stats[:reference_from_count] += 1 if interface.kind == Declarations::Interface::REFERENCE
      reference_counts[interface.schema.id] += 1 if interface.schema
    end
  end

  stats[:most_referenced] = reference_counts.sort_by do |_, v|
    -v
  end.take(10).to_h
  stats[:most_dependent] = dependency_counts.sort_by do |_, v|
    -v
  end.take(10).to_h

  stats
end

#export_to_package(output_path, options = {}) ⇒ String

Export repository to LER package

Parameters:

  • output_path (String)

    Path for output .ler file

  • options (Hash) (defaults to: {})

    Package options

Options Hash (options):

  • :name (String)

    Package name

  • :version (String)

    Package version

  • :description (String)

    Package description

  • :express_mode (String) — default: 'include_all'

    Bundling mode

  • :resolution_mode (String) — default: 'resolved'

    Resolution mode

  • :serialization_format (String) — default: 'marshal'

    Serialization format

Returns:

  • (String)

    Path to created package



326
327
328
329
# File 'lib/expressir/model/repository.rb', line 326

def export_to_package(output_path, options = {})
  builder = Package::Builder.new
  builder.build(self, output_path, options)
end

#find_entity(qualified_name:) ⇒ Declarations::Entity?

Find entity by qualified name

Parameters:

  • qualified_name (String)

    Entity qualified name (e.g., “action_schema.action”)

Returns:



97
98
99
100
# File 'lib/expressir/model/repository.rb', line 97

def find_entity(qualified_name:)
  ensure_indexes_built
  @entity_index.find(qualified_name)
end

#find_type(qualified_name:) ⇒ Declarations::Type?

Find type by qualified name

Parameters:

  • qualified_name (String)

    Type qualified name

Returns:



105
106
107
108
# File 'lib/expressir/model/repository.rb', line 105

def find_type(qualified_name:)
  ensure_indexes_built
  @type_index.find(qualified_name)
end

#largest_schemas(limit = 10) ⇒ Array<Hash>

Get largest schemas by total element count

Parameters:

  • limit (Integer) (defaults to: 10)

    Maximum number of schemas to return

Returns:

  • (Array<Hash>)

    Array of hashes with :schema and :total_elements



208
209
210
211
212
213
214
215
# File 'lib/expressir/model/repository.rb', line 208

def largest_schemas(limit = 10)
  all_schemas.map do |s|
    {
      schema: s,
      total_elements: count_schema_elements(s),
    }
  end.sort_by { |item| -item[:total_elements] }.take(limit)
end

#list_entities(schema: nil, format: :object) ⇒ Array

List all entities

Parameters:

  • schema (String, nil) (defaults to: nil)

    Filter by schema name

  • format (Symbol) (defaults to: :object)

    Output format (:object, :hash, :json, :yaml)

Returns:

  • (Array)

    List of entities



114
115
116
117
118
119
120
121
122
# File 'lib/expressir/model/repository.rb', line 114

def list_entities(schema: nil, format: :object)
  ensure_indexes_built

  entities = @entity_index.list(schema: schema)

  format_output(entities, format) do |entity|
    { id: entity.id, schema: entity.parent.id, path: entity.path }
  end
end

#list_types(schema: nil, category: nil, format: :object) ⇒ Array

List all types

Parameters:

  • schema (String, nil) (defaults to: nil)

    Filter by schema name

  • category (String, nil) (defaults to: nil)

    Filter by type category (select, enumeration, etc.)

  • format (Symbol) (defaults to: :object)

    Output format (:object, :hash, :json, :yaml)

Returns:

  • (Array)

    List of types



129
130
131
132
133
134
135
136
137
138
# File 'lib/expressir/model/repository.rb', line 129

def list_types(schema: nil, category: nil, format: :object)
  ensure_indexes_built

  types = @type_index.list(schema: schema, category: category)

  format_output(types, format) do |type|
    { id: type.id, schema: type.parent.id, path: type.path,
      category: @type_index.categorize(type) }
  end
end

#resolve_all_referencesvoid

This method returns an undefined value.

Resolve all references across schemas Uses the existing ResolveReferencesModelVisitor



143
144
145
146
# File 'lib/expressir/model/repository.rb', line 143

def resolve_all_references
  visitor = Expressir::Express::ResolveReferencesModelVisitor.new
  visitor.visit(self)
end

#restore_indexes(entity_index: nil, type_index: nil, reference_index: nil) ⇒ Object

Restore deserialized indexes (used by Package::Reader)



28
29
30
31
32
33
# File 'lib/expressir/model/repository.rb', line 28

def restore_indexes(entity_index: nil, type_index: nil,
reference_index: nil)
  @entity_index = entity_index if entity_index
  @type_index = type_index if type_index
  @reference_index = reference_index if reference_index
end

#schema_complexity(schema) ⇒ Integer

Calculate schema complexity score Entities=2, Types=1, Functions=3, Procedures=3, Rules=4, Interfaces=2

Parameters:

Returns:

  • (Integer)

    Complexity score



221
222
223
224
225
226
227
228
229
230
# File 'lib/expressir/model/repository.rb', line 221

def schema_complexity(schema)
  score = 0
  score += (schema.entities&.size || 0) * 2
  score += (schema.types&.size || 0) * 1
  score += (schema.functions&.size || 0) * 3
  score += (schema.procedures&.size || 0) * 3
  score += (schema.rules&.size || 0) * 4
  score += (schema.interfaces&.size || 0) * 2
  score
end

#schemasArray<Declarations::Schema>

Get all schemas (from both files and direct storage)

Returns:



61
62
63
64
# File 'lib/expressir/model/repository.rb', line 61

def schemas
  file_schemas = files&.flat_map(&:schemas)&.compact || []
  file_schemas + @_schemas
end

#schemas_by_categoryHash

Group schemas by category based on their contents

Returns:

  • (Hash)

    Hash with category keys and schema arrays



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/expressir/model/repository.rb', line 189

def schemas_by_category
  target_schemas = all_schemas
  {
    with_entities: target_schemas.select { |s| s.entities&.any? },
    with_types: target_schemas.select { |s| s.types&.any? },
    with_functions: target_schemas.select { |s| s.functions&.any? },
    with_rules: target_schemas.select { |s| s.rules&.any? },
    interface_only: target_schemas.select do |s|
      s.interfaces&.any? && !s.entities&.any? && !s.types&.any?
    end,
    empty: target_schemas.select do |s|
      !s.entities&.any? && !s.types&.any? && !s.functions&.any?
    end,
  }
end

#schemas_by_complexity(limit = 10) ⇒ Array<Hash>

Get schemas sorted by complexity

Parameters:

  • limit (Integer) (defaults to: 10)

    Maximum number of schemas to return

Returns:

  • (Array<Hash>)

    Array of hashes with :schema and :complexity



235
236
237
238
239
# File 'lib/expressir/model/repository.rb', line 235

def schemas_by_complexity(limit = 10)
  all_schemas.map { |s| { schema: s, complexity: schema_complexity(s) } }
    .sort_by { |item| -item[:complexity] }
    .take(limit)
end

#statistics(format: :hash) ⇒ Hash, String

Get statistics

Parameters:

  • format (Symbol) (defaults to: :hash)

    Output format (:hash, :json, :yaml)

Returns:

  • (Hash, String)

    Repository statistics



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/expressir/model/repository.rb', line 160

def statistics(format: :hash)
  ensure_indexes_built

  stats = {
    total_schemas: all_schemas.size,
    total_entities: count_entities,
    total_types: count_types,
    total_functions: count_functions,
    total_rules: count_rules,
    total_procedures: count_procedures,
    entities_by_schema: entities_by_schema_counts,
    types_by_category: types_by_category_counts,
    interfaces: interface_counts,
  }

  case format
  when :json
    require "json"
    stats.to_json
  when :yaml
    require "yaml"
    stats.to_yaml
  else
    stats
  end
end

#to_manifestSchemaManifest

Generate SchemaManifest from repository

Returns:



280
281
282
283
284
285
286
287
288
289
# File 'lib/expressir/model/repository.rb', line 280

def to_manifest
  manifest = Expressir::SchemaManifest.new
  all_schemas.each do |schema|
    manifest.schemas << Expressir::SchemaManifestEntry.new(
      id: schema.id,
      path: schema.file || "#{schema.id}.exp",
    )
  end
  manifest
end

#validate(strict: false) ⇒ Hash

Validate repository consistency

Parameters:

  • strict (Boolean) (defaults to: false)

    Enable strict validation

Returns:

  • (Hash)

    Validation results with :valid?, :errors, :warnings



151
152
153
154
155
# File 'lib/expressir/model/repository.rb', line 151

def validate(strict: false)
  ensure_indexes_built
  validator = RepositoryValidator.new(all_schemas, @reference_index)
  validator.validate(strict: strict)
end