expressir 2.1.30 → 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 (107) 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 +244 -39
  10. data/Gemfile +2 -1
  11. data/README.adoc +621 -54
  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/cli.rb +12 -4
  79. data/lib/expressir/commands/manifest.rb +427 -0
  80. data/lib/expressir/commands/package.rb +1274 -0
  81. data/lib/expressir/commands/validate.rb +70 -37
  82. data/lib/expressir/commands/validate_ascii.rb +607 -0
  83. data/lib/expressir/commands/validate_load.rb +88 -0
  84. data/lib/expressir/express/formatter.rb +5 -1
  85. data/lib/expressir/express/formatters/remark_item_formatter.rb +25 -0
  86. data/lib/expressir/express/parser.rb +33 -0
  87. data/lib/expressir/manifest/resolver.rb +213 -0
  88. data/lib/expressir/manifest/validator.rb +195 -0
  89. data/lib/expressir/model/declarations/entity.rb +6 -0
  90. data/lib/expressir/model/dependency_resolver.rb +270 -0
  91. data/lib/expressir/model/indexes/entity_index.rb +103 -0
  92. data/lib/expressir/model/indexes/reference_index.rb +148 -0
  93. data/lib/expressir/model/indexes/type_index.rb +149 -0
  94. data/lib/expressir/model/interface_validator.rb +384 -0
  95. data/lib/expressir/model/repository.rb +400 -5
  96. data/lib/expressir/model/repository_validator.rb +295 -0
  97. data/lib/expressir/model/search_engine.rb +525 -0
  98. data/lib/expressir/model.rb +4 -94
  99. data/lib/expressir/package/builder.rb +200 -0
  100. data/lib/expressir/package/metadata.rb +81 -0
  101. data/lib/expressir/package/reader.rb +165 -0
  102. data/lib/expressir/schema_manifest.rb +11 -1
  103. data/lib/expressir/version.rb +1 -1
  104. data/lib/expressir.rb +15 -2
  105. metadata +114 -4
  106. data/docs/benchmarking.adoc +0 -107
  107. data/docs/liquid_drops.adoc +0 -1547
