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,795 @@
1
+ ---
2
+ title: Documentation Coverage
3
+ nav_order: 7
4
+ ---
5
+
6
+ == Documentation Coverage Analysis
7
+
8
+ === Prerequisites
9
+
10
+ Before starting this tutorial, ensure you have:
11
+
12
+ * Completed link:liquid-templates.html[Liquid Templates]
13
+ * EXPRESS schemas with varying documentation levels
14
+ * Understanding of EXPRESS remarks
15
+ * Expressir CLI and Ruby access
16
+
17
+ === Learning Objectives
18
+
19
+ By the end of this tutorial, you will be able to:
20
+
21
+ * Analyze documentation coverage of schemas
22
+ * Use the coverage CLI tool
23
+ * Identify undocumented elements
24
+ * Generate coverage reports
25
+ * Exclude specific element types
26
+ * Set documentation standards
27
+ * Improve schema documentation systematically
28
+
29
+ === What You'll Build
30
+
31
+ You'll create documentation coverage reports and tools to improve and maintain high-quality EXPRESS schema documentation.
32
+
33
+ === Step 1: Understanding Documentation Coverage
34
+
35
+ ==== What is Coverage?
36
+
37
+ Documentation coverage measures how well your EXPRESS schemas are documented through remarks (comments).
38
+
39
+ **Covered element**::
40
+ Has at least one remark
41
+ +
42
+ [source,express]
43
+ ----
44
+ (* A person in the system *)
45
+ ENTITY person;
46
+ (* Full name of the person *)
47
+ name : STRING;
48
+ END_ENTITY;
49
+ ----
50
+
51
+ **Uncovered element**::
52
+ Has no remarks
53
+ +
54
+ [source,express]
55
+ ----
56
+ ENTITY person;
57
+ name : STRING; -- No documentation!
58
+ END_ENTITY;
59
+ ----
60
+
61
+ ==== Why Coverage Matters
62
+
63
+ * **Maintainability**: Documented code is easier to maintain
64
+ * **Standards compliance**: ISO standards require documentation
65
+ * **User experience**: Developers need clear documentation
66
+ * **Quality assurance**: Coverage metrics track quality
67
+
68
+ === Step 2: Create Test Schemas
69
+
70
+ Let's create schemas with different coverage levels.
71
+
72
+ ==== Well-Documented Schema
73
+
74
+ Create `documented.exp`:
75
+
76
+ [source,express]
77
+ ----
78
+ SCHEMA documented_schema;
79
+
80
+ (* Unique identifier for entities *)
81
+ TYPE identifier = STRING;
82
+ END_TYPE;
83
+
84
+ (* A person in the organization *)
85
+ ENTITY person;
86
+ (* Unique identifier *)
87
+ id : identifier;
88
+ (* Full name *)
89
+ name : STRING;
90
+ (* Email address *)
91
+ email : OPTIONAL STRING;
92
+ END_ENTITY;
93
+
94
+ (* An organizational unit *)
95
+ ENTITY organization;
96
+ (* Organization identifier *)
97
+ org_id : identifier;
98
+ (* Organization name *)
99
+ org_name : STRING;
100
+ (* List of employees *)
101
+ employees : SET [0:?] OF person;
102
+ END_ENTITY;
103
+
104
+ END_SCHEMA;
105
+ ----
106
+
107
+ ==== Poorly-Documented Schema
108
+
109
+ Create `undocumented.exp`:
110
+
111
+ [source,express]
112
+ ----
113
+ SCHEMA undocumented_schema;
114
+
115
+ TYPE identifier = STRING;
116
+ END_TYPE;
117
+
118
+ ENTITY person;
119
+ id : identifier;
120
+ name : STRING;
121
+ email : OPTIONAL STRING;
122
+ END_ENTITY;
123
+
124
+ ENTITY organization;
125
+ org_id : identifier;
126
+ org_name : STRING;
127
+ employees : SET [0:?] OF person;
128
+ END_ENTITY;
129
+
130
+ END_SCHEMA;
131
+ ----
132
+
133
+ === Step 3: Basic Coverage Analysis
134
+
135
+ ==== Using the CLI
136
+
137
+ [source,bash]
138
+ ----
139
+ # Check single file
140
+ expressir coverage documented.exp
141
+
142
+ # Check multiple files
143
+ expressir coverage documented.exp undocumented.exp
144
+
145
+ # Check directory recursively
146
+ expressir coverage schemas/
147
+ ----
148
+
149
+ **Output for documented.exp**:
150
+ [source]
151
+ ----
152
+ File: documented.exp
153
+
154
+ ✓ All elements documented
155
+
156
+ Coverage: 100.00%
157
+ Total: 9
158
+ Documented: 9
159
+ Undocumented: 0
160
+ ----
161
+
162
+ **Output for undocumented.exp**:
163
+ [source]
164
+ ----
165
+ File: undocumented.exp
166
+
167
+ Undocumented elements:
168
+ TYPE: identifier
169
+ ENTITY: person
170
+ ATTRIBUTE: person.id
171
+ ATTRIBUTE: person.name
172
+ ATTRIBUTE: person.email
173
+ ENTITY: organization
174
+ ATTRIBUTE: organization.org_id
175
+ ATTRIBUTE: organization.org_name
176
+ ATTRIBUTE: organization.employees
177
+
178
+ Coverage: 0.00%
179
+ Total: 9
180
+ Documented: 0
181
+ Undocumented: 9
182
+ ----
183
+
184
+ === Step 4: Coverage Output Formats
185
+
186
+ ==== Text Format (Default)
187
+
188
+ [source,bash]
189
+ ----
190
+ expressir coverage schemas/ --format text
191
+ ----
192
+
193
+ Human-readable table with:
194
+ * File paths
195
+ * Undocumented elements
196
+ * Coverage percentages
197
+ * Summary statistics
198
+
199
+ ==== JSON Format
200
+
201
+ [source,bash]
202
+ ----
203
+ expressir coverage schemas/ --format json > coverage.json
204
+ ----
205
+
206
+ Programmatically parseable output:
207
+
208
+ [source,json]
209
+ ----
210
+ {
211
+ "overall": {
212
+ "coverage_percentage": 50.0,
213
+ "total_entities": 18,
214
+ "documented_entities": 9,
215
+ "undocumented_entities": 9
216
+ },
217
+ "files": [
218
+ {
219
+ "file": "schemas/documented.exp",
220
+ "coverage": 100.0,
221
+ "total": 9,
222
+ "documented": 9,
223
+ "undocumented": []
224
+ },
225
+ {
226
+ "file": "schemas/undocumented.exp",
227
+ "coverage": 0.0,
228
+ "total": 9,
229
+ "documented": 0,
230
+ "undocumented": ["identifier", "person", "person.id", ...]
231
+ }
232
+ ]
233
+ }
234
+ ----
235
+
236
+ ==== YAML Format
237
+
238
+ [source,bash]
239
+ ----
240
+ expressir coverage schemas/ --format yaml > coverage.yaml
241
+ ----
242
+
243
+ YAML output for easy reading and processing:
244
+
245
+ [source,yaml]
246
+ ----
247
+ overall:
248
+ coverage_percentage: 50.0
249
+ total_entities: 18
250
+ documented_entities: 9
251
+ undocumented_entities: 9
252
+ files:
253
+ - file: schemas/documented.exp
254
+ coverage: 100.0
255
+ total: 9
256
+ documented: 9
257
+ undocumented: []
258
+ - file: schemas/undocumented.exp
259
+ coverage: 0.0
260
+ total: 9
261
+ documented: 0
262
+ undocumented:
263
+ - identifier
264
+ - person
265
+ - person.id
266
+ ----
267
+
268
+ === Step 5: Excluding Element Types
269
+
270
+ ==== Why Exclude?
271
+
272
+ Some element types may not require documentation:
273
+ * Auto-generated TYPE descriptions
274
+ * Simple type aliases
275
+ * Internal helper functions
276
+
277
+ ==== Basic Exclusions
278
+
279
+ [source,bash]
280
+ ----
281
+ # Exclude all TYPE definitions
282
+ expressir coverage schemas/ --exclude=TYPE
283
+
284
+ # Exclude multiple types
285
+ expressir coverage schemas/ --exclude=TYPE,CONSTANT,FUNCTION
286
+
287
+ # Exclude parameters and variables
288
+ expressir coverage schemas/ --exclude=PARAMETER,VARIABLE
289
+ ----
290
+
291
+ ==== Type-Specific Exclusions
292
+
293
+ [source,bash]
294
+ ----
295
+ # Exclude only SELECT types
296
+ expressir coverage schemas/ --exclude=TYPE:SELECT
297
+
298
+ # Exclude SELECT and ENUMERATION types
299
+ expressir coverage schemas/ --exclude=TYPE:SELECT,TYPE:ENUMERATION
300
+
301
+ # Exclude inner functions
302
+ expressir coverage schemas/ --exclude=FUNCTION:INNER
303
+ ----
304
+
305
+ ==== ISO 10303 Standard Exclusions
306
+
307
+ ISO 10303 excludes SELECT and ENUMERATION types:
308
+
309
+ [source,bash]
310
+ ----
311
+ expressir coverage schemas/ --exclude=TYPE:SELECT,TYPE:ENUMERATION
312
+ ----
313
+
314
+ === Step 6: Ignoring Files
315
+
316
+ ==== Create Ignore List
317
+
318
+ Create `coverage_ignore.yaml`:
319
+
320
+ [source,yaml]
321
+ ----
322
+ # Test and example schemas
323
+ - examples/test_*.exp
324
+ - examples/demo_*.exp
325
+
326
+ # Legacy schemas
327
+ - legacy/*.exp
328
+
329
+ # Generated schemas
330
+ - generated/auto_*.exp
331
+
332
+ # Specific files
333
+ - temp/temporary_schema.exp
334
+ ----
335
+
336
+ ==== Use Ignore List
337
+
338
+ [source,bash]
339
+ ----
340
+ expressir coverage schemas/ --ignore-files coverage_ignore.yaml
341
+ ----
342
+
343
+ Ignored files:
344
+ * Still appear in reports (marked as ignored)
345
+ * Don't affect overall coverage percentage
346
+ * Useful for partial documentation efforts
347
+
348
+ === Step 7: Programmatic Coverage Analysis
349
+
350
+ ==== Basic Coverage Check
351
+
352
+ Create `check_coverage.rb`:
353
+
354
+ [source,ruby]
355
+ ----
356
+ require 'expressir'
357
+
358
+ # Parse schema
359
+ repo = Expressir::Express::Parser.from_file('documented.exp')
360
+
361
+ # Create coverage report
362
+ report = Expressir::Coverage::Report.from_repository(repo)
363
+
364
+ puts "Coverage Analysis"
365
+ puts "=" * 60
366
+ puts "Overall: #{report.coverage_percentage.round(2)}%"
367
+ puts "Total: #{report.total_entities.size}"
368
+ puts "Documented: #{report.documented_entities.size}"
369
+ puts "Undocumented: #{report.undocumented_entities.size}"
370
+
371
+ if report.undocumented_entities.any?
372
+ puts "\nUndocumented elements:"
373
+ report.undocumented_entities.take(10).each do |entity|
374
+ puts " - #{entity}"
375
+ end
376
+ end
377
+ ----
378
+
379
+ ==== Detailed File Reports
380
+
381
+ [source,ruby]
382
+ ----
383
+ # Get per-file breakdown
384
+ report.file_reports.each do |file_report|
385
+ puts "\nFile: #{file_report[:file]}"
386
+ puts " Coverage: #{file_report[:coverage]}%"
387
+ puts " Total: #{file_report[:total]}"
388
+ puts " Documented: #{file_report[:documented]}"
389
+
390
+ if file_report[:undocumented].any?
391
+ puts " Undocumented:"
392
+ file_report[:undocumented].each do |item|
393
+ puts " - #{item}"
394
+ end
395
+ end
396
+ end
397
+ ----
398
+
399
+ ==== Check Individual Elements
400
+
401
+ [source,ruby]
402
+ ----
403
+ # Check if entity is documented
404
+ schema = repo.schemas.first
405
+ entity = schema.entities.first
406
+
407
+ if Expressir::Coverage.entity_documented?(entity)
408
+ puts "✓ #{entity.id} is documented"
409
+ else
410
+ puts "✗ #{entity.id} lacks documentation"
411
+ end
412
+
413
+ # Check all entities
414
+ schema.entities.each do |entity|
415
+ status = Expressir::Coverage.entity_documented?(entity) ? "✓" : "✗"
416
+ puts "#{status} #{entity.id}"
417
+ end
418
+ ----
419
+
420
+ === Step 8: Coverage Enforcement
421
+
422
+ ==== Create Coverage Checker
423
+
424
+ Create `enforce_coverage.rb`:
425
+
426
+ [source,ruby]
427
+ ----
428
+ require 'expressir'
429
+
430
+ class CoverageEnforcer
431
+ MINIMUM_COVERAGE = 80.0 # 80% required
432
+
433
+ def initialize(files)
434
+ @files = files
435
+ end
436
+
437
+ def check
438
+ repos = @files.map { |f| Expressir::Express::Parser.from_file(f) }
439
+
440
+ all_pass = true
441
+ repos.each do |repo|
442
+ report = Expressir::Coverage::Report.from_repository(repo)
443
+
444
+ file = repo.schemas.first.file
445
+ coverage = report.coverage_percentage
446
+
447
+ if coverage < MINIMUM_COVERAGE
448
+ puts "❌ #{file}: #{coverage.round(2)}% (minimum: #{MINIMUM_COVERAGE}%)"
449
+
450
+ puts " Undocumented:"
451
+ report.undocumented_entities.take(5).each do |item|
452
+ puts " - #{item}"
453
+ end
454
+
455
+ all_pass = false
456
+ else
457
+ puts "✓ #{file}: #{coverage.round(2)}%"
458
+ end
459
+ end
460
+
461
+ all_pass
462
+ end
463
+ end
464
+
465
+ # Usage
466
+ files = Dir.glob('schemas/**/*.exp')
467
+ enforcer = CoverageEnforcer.new(files)
468
+
469
+ unless enforcer.check
470
+ puts "\n❌ Coverage check failed!"
471
+ exit 1
472
+ end
473
+
474
+ puts "\n✓ All files meet coverage requirements!"
475
+ ----
476
+
477
+ ==== CI/CD Integration
478
+
479
+ Create `.github/workflows/coverage.yml`:
480
+
481
+ [source,yaml]
482
+ ----
483
+ name: Documentation Coverage
484
+
485
+ on: [push, pull_request]
486
+
487
+ jobs:
488
+ coverage:
489
+ runs-on: ubuntu-latest
490
+
491
+ steps:
492
+ - uses: actions/checkout@v2
493
+
494
+ - name: Set up Ruby
495
+ uses: ruby/setup-ruby@v1
496
+ with:
497
+ ruby-version: 3.0
498
+
499
+ - name: Install Expressir
500
+ run: gem install expressir
501
+
502
+ - name: Check Coverage
503
+ run: |
504
+ expressir coverage schemas/ --format json > coverage.json
505
+
506
+ # Extract coverage percentage
507
+ COVERAGE=$(cat coverage.json | jq '.overall.coverage_percentage')
508
+ echo "Coverage: ${COVERAGE}%"
509
+
510
+ # Fail if below 80%
511
+ if (( $(echo "$COVERAGE < 80" | bc -l) )); then
512
+ echo "❌ Coverage below 80%"
513
+ exit 1
514
+ fi
515
+
516
+ echo "✓ Coverage check passed"
517
+
518
+ - name: Upload Coverage Report
519
+ uses: actions/upload-artifact@v2
520
+ with:
521
+ name: coverage-report
522
+ path: coverage.json
523
+ ----
524
+
525
+ === Step 9: Improving Coverage
526
+
527
+ ==== Find Low-Coverage Files
528
+
529
+ Create `find_low_coverage.rb`:
530
+
531
+ [source,ruby]
532
+ ----
533
+ require 'expressir'
534
+ require 'json'
535
+
536
+ files = Dir.glob('schemas/**/*.exp')
537
+ results = []
538
+
539
+ files.each do |file|
540
+ repo = Expressir::Express::Parser.from_file(file)
541
+ report = Expressir::Coverage::Report.from_repository(repo)
542
+
543
+ results << {
544
+ file: file,
545
+ coverage: report.coverage_percentage,
546
+ undocumented: report.undocumented_entities.size
547
+ }
548
+ end
549
+
550
+ # Sort by coverage (worst first)
551
+ results.sort_by! { |r| r[:coverage] }
552
+
553
+ puts "Low Coverage Files"
554
+ puts "=" * 60
555
+
556
+ results.take(10).each do |result|
557
+ puts "\n#{result[:file]}"
558
+ puts " Coverage: #{result[:coverage].round(2)}%"
559
+ puts " Undocumented: #{result[:undocumented]}"
560
+ end
561
+ ----
562
+
563
+ ==== Generate Documentation Templates
564
+
565
+ Create `generate_doc_templates.rb`:
566
+
567
+ [source,ruby]
568
+ ----
569
+ require 'expressir'
570
+
571
+ def generate_template(entity)
572
+ template = "(* TODO: Document #{entity.id} *)\n"
573
+ template += "ENTITY #{entity.id};\n"
574
+
575
+ entity.attributes.each do |attr|
576
+ template += " (* TODO: Document #{attr.id} *)\n"
577
+ template += " #{attr.id} : #{attr.type};\n"
578
+ end
579
+
580
+ template += "END_ENTITY;\n"
581
+ template
582
+ end
583
+
584
+ # Usage
585
+ repo = Expressir::Express::Parser.from_file('undocumented.exp')
586
+ schema = repo.schemas.first
587
+
588
+ puts "Documentation Templates for Undocumented Entities"
589
+ puts "=" * 60
590
+
591
+ schema.entities.each do |entity|
592
+ next if Expressir::Coverage.entity_documented?(entity)
593
+
594
+ puts "\n#{generate_template(entity)}"
595
+ end
596
+ ----
597
+
598
+ === Step 10: Coverage Dashboard
599
+
600
+ ==== Generate HTML Report
601
+
602
+ Create `coverage_dashboard.rb`:
603
+
604
+ [source,ruby]
605
+ ----
606
+ require 'expressir'
607
+ require 'erb'
608
+
609
+ files = Dir.glob('schemas/**/*.exp')
610
+ reports_data = []
611
+
612
+ files.each do |file|
613
+ repo = Expressir::Express::Parser.from_file(file)
614
+ report = Expressir::Coverage::Report.from_repository(repo)
615
+
616
+ reports_data << {
617
+ file: file,
618
+ coverage: report.coverage_percentage,
619
+ total: report.total_entities.size,
620
+ documented: report.documented_entities.size,
621
+ undocumented: report.undocumented_entities
622
+ }
623
+ end
624
+
625
+ template = ERB.new(<<~HTML)
626
+ <!DOCTYPE html>
627
+ <html>
628
+ <head>
629
+ <title>Coverage Dashboard</title>
630
+ <style>
631
+ body { font-family: Arial, sans-serif; margin: 20px; }
632
+ table { border-collapse: collapse; width: 100%; }
633
+ th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
634
+ th { background-color: #4CAF50; color: white; }
635
+ .high { color: green; }
636
+ .medium { color: orange; }
637
+ .low { color: red; }
638
+ </style>
639
+ </head>
640
+ <body>
641
+ <h1>Documentation Coverage Dashboard</h1>
642
+
643
+ <h2>Summary</h2>
644
+ <table>
645
+ <tr>
646
+ <th>File</th>
647
+ <th>Coverage</th>
648
+ <th>Total</th>
649
+ <th>Documented</th>
650
+ <th>Undocumented</th>
651
+ </tr>
652
+ <% reports_data.each do |report| %>
653
+ <tr>
654
+ <td><%= report[:file] %></td>
655
+ <td class="<%= report[:coverage] >= 80 ? 'high' : report[:coverage] >= 50 ? 'medium' : 'low' %>">
656
+ <%= report[:coverage].round(2) %>%
657
+ </td>
658
+ <td><%= report[:total] %></td>
659
+ <td><%= report[:documented] %></td>
660
+ <td><%= report[:total] - report[:documented] %></td>
661
+ </tr>
662
+ <% end %>
663
+ </table>
664
+
665
+ <h2>Undocumented Elements</h2>
666
+ <% reports_data.each do |report| %>
667
+ <% if report[:undocumented].any? %>
668
+ <h3><%= report[:file] %></h3>
669
+ <ul>
670
+ <% report[:undocumented].each do |item| %>
671
+ <li><%= item %></li>
672
+ <% end %>
673
+ </ul>
674
+ <% end %>
675
+ <% end %>
676
+ </body>
677
+ </html>
678
+ HTML
679
+
680
+ File.write('coverage_dashboard.html', template.result(binding))
681
+ puts "Dashboard generated: coverage_dashboard.html"
682
+ ----
683
+
684
+ === Step 11: Practice Exercises
685
+
686
+ ==== Exercise 1: Coverage Trends
687
+
688
+ Track coverage over time:
689
+ * Store coverage results with timestamps
690
+ * Generate trend graphs
691
+ * Identify improving/declining files
692
+
693
+ ==== Exercise 2: Smart Exclusions
694
+
695
+ Create a configuration system that:
696
+ * Defines project-specific exclusions
697
+ * Supports per-directory rules
698
+ * Allows temporary exemptions with expiry
699
+
700
+ ==== Exercise 3: Documentation Generator
701
+
702
+ Build a tool that:
703
+ * Identifies undocumented elements
704
+ * Suggests documentation based on element names
705
+ * Generates documentation templates
706
+ * Validates documentation quality
707
+
708
+ === Best Practices
709
+
710
+ **Set Realistic Targets**::
711
+ * Start with current coverage baseline
712
+ * Improve gradually (e.g., +5% per sprint)
713
+ * Different standards for different schema types
714
+
715
+ **Exclude Appropriately**::
716
+ * Document exclusion rationale
717
+ * Review exclusions periodically
718
+ * Don't exclude to inflate numbers
719
+
720
+ **Integrate Early**::
721
+ * Add coverage checks to CI/CD
722
+ * Review coverage in code reviews
723
+ * Track coverage metrics over time
724
+
725
+ **Quality Over Quantity**::
726
+ * Brief, clear remarks are better than verbose ones
727
+ * Explain *why*, not just *what*
728
+ * Keep documentation up-to-date
729
+
730
+ === Common Issues
731
+
732
+ **False Positives**
733
+
734
+ **Problem**: Element marked as undocumented but has remarks
735
+
736
+ **Cause**: Remarks may not be properly formatted
737
+
738
+ **Solution**: Ensure remarks use EXPRESS comment syntax:
739
+ [source,express]
740
+ ----
741
+ (* This is a valid remark *)
742
+ ENTITY person;
743
+ END_ENTITY;
744
+ ----
745
+
746
+ **High Coverage, Poor Quality**
747
+
748
+ **Problem**: 100% coverage but unhelpful documentation
749
+
750
+ **Solution**:
751
+ * Review documentation quality manually
752
+ * Use specific, descriptive remarks
753
+ * Explain purpose and constraints
754
+
755
+ **Coverage Varies by Tool**
756
+
757
+ **Problem**: Different tools report different coverage
758
+
759
+ **Cause**: Different element types counted
760
+
761
+ **Solution**:
762
+ * Use consistent tool and exclusions
763
+ * Document counting methodology
764
+ * Track relative changes, not absolute numbers
765
+
766
+ === Next Steps
767
+
768
+ Congratulations! You can now analyze and improve documentation coverage.
769
+
770
+ **Continue learning**:
771
+
772
+ * link:../guides/cli/coverage-analysis.html[Coverage Analysis Guide] - Advanced techniques
773
+ * link:../guides/ruby-api/[Ruby API Guides] - Programmatic coverage tools
774
+ * link:../references/[References] - Complete API documentation
775
+
776
+ **Read more**:
777
+
778
+ * ISO 10303 documentation standards
779
+ * Technical writing best practices
780
+
781
+ === Summary
782
+
783
+ In this tutorial, you learned to:
784
+
785
+ * ✅ Analyze documentation coverage
786
+ * ✅ Use the coverage CLI tool
787
+ * ✅ Generate coverage reports in multiple formats
788
+ * ✅ Exclude specific element types
789
+ * ✅ Ignore files from coverage calculation
790
+ * ✅ Check coverage programmatically
791
+ * ✅ Enforce coverage standards
792
+ * ✅ Track and improve coverage over time
793
+ * ✅ Create coverage dashboards
794
+
795
+ You're now equipped to maintain high-quality EXPRESS schema documentation!