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

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

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

Constructor Details

#initialize(*args, base_dir: nil, **kwargs) ⇒ Repository

Returns a new instance of Repository.



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

def initialize(*args, base_dir: nil, **kwargs)
  super(*args, **kwargs)
  @base_dir = base_dir
  @entity_index = nil
  @type_index = nil
  @reference_index = nil
end

Instance Attribute Details

#base_dirObject

Base directory for schema files



17
18
19
# File 'lib/expressir/model/repository.rb', line 17

def base_dir
  @base_dir
end

#entity_indexObject (readonly)

Index instances (lazy-loaded)



20
21
22
# File 'lib/expressir/model/repository.rb', line 20

def entity_index
  @entity_index
end

#reference_indexObject (readonly)

Index instances (lazy-loaded)



20
21
22
# File 'lib/expressir/model/repository.rb', line 20

def reference_index
  @reference_index
end

#type_indexObject (readonly)

Index instances (lazy-loaded)



20
21
22
# File 'lib/expressir/model/repository.rb', line 20

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



248
249
250
251
252
253
254
255
256
257
258
# File 'lib/expressir/model/repository.rb', line 248

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)
    parsed&.schemas&.each { |schema| repo.schemas << schema }
  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:



263
264
265
266
# File 'lib/expressir/model/repository.rb', line 263

def self.from_package(package_path)
  require_relative "../package/reader"
  Package::Reader.load(package_path)
end

Instance Method Details

#build_indexesvoid

This method returns an undefined value.

Build indexes for entities, types, and references



42
43
44
45
46
# File 'lib/expressir/model/repository.rb', line 42

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

#childrenArray<Declaration>

Returns:

  • (Array<Declaration>)


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

def children
  schemas
end

#dependency_statisticsHash

Get dependency statistics

Returns:

  • (Hash)

    Statistics about interface dependencies



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/expressir/model/repository.rb', line 196

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)

  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



278
279
280
281
282
# File 'lib/expressir/model/repository.rb', line 278

def export_to_package(output_path, options = {})
  require_relative "../package/builder"
  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:



51
52
53
54
# File 'lib/expressir/model/repository.rb', line 51

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:



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

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



161
162
163
164
165
166
167
168
# File 'lib/expressir/model/repository.rb', line 161

def largest_schemas(limit = 10)
  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



68
69
70
71
72
73
74
75
76
# File 'lib/expressir/model/repository.rb', line 68

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



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

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



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

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



174
175
176
177
178
179
180
181
182
183
# File 'lib/expressir/model/repository.rb', line 174

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

#schemas_by_categoryHash

Group schemas by category based on their contents

Returns:

  • (Hash)

    Hash with category keys and schema arrays



143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/expressir/model/repository.rb', line 143

def schemas_by_category
  {
    with_entities: schemas.select { |s| s.entities&.any? },
    with_types: schemas.select { |s| s.types&.any? },
    with_functions: schemas.select { |s| s.functions&.any? },
    with_rules: schemas.select { |s| s.rules&.any? },
    interface_only: schemas.select do |s|
      s.interfaces&.any? && !s.entities&.any? && !s.types&.any?
    end,
    empty: 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



188
189
190
191
192
# File 'lib/expressir/model/repository.rb', line 188

def schemas_by_complexity(limit = 10)
  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



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/expressir/model/repository.rb', line 114

def statistics(format: :hash)
  ensure_indexes_built

  stats = {
    total_schemas: 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:



233
234
235
236
237
238
239
240
241
242
# File 'lib/expressir/model/repository.rb', line 233

def to_manifest
  manifest = Expressir::SchemaManifest.new
  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



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

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