expressir 2.1.29 → 2.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +98 -0
  3. data/.github/workflows/links.yml +100 -0
  4. data/.github/workflows/rake.yml +4 -0
  5. data/.github/workflows/release.yml +5 -0
  6. data/.github/workflows/validate_schemas.yml +1 -1
  7. data/.gitignore +3 -0
  8. data/.rubocop.yml +1 -1
  9. data/.rubocop_todo.yml +209 -55
  10. data/Gemfile +2 -1
  11. data/README.adoc +650 -83
  12. data/docs/Gemfile +12 -0
  13. data/docs/_config.yml +141 -0
  14. data/docs/_guides/changes/changes-format.adoc +778 -0
  15. data/docs/_guides/changes/importing-eengine.adoc +898 -0
  16. data/docs/_guides/changes/index.adoc +396 -0
  17. data/docs/_guides/changes/programmatic-usage.adoc +1038 -0
  18. data/docs/_guides/changes/validating-changes.adoc +681 -0
  19. data/docs/_guides/cli/benchmark-performance.adoc +834 -0
  20. data/docs/_guides/cli/coverage-analysis.adoc +921 -0
  21. data/docs/_guides/cli/format-schemas.adoc +547 -0
  22. data/docs/_guides/cli/index.adoc +8 -0
  23. data/docs/_guides/cli/managing-changes.adoc +927 -0
  24. data/docs/_guides/cli/validate-ascii.adoc +645 -0
  25. data/docs/_guides/cli/validate-schemas.adoc +534 -0
  26. data/docs/_guides/index.adoc +165 -0
  27. data/docs/_guides/ler/creating-packages.adoc +664 -0
  28. data/docs/_guides/ler/index.adoc +305 -0
  29. data/docs/_guides/ler/loading-packages.adoc +707 -0
  30. data/docs/_guides/ler/package-formats.adoc +748 -0
  31. data/docs/_guides/ler/querying-packages.adoc +826 -0
  32. data/docs/_guides/ler/validating-packages.adoc +750 -0
  33. data/docs/_guides/liquid/basic-templates.adoc +813 -0
  34. data/docs/_guides/liquid/documentation-generation.adoc +1042 -0
  35. data/docs/_guides/liquid/drops-reference.adoc +829 -0
  36. data/docs/_guides/liquid/filters-and-tags.adoc +912 -0
  37. data/docs/_guides/liquid/index.adoc +468 -0
  38. data/docs/_guides/manifests/creating-manifests.adoc +483 -0
  39. data/docs/_guides/manifests/index.adoc +307 -0
  40. data/docs/_guides/manifests/resolving-manifests.adoc +557 -0
  41. data/docs/_guides/manifests/validating-manifests.adoc +713 -0
  42. data/docs/_guides/ruby-api/formatting-schemas.adoc +605 -0
  43. data/docs/_guides/ruby-api/index.adoc +257 -0
  44. data/docs/_guides/ruby-api/parsing-files.adoc +421 -0
  45. data/docs/_guides/ruby-api/search-engine.adoc +609 -0
  46. data/docs/_guides/ruby-api/working-with-repository.adoc +577 -0
  47. data/docs/_pages/data-model.adoc +665 -0
  48. data/docs/_pages/express-language.adoc +506 -0
  49. data/docs/_pages/getting-started.adoc +414 -0
  50. data/docs/_pages/index.adoc +116 -0
  51. data/docs/_pages/introduction.adoc +256 -0
  52. data/docs/_pages/ler-packages.adoc +837 -0
  53. data/docs/_pages/parsers.adoc +683 -0
  54. data/docs/_pages/schema-manifests.adoc +431 -0
  55. data/docs/_references/index.adoc +228 -0
  56. data/docs/_tutorials/creating-ler-package.adoc +735 -0
  57. data/docs/_tutorials/documentation-coverage.adoc +795 -0
  58. data/docs/_tutorials/index.adoc +221 -0
  59. data/docs/_tutorials/liquid-templates.adoc +806 -0
  60. data/docs/_tutorials/parsing-your-first-schema.adoc +522 -0
  61. data/docs/_tutorials/querying-schemas.adoc +751 -0
  62. data/docs/_tutorials/working-with-multiple-schemas.adoc +676 -0
  63. data/docs/index.adoc +242 -0
  64. data/docs/lychee.toml +84 -0
  65. data/examples/demo_ler_usage.sh +86 -0
  66. data/examples/ler/README.md +111 -0
  67. data/examples/ler/simple_example.ler +0 -0
  68. data/examples/ler/simple_schema.exp +33 -0
  69. data/examples/ler_build.rb +75 -0
  70. data/examples/ler_cli.rb +79 -0
  71. data/examples/ler_demo_complete.rb +276 -0
  72. data/examples/ler_query.rb +91 -0
  73. data/examples/ler_query_examples.rb +305 -0
  74. data/examples/ler_stats.rb +81 -0
  75. data/examples/phase3_demo.rb +159 -0
  76. data/examples/query_demo_simple.rb +131 -0
  77. data/expressir.gemspec +2 -0
  78. data/lib/expressir/changes/schema_change.rb +32 -22
  79. data/lib/expressir/changes/{edition_change.rb → version_change.rb} +3 -3
  80. data/lib/expressir/cli.rb +12 -4
  81. data/lib/expressir/commands/changes_import_eengine.rb +2 -2
  82. data/lib/expressir/commands/changes_validate.rb +1 -1
  83. data/lib/expressir/commands/manifest.rb +427 -0
  84. data/lib/expressir/commands/package.rb +1274 -0
  85. data/lib/expressir/commands/validate.rb +70 -37
  86. data/lib/expressir/commands/validate_ascii.rb +607 -0
  87. data/lib/expressir/commands/validate_load.rb +88 -0
  88. data/lib/expressir/express/formatter.rb +5 -1
  89. data/lib/expressir/express/formatters/remark_item_formatter.rb +25 -0
  90. data/lib/expressir/express/parser.rb +33 -0
  91. data/lib/expressir/manifest/resolver.rb +213 -0
  92. data/lib/expressir/manifest/validator.rb +195 -0
  93. data/lib/expressir/model/declarations/entity.rb +6 -0
  94. data/lib/expressir/model/dependency_resolver.rb +270 -0
  95. data/lib/expressir/model/indexes/entity_index.rb +103 -0
  96. data/lib/expressir/model/indexes/reference_index.rb +148 -0
  97. data/lib/expressir/model/indexes/type_index.rb +149 -0
  98. data/lib/expressir/model/interface_validator.rb +384 -0
  99. data/lib/expressir/model/repository.rb +400 -5
  100. data/lib/expressir/model/repository_validator.rb +295 -0
  101. data/lib/expressir/model/search_engine.rb +525 -0
  102. data/lib/expressir/model.rb +4 -94
  103. data/lib/expressir/package/builder.rb +200 -0
  104. data/lib/expressir/package/metadata.rb +81 -0
  105. data/lib/expressir/package/reader.rb +165 -0
  106. data/lib/expressir/schema_manifest.rb +11 -1
  107. data/lib/expressir/version.rb +1 -1
  108. data/lib/expressir.rb +16 -3
  109. metadata +115 -5
  110. data/docs/benchmarking.adoc +0 -107
  111. data/docs/liquid_drops.adoc +0 -1547
