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,577 @@
1
+ ---
2
+ title: Working with Repository
3
+ parent: Ruby API
4
+ grand_parent: Guides
5
+ nav_order: 2
6
+ ---
7
+
8
+ = Working with Repository
9
+
10
+ == Purpose
11
+
12
+ The Repository class is the central container for managing collections of
13
+ EXPRESS schemas. It provides methods for querying, validation, statistics,
14
+ and export operations across multiple schemas.
15
+
16
+ == References
17
+
18
+ * link:parsing-files.html[Parsing Files] - Creating repositories
19
+ * link:search-engine.html[Search Engine] - Advanced querying
20
+ * link:../../pages/ler-packages.html[LER Packages] - Binary package format
21
+
22
+ == Concepts
23
+
24
+ repository:: Container holding multiple EXPRESS schemas with query and management capabilities
25
+ schema:: Individual EXPRESS schema within a repository
26
+ indexes:: Internal structures for fast entity and type lookup
27
+ validation:: Checking repository consistency and completeness
28
+ enumerable:: Repository implements Ruby's Enumerable interface for iteration
29
+
30
+ == Creating repositories
31
+
32
+ === From parsed files
33
+
34
+ The most common way to create a repository is through parsing:
35
+
36
+ [source,ruby]
37
+ ----
38
+ require "expressir"
39
+
40
+ # Single file creates a repository
41
+ repository = Expressir::Express::Parser.from_file("schema.exp")
42
+
43
+ # Multiple files create a combined repository
44
+ files = Dir.glob("schemas/**/*.exp")
45
+ repository = Expressir::Express::Parser.from_files(files)
46
+ ----
47
+
48
+ === From file paths
49
+
50
+ Build a repository directly from file paths:
51
+
52
+ [source,ruby]
53
+ ----
54
+ file_paths = [
55
+ "schemas/action_schema.exp",
56
+ "schemas/approval_schema.exp"
57
+ ]
58
+
59
+ repository = Expressir::Model::Repository.from_files(
60
+ file_paths,
61
+ base_dir: "schemas"
62
+ )
63
+ ----
64
+
65
+ This automatically parses all files and resolves references.
66
+
67
+ === From LER package
68
+
69
+ Load a pre-built LER package:
70
+
71
+ [source,ruby]
72
+ ----
73
+ # Load from binary package
74
+ repository = Expressir::Model::Repository.from_package("schemas.ler")
75
+
76
+ # Much faster than parsing source files
77
+ puts "Loaded #{repository.schemas.size} schemas from package"
78
+ ----
79
+
80
+ === Creating empty repository
81
+
82
+ Create an empty repository and populate it manually:
83
+
84
+ [source,ruby]
85
+ ----
86
+ repository = Expressir::Model::Repository.new(base_dir: "/path/to/schemas")
87
+
88
+ # Add schemas manually if needed
89
+ # repository.schemas << schema
90
+ ----
91
+
92
+ == Accessing schemas
93
+
94
+ === Enumerable interface
95
+
96
+ Repository implements `Enumerable`, enabling Ruby collection methods:
97
+
98
+ [source,ruby]
99
+ ----
100
+ # Iterate through schemas
101
+ repository.each do |schema|
102
+ puts "Schema: #{schema.id}"
103
+ end
104
+
105
+ # Filter schemas
106
+ action_schemas = repository.select { |s| s.id.include?("action") }
107
+
108
+ # Find specific schema
109
+ schema = repository.find { |s| s.id == "action_schema" }
110
+
111
+ # Map over schemas
112
+ schema_names = repository.map(&:id)
113
+
114
+ # Count schemas
115
+ total = repository.count
116
+ ----
117
+
118
+ === Direct access
119
+
120
+ [source,ruby]
121
+ ----
122
+ # Access schemas array directly
123
+ all_schemas = repository.schemas
124
+
125
+ # Get first/last schema
126
+ first_schema = repository.schemas.first
127
+ last_schema = repository.schemas.last
128
+
129
+ # Find by index
130
+ schema = repository.schemas[0]
131
+ ----
132
+
133
+ === Schema properties
134
+
135
+ Each schema in the repository has useful properties:
136
+
137
+ [source,ruby]
138
+ ----
139
+ schema = repository.schemas.first
140
+
141
+ puts "ID: #{schema.id}"
142
+ puts "File: #{schema.file}"
143
+ puts "Version: #{schema.version&.value}"
144
+ puts "Entities: #{schema.entities&.size}"
145
+ puts "Types: #{schema.types&.size}"
146
+ puts "Functions: #{schema.functions&.size}"
147
+ ----
148
+
149
+ == Querying entities and types
150
+
151
+ === List entities
152
+
153
+ Get all entities across schemas or filtered by schema:
154
+
155
+ [source,ruby]
156
+ ----
157
+ # List all entities (returns objects by default)
158
+ entities = repository.list_entities
159
+
160
+ # Filter by schema
161
+ entities = repository.list_entities(schema: "action_schema")
162
+
163
+ # Get as hash
164
+ entities = repository.list_entities(format: :hash)
165
+
166
+ # Get as JSON
167
+ json = repository.list_entities(format: :json)
168
+
169
+ # Get as YAML
170
+ yaml = repository.list_entities(format: :yaml)
171
+ ----
172
+
173
+ === List types
174
+
175
+ Get all types with optional filtering:
176
+
177
+ [source,ruby]
178
+ ----
179
+ # List all types
180
+ types = repository.list_types
181
+
182
+ # Filter by schema
183
+ types = repository.list_types(schema: "action_schema")
184
+
185
+ # Filter by category
186
+ select_types = repository.list_types(category: "select")
187
+ enum_types = repository.list_types(category: "enumeration")
188
+
189
+ # Combine filters
190
+ types = repository.list_types(
191
+ schema: "action_schema",
192
+ category: "select",
193
+ format: :hash
194
+ )
195
+ ----
196
+
197
+ === Find specific items
198
+
199
+ Use the index methods for fast lookup:
200
+
201
+ [source,ruby]
202
+ ----
203
+ # Find entity by qualified name
204
+ entity = repository.find_entity(
205
+ qualified_name: "action_schema.action"
206
+ )
207
+
208
+ if entity
209
+ puts "Found entity: #{entity.id}"
210
+ puts "Attributes: #{entity.attributes&.size}"
211
+ end
212
+
213
+ # Find type by qualified name
214
+ type = repository.find_type(
215
+ qualified_name: "action_schema.action_items"
216
+ )
217
+ ----
218
+
219
+ == Building indexes
220
+
221
+ Repositories use indexes for fast lookups. Indexes are built automatically
222
+ when needed, but you can build them explicitly:
223
+
224
+ [source,ruby]
225
+ ----
226
+ # Build all indexes
227
+ repository.build_indexes
228
+
229
+ # Indexes are now available
230
+ puts "Entity index size: #{repository.entity_index.count}"
231
+ puts "Type index size: #{repository.type_index.count}"
232
+ ----
233
+
234
+ Indexes are lazy-loaded by default - they're built on first use:
235
+
236
+ [source,ruby]
237
+ ----
238
+ # First query builds indexes automatically
239
+ entity = repository.find_entity(qualified_name: "action_schema.action")
240
+
241
+ # Subsequent queries use existing indexes (faster)
242
+ another = repository.find_entity(qualified_name: "action_schema.event")
243
+ ----
244
+
245
+ == Validation
246
+
247
+ === Basic validation
248
+
249
+ Validate repository consistency and completeness:
250
+
251
+ [source,ruby]
252
+ ----
253
+ result = repository.validate
254
+
255
+ if result[:valid?]
256
+ puts "Repository is valid"
257
+ else
258
+ puts "Validation failed"
259
+
260
+ result[:errors].each do |error|
261
+ puts "ERROR: #{error}"
262
+ end
263
+
264
+ result[:warnings].each do |warning|
265
+ puts "WARNING: #{warning}"
266
+ end
267
+ end
268
+ ----
269
+
270
+ === Strict validation
271
+
272
+ Enable strict mode for more rigorous checking:
273
+
274
+ [source,ruby]
275
+ ----
276
+ result = repository.validate(strict: true)
277
+
278
+ # Strict mode checks:
279
+ # - Interface completeness
280
+ # - Reference integrity
281
+ # - Type consistency
282
+ # - Entity relationships
283
+ ----
284
+
285
+ == Statistics and analysis
286
+
287
+ === Basic statistics
288
+
289
+ Get comprehensive repository statistics:
290
+
291
+ [source,ruby]
292
+ ----
293
+ stats = repository.statistics
294
+
295
+ puts "Total schemas: #{stats[:total_schemas]}"
296
+ puts "Total entities: #{stats[:total_entities]}"
297
+ puts "Total types: #{stats[:total_types]}"
298
+ puts "Total functions: #{stats[:total_functions]}"
299
+ puts "Total rules: #{stats[:total_rules]}"
300
+ puts "Total procedures: #{stats[:total_procedures]}"
301
+
302
+ # Entities by schema
303
+ stats[:entities_by_schema].each do |schema, count|
304
+ puts "#{schema}: #{count} entities"
305
+ end
306
+
307
+ # Types by category
308
+ stats[:types_by_category].each do |category, count|
309
+ puts "#{category}: #{count} types"
310
+ end
311
+ ----
312
+
313
+ === Output formats
314
+
315
+ Statistics can be exported in different formats:
316
+
317
+ [source,ruby]
318
+ ----
319
+ # As Ruby hash (default)
320
+ stats = repository.statistics(format: :hash)
321
+
322
+ # As JSON string
323
+ json = repository.statistics(format: :json)
324
+
325
+ # As YAML string
326
+ yaml = repository.statistics(format: :yaml)
327
+ ----
328
+
329
+ === Schema categorization
330
+
331
+ Group schemas by their content:
332
+
333
+ [source,ruby]
334
+ ----
335
+ categories = repository.schemas_by_category
336
+
337
+ puts "Schemas with entities: #{categories[:with_entities].size}"
338
+ puts "Schemas with types: #{categories[:with_types].size}"
339
+ puts "Schemas with functions: #{categories[:with_functions].size}"
340
+ puts "Schemas with rules: #{categories[:with_rules].size}"
341
+ puts "Interface-only schemas: #{categories[:interface_only].size}"
342
+ puts "Empty schemas: #{categories[:empty].size}"
343
+ ----
344
+
345
+ === Largest schemas
346
+
347
+ Find the largest schemas by element count:
348
+
349
+ [source,ruby]
350
+ ----
351
+ # Get top 10 largest schemas
352
+ largest = repository.largest_schemas(10)
353
+
354
+ largest.each do |item|
355
+ schema = item[:schema]
356
+ count = item[:total_elements]
357
+ puts "#{schema.id}: #{count} elements"
358
+ end
359
+ ----
360
+
361
+ === Complexity analysis
362
+
363
+ Calculate schema complexity scores:
364
+
365
+ [source,ruby]
366
+ ----
367
+ # Get schemas by complexity
368
+ complex = repository.schemas_by_complexity(10)
369
+
370
+ complex.each do |item|
371
+ schema = item[:schema]
372
+ score = item[:complexity]
373
+ puts "#{schema.id}: complexity score #{score}"
374
+ end
375
+
376
+ # Calculate complexity for specific schema
377
+ schema = repository.schemas.first
378
+ score = repository.schema_complexity(schema)
379
+ puts "Complexity: #{score}"
380
+ ----
381
+
382
+ Complexity scoring:
383
+
384
+ * Entities: 2 points each
385
+ * Types: 1 point each
386
+ * Functions: 3 points each
387
+ * Procedures: 3 points each
388
+ * Rules: 4 points each
389
+ * Interfaces: 2 points each
390
+
391
+ === Dependency statistics
392
+
393
+ Analyze interface dependencies between schemas:
394
+
395
+ [source,ruby]
396
+ ----
397
+ deps = repository.dependency_statistics
398
+
399
+ puts "Total interfaces: #{deps[:total_interfaces]}"
400
+ puts "USE FROM: #{deps[:use_from_count]}"
401
+ puts "REFERENCE FROM: #{deps[:reference_from_count]}"
402
+
403
+ # Most referenced schemas
404
+ puts "\nMost referenced schemas:"
405
+ deps[:most_referenced].each do |schema, count|
406
+ puts " #{schema}: #{count} references"
407
+ end
408
+
409
+ # Most dependent schemas
410
+ puts "\nMost dependent schemas:"
411
+ deps[:most_dependent].each do |schema, count|
412
+ puts " #{schema}: #{count} dependencies"
413
+ end
414
+ ----
415
+
416
+ == Reference resolution
417
+
418
+ === Automatic resolution
419
+
420
+ By default, the parser resolves references automatically:
421
+
422
+ [source,ruby]
423
+ ----
424
+ # References are resolved during parsing
425
+ repository = Expressir::Express::Parser.from_file("schema.exp")
426
+
427
+ # USE FROM and REFERENCE FROM are linked
428
+ schema = repository.schemas.first
429
+ schema.interfaces&.each do |interface|
430
+ puts "Interface to: #{interface.schema&.id}"
431
+ end
432
+ ----
433
+
434
+ === Manual resolution
435
+
436
+ Resolve references manually if needed:
437
+
438
+ [source,ruby]
439
+ ----
440
+ # Parse without resolving references
441
+ repository = Expressir::Express::Parser.from_file(
442
+ "schema.exp",
443
+ skip_references: true
444
+ )
445
+
446
+ # Resolve later
447
+ repository.resolve_all_references
448
+
449
+ # Now references are linked
450
+ ----
451
+
452
+ This is useful when building repositories incrementally or when you want
453
+ control over when resolution happens.
454
+
455
+ == Export operations
456
+
457
+ === Create schema manifest
458
+
459
+ Generate a manifest file describing all schemas:
460
+
461
+ [source,ruby]
462
+ ----
463
+ manifest = repository.to_manifest
464
+
465
+ # Save to file
466
+ manifest.to_file("schemas.yml")
467
+
468
+ # Access entries
469
+ manifest.schemas.each do |entry|
470
+ puts "Schema: #{entry.id}"
471
+ puts "Path: #{entry.path}"
472
+ end
473
+ ----
474
+
475
+ === Export to LER package
476
+
477
+ Create a binary LER package for faster loading:
478
+
479
+ [source,ruby]
480
+ ----
481
+ # Export with default options
482
+ repository.export_to_package("output.ler")
483
+
484
+ # With custom options
485
+ repository.export_to_package(
486
+ "output.ler",
487
+ name: "My Schema Set",
488
+ version: "1.0",
489
+ description: "Production schemas",
490
+ express_mode: "include_all",
491
+ resolution_mode: "resolved",
492
+ serialization_format: "marshal"
493
+ )
494
+ ----
495
+
496
+ Options:
497
+
498
+ `name`:: Package name
499
+ `version`:: Package version
500
+ `description`:: Package description
501
+ `express_mode`:: "include_all", "include_referenced", or "exclude_all"
502
+ `resolution_mode`:: "resolved" or "unresolved"
503
+ `serialization_format`:: "marshal", "yaml", or "json"
504
+
505
+ == Performance best practices
506
+
507
+ === Use indexes efficiently
508
+
509
+ [source,ruby]
510
+ ----
511
+ # Build indexes once for multiple queries
512
+ repository.build_indexes
513
+
514
+ # Fast lookups using indexes
515
+ 100.times do |i|
516
+ entity = repository.find_entity(
517
+ qualified_name: "action_schema.entity_#{i}"
518
+ )
519
+ end
520
+ ----
521
+
522
+ === Cache repositories
523
+
524
+ For frequently used schema sets, consider caching:
525
+
526
+ [source,ruby]
527
+ ----
528
+ # Enable caching
529
+ Expressir::Express::Cache.cache_path = ".cache"
530
+
531
+ # First load parses and caches
532
+ repository = Expressir::Express::Parser.from_file("schema.exp")
533
+
534
+ # Subsequent loads use cache
535
+ repository = Expressir::Express::Parser.from_file("schema.exp")
536
+ ----
537
+
538
+ === Use LER packages
539
+
540
+ For production environments, use LER packages:
541
+
542
+ [source,ruby]
543
+ ----
544
+ # Build package once (development)
545
+ repository = Expressir::Express::Parser.from_files(all_files)
546
+ repository.export_to_package("production.ler")
547
+
548
+ # Load package quickly (production)
549
+ repository = Expressir::Model::Repository.from_package("production.ler")
550
+ # Much faster than parsing source files
551
+ ----
552
+
553
+ == Next steps
554
+
555
+ * link:search-engine.html[Search Engine] - Advanced querying capabilities
556
+ * link:formatting-schemas.html[Formatting Schemas] - Convert to EXPRESS
557
+ * link:../../pages/ler-packages.html[LER Packages] - Learn about binary packages
558
+
559
+ == Summary
560
+
561
+ The Repository class provides comprehensive schema management:
562
+
563
+ * Multiple ways to create repositories (parsing, packages, manual)
564
+ * Enumerable interface for Ruby-style collection operations
565
+ * Fast indexed lookups for entities and types
566
+ * Validation and consistency checking
567
+ * Rich statistics and analysis capabilities
568
+ * Dependency tracking and complexity scoring
569
+ * Export to manifests and LER packages
570
+
571
+ Key takeaways:
572
+
573
+ * Repository implements Enumerable for familiar collection methods
574
+ * Indexes provide fast lookups and are built automatically
575
+ * Statistics methods help understand schema characteristics
576
+ * Validation identifies issues before deployment
577
+ * LER packages offer faster loading for production use