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, #remark_infos, #reset_children_by_id, #source, #to_s

Constructor Details

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

Returns a new instance of Repository.



35
36
37
38
39
40
41
42
43
44
45
# File 'lib/expressir/model/repository.rb', line 35

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



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

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



283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/expressir/model/repository.rb', line 283

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:



300
301
302
# File 'lib/expressir/model/repository.rb', line 300

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:



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

def add_schema(schema)
  return unless schema

  @_schemas << schema
end

#all_schemasArray<Declarations::Schema>

Alias for schemas

Returns:



56
57
58
# File 'lib/expressir/model/repository.rb', line 56

def all_schemas
  schemas
end

#build_indexesvoid

This method returns an undefined value.

Build indexes for entities, types, and references



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

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:



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

def children
  schemas
end

#dependency_statisticsHash

Get dependency statistics

Returns:

  • (Hash)

    Statistics about interface dependencies



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/expressir/model/repository.rb', line 231

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



314
315
316
317
# File 'lib/expressir/model/repository.rb', line 314

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:



85
86
87
88
# File 'lib/expressir/model/repository.rb', line 85

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:



93
94
95
96
# File 'lib/expressir/model/repository.rb', line 93

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



196
197
198
199
200
201
202
203
# File 'lib/expressir/model/repository.rb', line 196

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



102
103
104
105
106
107
108
109
110
# File 'lib/expressir/model/repository.rb', line 102

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



117
118
119
120
121
122
123
124
125
126
# File 'lib/expressir/model/repository.rb', line 117

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



131
132
133
134
# File 'lib/expressir/model/repository.rb', line 131

def resolve_all_references
  visitor = Expressir::Express::ResolveReferencesModelVisitor.new
  visitor.visit(self)
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



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

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:



49
50
51
52
# File 'lib/expressir/model/repository.rb', line 49

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



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/expressir/model/repository.rb', line 177

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



223
224
225
226
227
# File 'lib/expressir/model/repository.rb', line 223

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



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/expressir/model/repository.rb', line 148

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:



268
269
270
271
272
273
274
275
276
277
# File 'lib/expressir/model/repository.rb', line 268

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



139
140
141
142
143
# File 'lib/expressir/model/repository.rb', line 139

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