@@ -0,0 +1,665 @@
1
+ ---
2
+ title: Data Model
3
+ nav_order: 5
4
+ ---
5
+
6
+ == Expressir Data Model
7
+
8
+ === Purpose
9
+
10
+ This page explains Expressir's Ruby data model that represents EXPRESS schemas. Understanding this model is essential for programmatic manipulation of EXPRESS data, writing custom tools, and generating documentation.
11
+
12
+ === References
13
+
14
+ * link:express-language.html[EXPRESS Language] - Understanding EXPRESS concepts
15
+ * link:parsers.html[Parsers] - How EXPRESS becomes the data model
16
+ * link:../guides/ruby-api/model-traversal.html[Guide: Model Traversal] - Navigating the model
17
+ * link:../references/data-model/[Data Model Reference] - Complete API documentation
18
+
19
+ === Concepts
20
+
21
+ Model Element:: Base class for all EXPRESS constructs in Expressir's object model
22
+ Repository:: Top-level container holding multiple schemas
23
+ Declaration:: Named definition (entity, type, function, etc.) within a schema
24
+ Data Type:: Type specification (primitives, aggregates, constructed types)
25
+ Expression:: Algorithmic calculation or value reference
26
+ Statement:: Procedural operation (assignment, if/then, loop, etc.)
27
+ Reference:: Link to another model element by name
28
+
29
+ === Model Hierarchy
30
+
31
+ Expressir's data model follows an object-oriented design with clear class hierarchies:
32
+
33
+ [source]
34
+ ----
35
+ ModelElement (base class)
36
+ ├── Repository
37
+ │ └── Schema
38
+ │ ├── Entity
39
+ │ ├── Type
40
+ │ ├── Function
41
+ │ ├── Procedure
42
+ │ ├── Rule
43
+ │ ├── Constant
44
+ │ ├── SubtypeConstraint
45
+ │ └── Interface
46
+ ├── DataTypes
47
+ │ ├── Primitives (String, Integer, Real, Boolean, Logical, Binary, Number)
48
+ │ ├── Aggregates (Array, List, Set, Bag)
49
+ │ ├── Enumeration
50
+ │ ├── Select
51
+ │ ├── Generic
52
+ │ └── GenericEntity
53
+ ├── Declarations
54
+ │ ├── Attribute
55
+ │ ├── DerivedAttribute
56
+ │ ├── InverseAttribute
57
+ │ ├── Parameter
58
+ │ ├── Variable
59
+ │ ├── UniqueRule
60
+ │ ├── WhereRule
61
+ │ └── ...
62
+ ├── Expressions
63
+ │ ├── BinaryExpression
64
+ │ ├── UnaryExpression
65
+ │ ├── FunctionCall
66
+ │ ├── QueryExpression
67
+ │ ├── AggregateInitializer
68
+ │ └── ...
69
+ ├── Statements
70
+ │ ├── Assignment
71
+ │ ├── If
72
+ │ ├── Repeat
73
+ │ ├── Case
74
+ │ ├── Return
75
+ │ └── ...
76
+ ├── References
77
+ │ ├── SimpleReference
78
+ │ ├── AttributeReference
79
+ │ ├── GroupReference
80
+ │ └── IndexReference
81
+ └── Literals
82
+ ├── String
83
+ ├── Integer
84
+ ├── Real
85
+ ├── Logical
86
+ └── Binary
87
+ ----
88
+
89
+ === Key Classes
90
+
91
+ ==== ModelElement
92
+
93
+ The base class for all EXPRESS constructs:
94
+
95
+ [source,ruby]
96
+ ----
97
+ module Expressir::Model
98
+ class ModelElement
99
+ attr_accessor :parent # Parent element in tree
100
+ attribute :source, :string # Original EXPRESS text
101
+ attribute :untagged_remarks, :string, collection: true
102
+
103
+ # Navigate to parent and ancestors
104
+ def path # Full qualified path (e.g., "schema.entity.attribute")
105
+ def find(path) # Find child by path
106
+ def children # Direct children
107
+ def children_by_id # Children indexed by ID
108
+ end
109
+ end
110
+ ----
111
+
112
+ **Key features**:
113
+
114
+ * **Parent linkage**: Every element knows its parent
115
+ * **Path navigation**: Full qualified paths for reference resolution
116
+ * **Child access**: Easy traversal of the tree structure
117
+ * **Source tracking**: Original EXPRESS text available
118
+
119
+ ==== Repository
120
+
121
+ Top-level container for schemas:
122
+
123
+ [source,ruby]
124
+ ----
125
+ repository = Expressir::Model::Repository.new
126
+ repository.schemas << schema1
127
+ repository.schemas << schema2
128
+
129
+ # Access schemas
130
+ repository.schemas.each { |schema| puts schema.id }
131
+
132
+ # Find elements across schemas
133
+ entity = repository.find_entity(qualified_name: "action_schema.action")
134
+ type = repository.find_type(qualified_name: "geometry_schema.point")
135
+
136
+ # Statistics
137
+ stats = repository.statistics
138
+ # => { total_schemas: 10, total_entities: 150, ... }
139
+
140
+ # Build indexes for fast lookup
141
+ repository.build_indexes
142
+ ----
143
+
144
+ **Capabilities**:
145
+
146
+ * **Multi-schema management**: Holds multiple schemas
147
+ * **Global search**: Find entities/types across all schemas
148
+ * **Statistics**: Aggregate counts and metrics
149
+ * **Indexing**: Fast lookup with automatic index building
150
+ * **Validation**: Schema consistency checking
151
+
152
+ ==== Schema
153
+
154
+ A named collection of EXPRESS declarations:
155
+
156
+ [source,ruby]
157
+ ----
158
+ schema = Expressir::Model::Declarations::Schema.new(id: "example_schema")
159
+
160
+ # Access declarations
161
+ schema.entities # Array of Entity objects
162
+ schema.types # Array of Type objects
163
+ schema.functions # Array of Function objects
164
+ schema.procedures # Array of Procedure objects
165
+ schema.rules # Array of Rule objects
166
+ schema.constants # Array of Constant objects
167
+ schema.interfaces # Array of Interface objects
168
+
169
+ # Metadata
170
+ schema.file # Source file path
171
+ schema.version # Schema version info
172
+ schema.remarks # Documentation comments
173
+
174
+ # Find child element
175
+ entity = schema.find("person")
176
+ ----
177
+
178
+ **Key attributes**:
179
+
180
+ * **id**: Schema name
181
+ * **file**: Source file path
182
+ * **version**: SchemaVersion object
183
+ * **interfaces**: USE FROM / REFERENCE FROM declarations
184
+ * **entities, types, functions, etc.**: Collections of declarations
185
+
186
+ ==== Entity
187
+
188
+ Represents an EXPRESS ENTITY:
189
+
190
+ [source,ruby]
191
+ ----
192
+ entity = Expressir::Model::Declarations::Entity.new(
193
+ id: "person",
194
+ abstract: false
195
+ )
196
+
197
+ # Attributes
198
+ entity.attributes # Explicit attributes
199
+ entity.derived_attributes # DERIVE attributes
200
+ entity.inverse_attributes # INVERSE attributes
201
+
202
+ # Inheritance
203
+ entity.subtype_of # Array of supertypes
204
+ entity.supertype_expression # Supertype expression
205
+
206
+ # Constraints
207
+ entity.unique_rules # UNIQUE rules
208
+ entity.where_rules # WHERE rules
209
+ entity.informal_propositions # Informal propositions
210
+
211
+ # Navigation
212
+ entity.children # All child elements
213
+ entity.parent # Parent schema
214
+ ----
215
+
216
+ **Key features**:
217
+
218
+ * **Inheritance support**: Subtypes and supertypes
219
+ * **Three attribute types**: Explicit, derived, inverse
220
+ * **Constraint modeling**: WHERE and UNIQUE rules
221
+ * **Documentation**: Remarks and informal propositions
222
+
223
+ ==== Type
224
+
225
+ Represents an EXPRESS TYPE:
226
+
227
+ [source,ruby]
228
+ ----
229
+ type = Expressir::Model::Declarations::Type.new(id: "length_measure")
230
+
231
+ # Underlying type definition
232
+ type.underlying_type # DataType object (Select, Enumeration, etc.)
233
+
234
+ # Constraints
235
+ type.where_rules # WHERE constraints
236
+
237
+ # For enumeration types
238
+ if type.underlying_type.is_a?(Expressir::Model::DataTypes::Enumeration)
239
+ type.enumeration_items # Array of EnumerationItem objects
240
+ end
241
+ ----
242
+
243
+ **Type categories**:
244
+
245
+ * **Simple types**: Constrained primitives
246
+ * **Enumeration**: Named values
247
+ * **Select**: Union types
248
+ * **Aggregate**: Arrays, lists, sets, bags
249
+ * **Generic**: Generic and GenericEntity
250
+
251
+ ==== Attribute
252
+
253
+ Entity attributes come in three varieties:
254
+
255
+ **Explicit attributes** (stored data):
256
+
257
+ [source,ruby]
258
+ ----
259
+ attr = Expressir::Model::Declarations::Attribute.new(
260
+ id: "name",
261
+ optional: false
262
+ )
263
+ attr.type = String_type # Type reference
264
+ ----
265
+
266
+ **Derived attributes** (computed):
267
+
268
+ [source,ruby]
269
+ ----
270
+ derived = Expressir::Model::Declarations::DerivedAttribute.new(
271
+ id: "full_name"
272
+ )
273
+ derived.type = String_type
274
+ derived.expression = ... # Calculation expression
275
+ ----
276
+
277
+ **Inverse attributes** (relationships):
278
+
279
+ [source,ruby]
280
+ ----
281
+ inverse = Expressir::Model::Declarations::InverseAttribute.new(
282
+ id: "employees"
283
+ )
284
+ inverse.type = Set_type
285
+ inverse.attribute = "works_for" # Target attribute
286
+ ----
287
+
288
+ === Relationships
289
+
290
+ ==== Parent-Child Relationships
291
+
292
+ Every element maintains a link to its parent:
293
+
294
+ [source,ruby]
295
+ ----
296
+ # Navigate up
297
+ attribute.parent # => Entity
298
+ entity.parent # => Schema
299
+ schema.parent # => Repository
300
+
301
+ # Navigate down
302
+ schema.entities.first # => Entity
303
+ entity.attributes.first # => Attribute
304
+ ----
305
+
306
+ ==== Reference Resolution
307
+
308
+ References are resolved to their target elements:
309
+
310
+ [source,ruby]
311
+ ----
312
+ # Before resolution: SimpleReference with id
313
+ attribute.type # => SimpleReference(id: "length_measure")
314
+
315
+ # After resolution: Points to actual Type
316
+ repository.resolve_all_references
317
+ attribute.type.ref # => Type(id: "length_measure")
318
+ ----
319
+
320
+ ==== Interface Relationships
321
+
322
+ Schemas can import from other schemas:
323
+
324
+ [source,ruby]
325
+ ----
326
+ interface = Expressir::Model::Declarations::Interface.new(
327
+ kind: Interface::USE, # or Interface::REFERENCE
328
+ schema: schema_ref
329
+ )
330
+
331
+ # Items can be selectively imported
332
+ interface.items = [item1, item2] # or empty for all
333
+ ----
334
+
335
+ === Working Programmatically
336
+
337
+ ==== Creating Models from Code
338
+
339
+ You can construct models programmatically:
340
+
341
+ [source,ruby]
342
+ ----
343
+ # Create repository
344
+ repo = Expressir::Model::Repository.new
345
+
346
+ # Create schema
347
+ schema = Expressir::Model::Declarations::Schema.new(id: "my_schema")
348
+ repo.schemas << schema
349
+
350
+ # Create entity
351
+ entity = Expressir::Model::Declarations::Entity.new(id: "my_entity")
352
+ schema.entities << entity
353
+
354
+ # Create attribute
355
+ attr = Expressir::Model::Declarations::Attribute.new(
356
+ id: "my_attribute",
357
+ optional: false
358
+ )
359
+ entity.attributes << attr
360
+
361
+ # Set attribute type
362
+ string_ref = Expressir::Model::References::SimpleReference.new(id: "STRING")
363
+ attr.type = string_ref
364
+ ----
365
+
366
+ ==== Parsing EXPRESS to Model
367
+
368
+ Most commonly, you parse EXPRESS files:
369
+
370
+ [source,ruby]
371
+ ----
372
+ # Parse file to repository
373
+ repo = Expressir::Express::Parser.from_file("schema.exp")
374
+
375
+ # Access elements
376
+ schema = repo.schemas.first
377
+ entity = schema.entities.find { |e| e.id == "person" }
378
+ attribute = entity.attributes.find { |a| a.id == "name" }
379
+
380
+ # Check type
381
+ if attribute.type.is_a?(Expressir::Model::References::SimpleReference)
382
+ puts "Type name: #{attribute.type.id}"
383
+ end
384
+ ----
385
+
386
+ ==== Model Traversal
387
+
388
+ Navigate the model tree systematically:
389
+
390
+ [source,ruby]
391
+ ----
392
+ # Visit all entities in repository
393
+ repo.schemas.each do |schema|
394
+ schema.entities.each do |entity|
395
+ puts "Entity: #{entity.path}"
396
+
397
+ entity.attributes.each do |attr|
398
+ puts " Attribute: #{attr.id}"
399
+ end
400
+ end
401
+ end
402
+
403
+ # Find specific element by path
404
+ element = repo.find("action_schema.action.id")
405
+
406
+ # Search with pattern
407
+ entities = repo.schemas.flat_map(&:entities)
408
+ person_entities = entities.select { |e| e.id.include?("person") }
409
+ ----
410
+
411
+ ==== Visitor Pattern
412
+
413
+ For complex traversals, use the visitor pattern:
414
+
415
+ [source,ruby]
416
+ ----
417
+ class MyVisitor < Expressir::Express::Visitor
418
+ def visit_entity(entity)
419
+ puts "Visiting entity: #{entity.id}"
420
+ super # Visit children
421
+ end
422
+
423
+ def visit_attribute(attribute)
424
+ puts " Attribute: #{attribute.id}"
425
+ end
426
+ end
427
+
428
+ visitor = MyVisitor.new
429
+ visitor.visit(repository)
430
+ ----
431
+
432
+ === Accessing Elements
433
+
434
+ ==== By Direct Navigation
435
+
436
+ [source,ruby]
437
+ ----
438
+ # Access through collections
439
+ schema = repo.schemas.first
440
+ entity = schema.entities.first
441
+ attribute = entity.attributes.first
442
+ ----
443
+
444
+ ==== By ID Lookup
445
+
446
+ [source,ruby]
447
+ ----
448
+ # Find by ID in collection
449
+ entity = schema.entities.find { |e| e.id == "person" }
450
+
451
+ # Use children_by_id for faster lookup
452
+ entity = schema.children_by_id["person"]
453
+ ----
454
+
455
+ ==== By Path
456
+
457
+ [source,ruby]
458
+ ----
459
+ # Full qualified path
460
+ element = repo.find("action_schema.action.id")
461
+
462
+ # Relative path from schema
463
+ attribute = schema.find("person.name")
464
+ ----
465
+
466
+ ==== By Type
467
+
468
+ [source,ruby]
469
+ ----
470
+ # Filter by class
471
+ entities = schema.children.select { |c| c.is_a?(Expressir::Model::Declarations::Entity) }
472
+
473
+ # Type-specific collections
474
+ schema.entities # Only entities
475
+ schema.types # Only types
476
+ ----
477
+
478
+ === Model Modification
479
+
480
+ ==== Adding Elements
481
+
482
+ [source,ruby]
483
+ ----
484
+ # Add to collections
485
+ schema.entities << new_entity
486
+ entity.attributes << new_attribute
487
+
488
+ # Parent is automatically set
489
+ new_entity.parent # => schema
490
+ ----
491
+
492
+ ==== Removing Elements
493
+
494
+ [source,ruby]
495
+ ----
496
+ # Remove from collection
497
+ schema.entities.delete(entity)
498
+
499
+ # Clear parent link
500
+ entity.parent = nil
501
+ ----
502
+
503
+ ==== Modifying Elements
504
+
505
+ [source,ruby]
506
+ ----
507
+ # Change properties
508
+ entity.id = "new_name"
509
+ entity.abstract = true
510
+
511
+ # Modify collections
512
+ entity.attributes.clear
513
+ entity.attributes = new_attributes
514
+ ----
515
+
516
+ === Converting to EXPRESS
517
+
518
+ Convert model back to EXPRESS text:
519
+
520
+ [source,ruby]
521
+ ----
522
+ # Format entire repository
523
+ express = Expressir::Express::Formatter.format(repo)
524
+
525
+ # Format single schema
526
+ express = Expressir::Express::Formatter.format(schema)
527
+
528
+ # Format with options
529
+ formatter = Expressir::Express::Formatter.new(no_remarks: true)
530
+ express = formatter.format(schema)
531
+ ----
532
+
533
+ === Converting to Liquid
534
+
535
+ For template-based generation:
536
+
537
+ [source,ruby]
538
+ ----
539
+ # Convert to Liquid drops
540
+ repo_drop = repo.to_liquid
541
+ schema_drop = schema.to_liquid
542
+
543
+ # Use in Liquid templates
544
+ template = Liquid::Template.parse("{{ schema.id }}")
545
+ output = template.render('schema' => schema_drop)
546
+ ----
547
+
548
+ See link:../guides/liquid/[Liquid guides] for details.
549
+
550
+ === Performance Considerations
551
+
552
+ ==== Indexing
553
+
554
+ Build indexes for large repositories:
555
+
556
+ [source,ruby]
557
+ ----
558
+ # Build all indexes
559
+ repo.build_indexes
560
+
561
+ # Fast lookups now available
562
+ entity = repo.find_entity(qualified_name: "schema.entity")
563
+ type = repo.find_type(qualified_name: "schema.type")
564
+ ----
565
+
566
+ ==== Lazy Loading
567
+
568
+ Collections are loaded on demand:
569
+
570
+ [source,ruby]
571
+ ----
572
+ # No parsing until accessed
573
+ schema.entities # Triggers collection loading
574
+ ----
575
+
576
+ ==== Memory Management
577
+
578
+ For very large schemas:
579
+
580
+ [source,ruby]
581
+ ----
582
+ # Process one schema at a time
583
+ schemas.each do |schema_file|
584
+ repo = Expressir::Express::Parser.from_file(schema_file)
585
+ process(repo)
586
+ repo = nil # Allow garbage collection
587
+ end
588
+ ----
589
+
590
+ === Best Practices
591
+
592
+ **Navigate using relationships**::
593
+ Use parent/child links rather than searching
594
+
595
+ **Use typed collections**::
596
+ Access `schema.entities` rather than filtering `schema.children`
597
+
598
+ **Build indexes once**::
599
+ For repositories with many schemas, build indexes early
600
+
601
+ **Check types carefully**::
602
+ Types can be references, resolved references, or inline definitions
603
+
604
+ **Preserve parent links**::
605
+ When moving elements, update parent correctly
606
+
607
+ **Use visitor pattern**:
608
+ For complex tree traversals, implement a visitor
609
+
610
+ === Common Patterns
611
+
612
+ ==== Find All Entities Inheriting From Base
613
+
614
+ [source,ruby]
615
+ ----
616
+ def find_subtypes(repo, base_entity_name)
617
+ repo.schemas.flat_map(&:entities).select do |entity|
618
+ entity.subtype_of&.any? { |st| st.ref&.id == base_entity_name }
619
+ end
620
+ end
621
+ ----
622
+
623
+ ==== List All SELECT Types
624
+
625
+ [source,ruby]
626
+ ----
627
+ select_types = repo.schemas.flat_map(&:types).select do |type|
628
+ type.underlying_type.is_a?(Expressir::Model::DataTypes::Select)
629
+ end
630
+ ----
631
+
632
+ ==== Find Undefined References
633
+
634
+ [source,ruby]
635
+ ----
636
+ undefined = []
637
+ visitor = Expressir::Express::Visitor.new
638
+ visitor.define_singleton_method(:visit_simple_reference) do |ref|
639
+ undefined << ref if ref.ref.nil?
640
+ end
641
+ visitor.visit(repo)
642
+ ----
643
+
644
+ === Next Steps
645
+
646
+ Now that you understand the data model:
647
+
648
+ **Try it out**::
649
+ link:../tutorials/parsing-your-first-schema.html[Parse a schema] and explore the model
650
+
651
+ **Learn traversal**::
652
+ Read the link:../guides/ruby-api/model-traversal.html[model traversal guide]
653
+
654
+ **Study references**::
655
+ Review the complete link:../references/data-model/[data model reference]
656
+
657
+ **Build tools**::
658
+ Create custom analyzers or converters using the model
659
+
660
+ === Bibliography
661
+
662
+ * link:../references/data-model/[Data Model Reference] - Complete API documentation
663
+ * link:parsers.html[Parsers] - How the model is created
664
+ * https://github.com/lutaml/expressir/tree/main/lib/expressir/model[Model Source Code] - Implementation details
665
+ * link:../guides/ruby-api/[Ruby API Guides] - Working with the model programmatically