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.



39
40
41
42
43
44
45
46
47
48
49
# File 'lib/expressir/model/repository.rb', line 39

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



287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/expressir/model/repository.rb', line 287

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:



304
305
306
# File 'lib/expressir/model/repository.rb', line 304

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:



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

def add_schema(schema)
  return unless schema

  @_schemas << schema
end

#all_schemasArray<Declarations::Schema>

Alias for schemas

Returns:



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

def all_schemas
  schemas
end

#build_indexesvoid

This method returns an undefined value.

Build indexes for entities, types, and references



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

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:



73
74
75
# File 'lib/expressir/model/repository.rb', line 73

def children
  schemas
end

#dependency_statisticsHash

Get dependency statistics

Returns:

  • (Hash)

    Statistics about interface dependencies



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
265
266
267
268
# File 'lib/expressir/model/repository.rb', line 235

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



318
319
320
321
# File 'lib/expressir/model/repository.rb', line 318

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:



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

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:



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

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



200
201
202
203
204
205
206
207
# File 'lib/expressir/model/repository.rb', line 200

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



106
107
108
109
110
111
112
113
114
# File 'lib/expressir/model/repository.rb', line 106

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



121
122
123
124
125
126
127
128
129
130
# File 'lib/expressir/model/repository.rb', line 121

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



135
136
137
138
# File 'lib/expressir/model/repository.rb', line 135

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



213
214
215
216
217
218
219
220
221
222
# File 'lib/expressir/model/repository.rb', line 213

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:



53
54
55
56
# File 'lib/expressir/model/repository.rb', line 53

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



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/expressir/model/repository.rb', line 181

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



227
228
229
230
231
# File 'lib/expressir/model/repository.rb', line 227

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



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

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:



272
273
274
275
276
277
278
279
280
281
# File 'lib/expressir/model/repository.rb', line 272

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



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

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