@@ -0,0 +1,676 @@
1
+ ---
2
+ title: Working with Multiple Schemas
3
+ nav_order: 3
4
+ ---
5
+
6
+ == Working with Multiple Schemas
7
+
8
+ === Prerequisites
9
+
10
+ Before starting this tutorial, ensure you have:
11
+
12
+ * Completed link:parsing-your-first-schema.html[Parsing Your First Schema]
13
+ * Understanding of EXPRESS interfaces (USE FROM, REFERENCE FROM)
14
+ * Basic knowledge of schema dependencies
15
+ * Expressir installed and working
16
+
17
+ === Learning Objectives
18
+
19
+ By the end of this tutorial, you will be able to:
20
+
21
+ * Parse multiple EXPRESS schema files together
22
+ * Understand and work with schema dependencies
23
+ * Use interfaces to share entities and types
24
+ * Resolve cross-schema references
25
+ * Manage schema collections effectively
26
+ * Handle circular dependencies
27
+
28
+ === What You'll Build
29
+
30
+ You'll create a multi-schema EXPRESS application modeling a product catalog system with base definitions, product schemas, and an application schema that uses them all.
31
+
32
+ === Step 1: Understanding Schema Dependencies
33
+
34
+ EXPRESS schemas often depend on each other through interfaces.
35
+
36
+ ==== Interface Types
37
+
38
+ **USE FROM**::
39
+ Imports all declarations from another schema
40
+ +
41
+ [source,express]
42
+ ----
43
+ USE FROM base_schema;
44
+ ----
45
+
46
+ **REFERENCE FROM**::
47
+ Imports specific declarations from another schema
48
+ +
49
+ [source,express]
50
+ ----
51
+ REFERENCE FROM base_schema (person, organization);
52
+ ----
53
+
54
+ ==== Why Multiple Schemas?
55
+
56
+ * **Modularity**: Separate concerns into logical units
57
+ * **Reusability**: Share common definitions across projects
58
+ * **Maintainability**: Easier to update and test smaller schemas
59
+ * **Standards compliance**: ISO standards use modular schemas
60
+
61
+ === Step 2: Create Base Schemas
62
+
63
+ Let's create a foundation with reusable types.
64
+
65
+ ==== Create `base_types.exp`
66
+
67
+ [source,express]
68
+ ----
69
+ SCHEMA base_types;
70
+
71
+ TYPE identifier = STRING;
72
+ END_TYPE;
73
+
74
+ TYPE label = STRING;
75
+ END_TYPE;
76
+
77
+ TYPE text = STRING;
78
+ END_TYPE;
79
+
80
+ TYPE positive_integer = INTEGER;
81
+ WHERE
82
+ WR1: SELF > 0;
83
+ END_TYPE;
84
+
85
+ TYPE date_string = STRING;
86
+ END_TYPE;
87
+
88
+ END_SCHEMA;
89
+ ----
90
+
91
+ ==== Create `base_entities.exp`
92
+
93
+ [source,express]
94
+ ----
95
+ SCHEMA base_entities;
96
+
97
+ USE FROM base_types;
98
+
99
+ ENTITY person;
100
+ name : label;
101
+ email : OPTIONAL text;
102
+ END_ENTITY;
103
+
104
+ ENTITY organization;
105
+ org_name : label;
106
+ employees : SET [0:?] OF person;
107
+ END_ENTITY;
108
+
109
+ ENTITY address;
110
+ street : text;
111
+ city : label;
112
+ country : label;
113
+ END_ENTITY;
114
+
115
+ END_SCHEMA;
116
+ ----
117
+
118
+ === Step 3: Create Product Schema
119
+
120
+ Now create a schema that uses the base schemas.
121
+
122
+ ==== Create `product_schema.exp`
123
+
124
+ [source,express]
125
+ ----
126
+ SCHEMA product_schema;
127
+
128
+ REFERENCE FROM base_types (identifier, label, text, positive_integer);
129
+ REFERENCE FROM base_entities (person, organization);
130
+
131
+ ENTITY product;
132
+ id : identifier;
133
+ name : label;
134
+ description : OPTIONAL text;
135
+ price : REAL;
136
+ quantity : positive_integer;
137
+ manufacturer : organization;
138
+ END_ENTITY;
139
+
140
+ ENTITY product_category;
141
+ category_name : label;
142
+ products : SET [0:?] OF product;
143
+ END_ENTITY;
144
+
145
+ TYPE product_list = LIST [1:?] OF product;
146
+ END_TYPE;
147
+
148
+ END_SCHEMA;
149
+ ----
150
+
151
+ === Step 4: Create Application Schema
152
+
153
+ Finally, create an application schema that ties everything together.
154
+
155
+ ==== Create `catalog_application.exp`
156
+
157
+ [source,express]
158
+ ----
159
+ SCHEMA catalog_application;
160
+
161
+ USE FROM product_schema;
162
+ REFERENCE FROM base_entities (address);
163
+
164
+ ENTITY catalog;
165
+ catalog_name : label;
166
+ categories : LIST [1:?] OF product_category;
167
+ contact : person;
168
+ location : address;
169
+ END_ENTITY;
170
+
171
+ ENTITY order_item;
172
+ product_ref : product;
173
+ quantity : positive_integer;
174
+ END_ENTITY;
175
+
176
+ ENTITY customer_order;
177
+ order_id : identifier;
178
+ customer : person;
179
+ items : LIST [1:?] OF order_item;
180
+ total : REAL;
181
+ END_ENTITY;
182
+
183
+ END_SCHEMA;
184
+ ----
185
+
186
+ === Step 5: Parse Multiple Files with CLI
187
+
188
+ Use the CLI to parse all schemas together.
189
+
190
+ [source,bash]
191
+ ----
192
+ # Format all schemas
193
+ expressir format base_types.exp base_entities.exp product_schema.exp catalog_application.exp
194
+
195
+ # Validate all schemas
196
+ expressir validate base_types.exp base_entities.exp product_schema.exp catalog_application.exp
197
+ ----
198
+
199
+ **Expected output**:
200
+ [source]
201
+ ----
202
+ Validation passed for all EXPRESS schemas.
203
+ ----
204
+
205
+ === Step 6: Parse Multiple Files with Ruby API
206
+
207
+ Now let's parse programmatically.
208
+
209
+ ==== Basic Multi-File Parsing
210
+
211
+ Create `parse_multiple.rb`:
212
+
213
+ [source,ruby]
214
+ ----
215
+ require 'expressir'
216
+
217
+ # List all schema files in dependency order
218
+ files = [
219
+ 'base_types.exp',
220
+ 'base_entities.exp',
221
+ 'product_schema.exp',
222
+ 'catalog_application.exp'
223
+ ]
224
+
225
+ # Parse all files
226
+ repository = Expressir::Express::Parser.from_files(files)
227
+
228
+ # Display results
229
+ puts "Loaded #{repository.schemas.size} schemas:"
230
+ repository.schemas.each do |schema|
231
+ puts " - #{schema.id}"
232
+ puts " File: #{schema.file}"
233
+ puts " Entities: #{schema.entities.size}"
234
+ puts " Types: #{schema.types.size}"
235
+ end
236
+ ----
237
+
238
+ Run it:
239
+
240
+ [source,bash]
241
+ ----
242
+ ruby parse_multiple.rb
243
+ ----
244
+
245
+ **Expected output**:
246
+ [source]
247
+ ----
248
+ Loaded 4 schemas:
249
+ - base_types
250
+ File: base_types.exp
251
+ Entities: 0
252
+ Types: 5
253
+ - base_entities
254
+ File: base_entities.exp
255
+ Entities: 3
256
+ Types: 0
257
+ - product_schema
258
+ File: product_schema.exp
259
+ Entities: 2
260
+ Types: 1
261
+ - catalog_application
262
+ File: catalog_application.exp
263
+ Entities: 3
264
+ Types: 0
265
+ ----
266
+
267
+ ==== Progress Tracking
268
+
269
+ Add progress tracking:
270
+
271
+ [source,ruby]
272
+ ----
273
+ repository = Expressir::Express::Parser.from_files(files) do |filename, schemas, error|
274
+ if error
275
+ puts "❌ Error loading #{filename}:"
276
+ puts " #{error.message}"
277
+ else
278
+ puts "✓ Loaded #{schemas.length} schema(s) from #{filename}"
279
+ end
280
+ end
281
+
282
+ puts "\n📊 Total: #{repository.schemas.size} schemas loaded successfully"
283
+ ----
284
+
285
+ **Output**:
286
+ [source]
287
+ ----
288
+ ✓ Loaded 1 schema(s) from base_types.exp
289
+ ✓ Loaded 1 schema(s) from base_entities.exp
290
+ ✓ Loaded 1 schema(s) from product_schema.exp
291
+ ✓ Loaded 1 schema(s) from catalog_application.exp
292
+
293
+ 📊 Total: 4 schemas loaded successfully
294
+ ----
295
+
296
+ === Step 7: Explore Cross-Schema References
297
+
298
+ Now let's explore how references work across schemas.
299
+
300
+ ==== Inspect Interfaces
301
+
302
+ Create `inspect_interfaces.rb`:
303
+
304
+ [source,ruby]
305
+ ----
306
+ require 'expressir'
307
+
308
+ files = ['base_types.exp', 'base_entities.exp', 'product_schema.exp', 'catalog_application.exp']
309
+ repo = Expressir::Express::Parser.from_files(files)
310
+
311
+ repo.schemas.each do |schema|
312
+ next if schema.interfaces.empty?
313
+
314
+ puts "\n#{schema.id} interfaces:"
315
+ schema.interfaces.each do |interface|
316
+ puts " #{interface.kind.upcase}: #{interface.schema.ref&.id || interface.schema.id}"
317
+
318
+ if interface.items && !interface.items.empty?
319
+ interface.items.each do |item|
320
+ puts " - #{item.id}"
321
+ end
322
+ else
323
+ puts " (all declarations)"
324
+ end
325
+ end
326
+ end
327
+ ----
328
+
329
+ **Output**:
330
+ [source]
331
+ ----
332
+ base_entities interfaces:
333
+ USE: base_types
334
+ (all declarations)
335
+
336
+ product_schema interfaces:
337
+ REFERENCE: base_types
338
+ - identifier
339
+ - label
340
+ - text
341
+ - positive_integer
342
+ REFERENCE: base_entities
343
+ - person
344
+ - organization
345
+
346
+ catalog_application interfaces:
347
+ USE: product_schema
348
+ (all declarations)
349
+ REFERENCE: base_entities
350
+ - address
351
+ ----
352
+
353
+ ==== Trace Reference Resolution
354
+
355
+ Create `trace_references.rb`:
356
+
357
+ [source,ruby]
358
+ ----
359
+ require 'expressir'
360
+
361
+ files = ['base_types.exp', 'base_entities.exp', 'product_schema.exp', 'catalog_application.exp']
362
+ repo = Expressir::Express::Parser.from_files(files)
363
+
364
+ # Find product entity
365
+ product_schema = repo.schemas.find { |s| s.id == 'product_schema' }
366
+ product_entity = product_schema.entities.find { |e| e.id == 'product' }
367
+
368
+ puts "Product entity attributes:"
369
+ product_entity.attributes.each do |attr|
370
+ puts "\n #{attr.id}: #{attr.type}"
371
+
372
+ # Check if type is a reference
373
+ if attr.type.respond_to?(:ref) && attr.type.ref
374
+ ref = attr.type.ref
375
+ puts " Resolved to: #{ref.class.name}"
376
+ puts " Defined in: #{ref.parent.id}" if ref.respond_to?(:parent)
377
+ end
378
+ end
379
+ ----
380
+
381
+ **Output**:
382
+ [source]
383
+ ----
384
+ Product entity attributes:
385
+
386
+ id: identifier
387
+ Resolved to: Expressir::Model::Declarations::Type
388
+ Defined in: base_types
389
+
390
+ name: label
391
+ Resolved to: Expressir::Model::Declarations::Type
392
+ Defined in: base_types
393
+
394
+ description: text
395
+ Resolved to: Expressir::Model::Declarations::Type
396
+ Defined in: base_types
397
+
398
+ price: REAL
399
+
400
+ quantity: positive_integer
401
+ Resolved to: Expressir::Model::Declarations::Type
402
+ Defined in: base_types
403
+
404
+ manufacturer: organization
405
+ Resolved to: Expressir::Model::Declarations::Entity
406
+ Defined in: base_entities
407
+ ----
408
+
409
+ === Step 8: Handle Dependencies Automatically
410
+
411
+ Expressir can discover dependencies automatically.
412
+
413
+ ==== Using Schema Manifests
414
+
415
+ Create `schemas.yml`:
416
+
417
+ [source,yaml]
418
+ ----
419
+ schemas:
420
+ - path: base_types.exp
421
+ id: base_types
422
+ - path: base_entities.exp
423
+ id: base_entities
424
+ - path: product_schema.exp
425
+ id: product_schema
426
+ - path: catalog_application.exp
427
+ id: catalog_application
428
+ ----
429
+
430
+ ==== Load from Manifest
431
+
432
+ Create `load_manifest.rb`:
433
+
434
+ [source,ruby]
435
+ ----
436
+ require 'expressir'
437
+
438
+ # Load manifest
439
+ manifest = Expressir::SchemaManifest.from_file('schemas.yml')
440
+
441
+ # Get file paths
442
+ files = manifest.schemas.map(&:path)
443
+ puts "Loading #{files.size} schemas from manifest..."
444
+
445
+ # Parse all
446
+ repo = Expressir::Express::Parser.from_files(files)
447
+
448
+ puts "\nLoaded successfully:"
449
+ repo.schemas.each { |s| puts " - #{s.id}" }
450
+ ----
451
+
452
+ === Step 9: Validate Cross-Schema Consistency
453
+
454
+ Check that all references resolve correctly.
455
+
456
+ ==== Create Validation Script
457
+
458
+ Create `validate_references.rb`:
459
+
460
+ [source,ruby]
461
+ ----
462
+ require 'expressir'
463
+
464
+ files = ['base_types.exp', 'base_entities.exp', 'product_schema.exp', 'catalog_application.exp']
465
+ repo = Expressir::Express::Parser.from_files(files)
466
+
467
+ unresolved = []
468
+
469
+ repo.schemas.each do |schema|
470
+ schema.entities.each do |entity|
471
+ entity.attributes.each do |attr|
472
+ if attr.type.respond_to?(:ref) && attr.type.ref.nil?
473
+ unresolved << {
474
+ schema: schema.id,
475
+ entity: entity.id,
476
+ attribute: attr.id,
477
+ type: attr.type.id
478
+ }
479
+ end
480
+ end
481
+ end
482
+ end
483
+
484
+ if unresolved.empty?
485
+ puts "✓ All references resolved successfully!"
486
+ else
487
+ puts "❌ Found #{unresolved.size} unresolved references:"
488
+ unresolved.each do |item|
489
+ puts " #{item[:schema]}.#{item[:entity]}.#{item[:attribute]}: #{item[:type]}"
490
+ end
491
+ end
492
+ ----
493
+
494
+ === Step 10: Generate Dependency Graph
495
+
496
+ Visualize schema dependencies.
497
+
498
+ ==== Create Dependency Report
499
+
500
+ Create `dependency_graph.rb`:
501
+
502
+ [source,ruby]
503
+ ----
504
+ require 'expressir'
505
+
506
+ files = ['base_types.exp', 'base_entities.exp', 'product_schema.exp', 'catalog_application.exp']
507
+ repo = Expressir::Express::Parser.from_files(files)
508
+
509
+ puts "Schema Dependency Graph:"
510
+ puts "=" * 60
511
+
512
+ repo.schemas.each do |schema|
513
+ puts "\n#{schema.id}:"
514
+
515
+ if schema.interfaces.empty?
516
+ puts " (no dependencies)"
517
+ else
518
+ schema.interfaces.each do |interface|
519
+ target = interface.schema.ref&.id || interface.schema.id
520
+ kind = interface.kind == 'use' ? 'USES' : 'REFERENCES'
521
+ puts " #{kind} #{target}"
522
+
523
+ if interface.items && !interface.items.empty?
524
+ puts " Imports: #{interface.items.map(&:id).join(', ')}"
525
+ end
526
+ end
527
+ end
528
+ end
529
+ ----
530
+
531
+ **Output**:
532
+ [source]
533
+ ----
534
+ Schema Dependency Graph:
535
+ ============================================================
536
+
537
+ base_types:
538
+ (no dependencies)
539
+
540
+ base_entities:
541
+ USES base_types
542
+
543
+ product_schema:
544
+ REFERENCES base_types
545
+ Imports: identifier, label, text, positive_integer
546
+ REFERENCES base_entities
547
+ Imports: person, organization
548
+
549
+ catalog_application:
550
+ USES product_schema
551
+ REFERENCES base_entities
552
+ Imports: address
553
+ ----
554
+
555
+ === Step 11: Practice Exercises
556
+
557
+ ==== Exercise 1: Add New Schema
558
+
559
+ Create a `shipping_schema.exp` that:
560
+
561
+ * References `base_entities` for address
562
+ * References `product_schema` for product
563
+ * Defines shipment and delivery entities
564
+
565
+ Parse all schemas together and verify references resolve.
566
+
567
+ ==== Exercise 2: Circular Dependencies
568
+
569
+ Create two schemas that reference each other:
570
+
571
+ [source,express]
572
+ ----
573
+ SCHEMA schema_a;
574
+ REFERENCE FROM schema_b (entity_b);
575
+ ENTITY entity_a;
576
+ ref_b : entity_b;
577
+ END_ENTITY;
578
+ END_SCHEMA;
579
+
580
+ SCHEMA schema_b;
581
+ REFERENCE FROM schema_a (entity_a);
582
+ ENTITY entity_b;
583
+ ref_a : entity_a;
584
+ END_ENTITY;
585
+ END_SCHEMA;
586
+ ----
587
+
588
+ Parse them and observe how Expressir handles circular references.
589
+
590
+ ==== Exercise 3: Dependency Ordering
591
+
592
+ Given these schemas:
593
+ * `z_schema.exp` - References `m_schema`
594
+ * `m_schema.exp` - References `a_schema`
595
+ * `a_schema.exp` - No dependencies
596
+
597
+ Find the correct parsing order and explain why it matters.
598
+
599
+ === Common Pitfalls
600
+
601
+ ==== Wrong File Order
602
+
603
+ [source,ruby]
604
+ ----
605
+ # ❌ Wrong: dependent schema before dependency
606
+ files = ['catalog_application.exp', 'base_types.exp']
607
+
608
+ # ✅ Correct: dependencies first
609
+ files = ['base_types.exp', 'catalog_application.exp']
610
+ ----
611
+
612
+ **Note**: Expressir handles this automatically, but explicit ordering is clearer.
613
+
614
+ ==== Missing Interface Declarations
615
+
616
+ [source,express]
617
+ ----
618
+ # ❌ Wrong: using type without interface
619
+ SCHEMA my_schema;
620
+ ENTITY my_entity;
621
+ name : label; -- label not declared or imported!
622
+ END_ENTITY;
623
+ END_SCHEMA;
624
+
625
+ # ✅ Correct: import the type
626
+ SCHEMA my_schema;
627
+ REFERENCE FROM base_types (label);
628
+ ENTITY my_entity;
629
+ name : label;
630
+ END_ENTITY;
631
+ END_SCHEMA;
632
+ ----
633
+
634
+ ==== Assuming Reference Resolution
635
+
636
+ [source,ruby]
637
+ ----
638
+ # ❌ Wrong: assuming ref is resolved
639
+ attr.type.ref.id # May crash if ref is nil!
640
+
641
+ # ✅ Correct: check first
642
+ if attr.type.respond_to?(:ref) && attr.type.ref
643
+ puts attr.type.ref.id
644
+ else
645
+ puts "Unresolved reference: #{attr.type.id}"
646
+ end
647
+ ----
648
+
649
+ === Next Steps
650
+
651
+ Congratulations! You now understand multi-schema EXPRESS applications.
652
+
653
+ **Continue learning**:
654
+
655
+ * link:creating-ler-package.html[Creating LER Packages] - Package schemas for distribution
656
+ * link:querying-schemas.html[Querying Schemas] - Search across multiple schemas
657
+ * link:../pages/ler-packages.html[LER Packages] - Understanding LER format
658
+
659
+ **Read more**:
660
+
661
+ * link:../pages/parsers.html[Parsers] - Deep dive into parsing
662
+ * link:../pages/data-model.html[Data Model] - Understanding the model
663
+ * link:../guides/ruby-api/[Ruby API Guides] - Advanced techniques
664
+
665
+ === Summary
666
+
667
+ In this tutorial, you learned to:
668
+
669
+ * ✅ Parse multiple EXPRESS schema files
670
+ * ✅ Work with USE FROM and REFERENCE FROM
671
+ * ✅ Resolve and validate cross-schema references
672
+ * ✅ Manage schema dependencies
673
+ * ✅ Create dependency graphs
674
+ * ✅ Use schema manifests
675
+
676
+ You're now ready to work with complex, multi-schema EXPRESS applications!