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,813 @@
1
+ ---
2
+ title: Basic Templates
3
+ parent: Liquid
4
+ grand_parent: Guides
5
+ nav_order: 1
6
+ ---
7
+
8
+ = Basic Liquid templates
9
+
10
+ == Purpose
11
+
12
+ This guide covers Liquid template fundamentals including syntax,
13
+ variables, control flow, loops, and template organization. Master these
14
+ basics to create effective documentation templates for EXPRESS schemas.
15
+
16
+ == References
17
+
18
+ * link:index.html[Liquid Overview] - Integration fundamentals
19
+ * link:drops-reference.html[Drops Reference] - Available data objects
20
+ * link:filters-and-tags.html[Filters and Tags] - Built-in operations
21
+ * https://shopify.github.io/liquid/basics/introduction/[Liquid Basics] -
22
+ Official introduction
23
+
24
+ == Concepts
25
+
26
+ Object:: A piece of data that Liquid can output, accessed with `{{}}`.
27
+
28
+ Tag:: Logic and control flow elements that use `{%%}` delimiters.
29
+
30
+ Filter:: A function that transforms values, applied with the `|`
31
+ operator.
32
+
33
+ Variable:: A named reference to data, either from input or created with
34
+ `assign`.
35
+
36
+ Whitespace control:: Techniques to manage blank lines and spacing in
37
+ output.
38
+
39
+ == Template structure
40
+
41
+ A Liquid template is a text file containing:
42
+
43
+ * **Static content**: Output as-is
44
+ * **Output tags**: `{{ }}` to display data
45
+ * **Logic tags**: `{% %}` for control flow
46
+ * **Filters**: Transform output values
47
+ * **Comments**: Documentation within templates
48
+
49
+ .Basic template anatomy
50
+ [example]
51
+ ====
52
+ [source,liquid]
53
+ ----
54
+ # {{ schema.id }} Documentation <1>
55
+
56
+ {% comment %}This is a comment{% endcomment %} <2>
57
+
58
+ {% if schema.version %} <3>
59
+ **Version**: {{ schema.version.value }} <4>
60
+ {% endif %}
61
+
62
+ Entities: {{ schema.entities | size }} <5>
63
+
64
+ {% for entity in schema.entities %} <6>
65
+ - {{ entity.id }}
66
+ {% endfor %}
67
+ ----
68
+ <1> Static text with output tag
69
+ <2> Comment (not included in output)
70
+ <3> Conditional logic tag
71
+ <4> Nested output with data access
72
+ <5> Output with filter
73
+ <6> Loop tag
74
+ ====
75
+
76
+ == Variables and output
77
+
78
+ === Output tags
79
+
80
+ Use `{{ }}` to output data:
81
+
82
+ [source,liquid]
83
+ ----
84
+ {{ schema.id }}
85
+ {{ entity.attributes.size }}
86
+ {{ attr.type }}
87
+ ----
88
+
89
+ === Dot notation
90
+
91
+ Access nested attributes with dots:
92
+
93
+ [source,liquid]
94
+ ----
95
+ {{ repository.schemas.first.id }}
96
+ {{ entity.parent.id }}
97
+ {{ schema.version.value }}
98
+ ----
99
+
100
+ === Array access
101
+
102
+ Access array elements by index or use filters:
103
+
104
+ [source,liquid]
105
+ ----
106
+ {{ schema.entities[0].id }}
107
+ {{ schema.entities.first.id }}
108
+ {{ schema.entities.last.id }}
109
+ {{ schema.entities | map: "id" }}
110
+ ----
111
+
112
+ .Complete example accessing schema data
113
+ [example]
114
+ ====
115
+ [source,liquid]
116
+ ----
117
+ Schema: {{ schema.id }}
118
+ File: {{ schema.file }}
119
+
120
+ First entity: {{ schema.entities.first.id }}
121
+ Last entity: {{ schema.entities.last.id }}
122
+ Total entities: {{ schema.entities.size }}
123
+ ----
124
+
125
+ Given a schema with entities `person` and `company`:
126
+
127
+ [source]
128
+ ----
129
+ Schema: example_schema
130
+ File: schemas/example_schema.exp
131
+
132
+ First entity: person
133
+ Last entity: company
134
+ Total entities: 2
135
+ ----
136
+ ====
137
+
138
+ == Variable assignment
139
+
140
+ Create your own variables with `assign`:
141
+
142
+ [source,liquid]
143
+ ----
144
+ {% assign total = schema.entities.size %}
145
+ {% assign has_types = schema.types.size > 0 %}
146
+ {% assign first_entity = schema.entities.first %}
147
+
148
+ Total entities: {{ total }}
149
+ Has types: {{ has_types }}
150
+ First: {{ first_entity.id }}
151
+ ----
152
+
153
+ Variables persist within the template scope:
154
+
155
+ [source,liquid]
156
+ ----
157
+ {% assign count = 0 %}
158
+
159
+ {% for entity in schema.entities %}
160
+ {% assign count = count | plus: 1 %}
161
+ {% endfor %}
162
+
163
+ Processed {{ count }} entities
164
+ ----
165
+
166
+ == Control flow
167
+
168
+ === If statements
169
+
170
+ Conditional logic with `if`:
171
+
172
+ [source,liquid]
173
+ ----
174
+ {% if entity.abstract %}
175
+ **Abstract Entity** - Cannot be instantiated directly.
176
+ {% endif %}
177
+
178
+ {% if entity.attributes.size > 0 %}
179
+ This entity has attributes.
180
+ {% endif %}
181
+ ----
182
+
183
+ === If-else statements
184
+
185
+ [source,liquid]
186
+ ----
187
+ {% if entity.remarks.size > 0 %}
188
+ ## Description
189
+ {{ entity.remarks | join: "\n" }}
190
+ {% else %}
191
+ No description available.
192
+ {% endif %}
193
+ ----
194
+
195
+ === Elsif for multiple conditions
196
+
197
+ [source,liquid]
198
+ ----
199
+ {% if entity.attributes.size > 10 %}
200
+ Complex entity with many attributes.
201
+ {% elsif entity.attributes.size > 5 %}
202
+ Medium complexity entity.
203
+ {% elsif entity.attributes.size > 0 %}
204
+ Simple entity.
205
+ {% else %}
206
+ Entity without attributes.
207
+ {% endif %}
208
+ ----
209
+
210
+ === Unless (negative condition)
211
+
212
+ `unless` is the opposite of `if`:
213
+
214
+ [source,liquid]
215
+ ----
216
+ {% unless entity.abstract %}
217
+ Can be instantiated.
218
+ {% endunless %}
219
+
220
+ {% comment %}Equivalent to:{% endcomment %}
221
+ {% if entity.abstract == false %}
222
+ Can be instantiated.
223
+ {% endif %}
224
+ ----
225
+
226
+ === Case statements
227
+
228
+ Use `case` for multiple value checks:
229
+
230
+ [source,liquid]
231
+ ----
232
+ {% case entity.attributes.size %}
233
+ {% when 0 %}
234
+ No attributes
235
+ {% when 1 %}
236
+ One attribute
237
+ {% when 2 %}
238
+ Two attributes
239
+ {% else %}
240
+ {{ entity.attributes.size }} attributes
241
+ {% endcase %}
242
+ ----
243
+
244
+ .Complete example with control flow
245
+ [example]
246
+ ====
247
+ [source,liquid]
248
+ ----
249
+ {% for entity in schema.entities %}
250
+ ## {{ entity.id }}
251
+
252
+ {% if entity.abstract %}
253
+ _Abstract entity_
254
+ {% endif %}
255
+
256
+ {% if entity.remarks.size > 0 %}
257
+ {{ entity.remarks | join: " " }}
258
+ {% else %}
259
+ No documentation available.
260
+ {% endif %}
261
+
262
+ {% case entity.attributes.size %}
263
+ {% when 0 %}
264
+ No attributes defined.
265
+ {% when 1 %}
266
+ Has one attribute.
267
+ {% else %}
268
+ Has {{ entity.attributes.size }} attributes.
269
+ {% endcase %}
270
+
271
+ {% endfor %}
272
+ ----
273
+ ====
274
+
275
+ == Comparison operators
276
+
277
+ Use these operators in conditions:
278
+
279
+ `==`:: Equal to
280
+ `!=`:: Not equal to
281
+ `>`:: Greater than
282
+ `<`:: Less than
283
+ `>=`:: Greater than or equal to
284
+ `<=`:: Less than or equal to
285
+
286
+ [source,liquid]
287
+ ----
288
+ {% if entity.attributes.size == 0 %}
289
+ {% if entity.attributes.size != 0 %}
290
+ {% if entity.attributes.size > 5 %}
291
+ {% if entity.attributes.size >= 10 %}
292
+ ----
293
+
294
+ == Logical operators
295
+
296
+ Combine conditions with logical operators:
297
+
298
+ `and`:: Both conditions must be true
299
+ `or`:: At least one condition must be true
300
+ `contains`:: Check if string/array contains value
301
+
302
+ [source,liquid]
303
+ ----
304
+ {% if entity.attributes.size > 0 and entity.where_rules.size > 0 %}
305
+ Entity has both attributes and constraints.
306
+ {% endif %}
307
+
308
+ {% if entity.abstract or entity.attributes.size == 0 %}
309
+ Abstract or empty entity.
310
+ {% endif %}
311
+
312
+ {% if entity.id contains "person" %}
313
+ Person-related entity.
314
+ {% endif %}
315
+ ----
316
+
317
+ == Loops
318
+
319
+ === For loops
320
+
321
+ Iterate over collections with `for`:
322
+
323
+ [source,liquid]
324
+ ----
325
+ {% for entity in schema.entities %}
326
+ - {{ entity.id }}
327
+ {% endfor %}
328
+ ----
329
+
330
+ === Loop variables
331
+
332
+ Special variables available in loops:
333
+
334
+ `forloop.index`:: Current iteration (1-based)
335
+ `forloop.index0`:: Current iteration (0-based)
336
+ `forloop.first`:: True for first iteration
337
+ `forloop.last`:: True for last iteration
338
+ `forloop.length`:: Total iterations
339
+
340
+ [source,liquid]
341
+ ----
342
+ {% for entity in schema.entities %}
343
+ {{ forloop.index }}. {{ entity.id }}
344
+ {% if forloop.first %}(first){% endif %}
345
+ {% if forloop.last %}(last){% endif %}
346
+ {% endfor %}
347
+ ----
348
+
349
+ .Output example
350
+ [example]
351
+ ====
352
+ [source]
353
+ ----
354
+ 1. person (first)
355
+ 2. company (last)
356
+ ----
357
+ ====
358
+
359
+ === Loop limits
360
+
361
+ Control iteration with `limit` and `offset`:
362
+
363
+ [source,liquid]
364
+ ----
365
+ {% comment %}First 5 entities{% endcomment %}
366
+ {% for entity in schema.entities limit:5 %}
367
+ - {{ entity.id }}
368
+ {% endfor %}
369
+
370
+ {% comment %}Skip first 2, take next 3{% endcomment %}
371
+ {% for entity in schema.entities offset:2 limit:3 %}
372
+ - {{ entity.id }}
373
+ {% endfor %}
374
+ ----
375
+
376
+ === Range loops
377
+
378
+ Iterate over number ranges:
379
+
380
+ [source,liquid]
381
+ ----
382
+ {% for i in (1..5) %}
383
+ {{ i }}
384
+ {% endfor %}
385
+
386
+ {% comment %}Output: 1 2 3 4 5{% endcomment %}
387
+
388
+ {% assign max = 10 %}
389
+ {% for i in (1..max) %}
390
+ Item {{ i }}
391
+ {% endfor %}
392
+ ----
393
+
394
+ === Reversed loops
395
+
396
+ Iterate in reverse order:
397
+
398
+ [source,liquid]
399
+ ----
400
+ {% for entity in schema.entities reversed %}
401
+ - {{ entity.id }}
402
+ {% endfor %}
403
+ ----
404
+
405
+ === Break and continue
406
+
407
+ Control loop flow:
408
+
409
+ [source,liquid]
410
+ ----
411
+ {% comment %}Stop after finding first abstract entity{% endcomment %}
412
+ {% for entity in schema.entities %}
413
+ {% if entity.abstract %}
414
+ Found: {{ entity.id }}
415
+ {% break %}
416
+ {% endif %}
417
+ {% endfor %}
418
+
419
+ {% comment %}Skip non-abstract entities{% endcomment %}
420
+ {% for entity in schema.entities %}
421
+ {% unless entity.abstract %}
422
+ {% continue %}
423
+ {% endunless %}
424
+ Abstract: {{ entity.id }}
425
+ {% endfor %}
426
+ ----
427
+
428
+ === Nested loops
429
+
430
+ Combine loops for hierarchical data:
431
+
432
+ [source,liquid]
433
+ ----
434
+ {% for schema in repository.schemas %}
435
+ # {{ schema.id }}
436
+
437
+ {% for entity in schema.entities %}
438
+ ## {{ entity.id }}
439
+
440
+ {% for attr in entity.attributes %}
441
+ - {{ attr.id }}: {{ attr.type }}
442
+ {% endfor %}
443
+ {% endfor %}
444
+ {% endfor %}
445
+ ----
446
+
447
+ .Complete loop example with features
448
+ [example]
449
+ ====
450
+ [source,liquid]
451
+ ----
452
+ # Entity Listing
453
+
454
+ {% for entity in schema.entities %}
455
+ {{ forloop.index }}. **{{ entity.id }}**
456
+ {% if entity.abstract %}(Abstract){% endif %}
457
+
458
+ {% if entity.attributes.size > 0 %}
459
+ Attributes:
460
+ {% for attr in entity.attributes limit:3 %}
461
+ - {{ attr.id }}
462
+ {% if forloop.last and entity.attributes.size > 3 %}
463
+ - ... and {{ entity.attributes.size | minus: 3 }} more
464
+ {% endif %}
465
+ {% endfor %}
466
+ {% endif %}
467
+
468
+ {% unless forloop.last %}
469
+ ---
470
+ {% endunless %}
471
+ {% endfor %}
472
+ ----
473
+ ====
474
+
475
+ == Comments
476
+
477
+ Add notes within templates:
478
+
479
+ [source,liquid]
480
+ ----
481
+ {% comment %}
482
+ This template generates entity documentation.
483
+ Author: Team Name
484
+ Last updated: 2024-01-15
485
+ {% endcomment %}
486
+
487
+ {% comment %}TODO: Add where rules section{% endcomment %}
488
+
489
+ {% for entity in schema.entities %}
490
+ {% comment %}Process each entity{% endcomment %}
491
+ {{ entity.id }}
492
+ {% endfor %}
493
+ ----
494
+
495
+ Single-line comments:
496
+
497
+ [source,liquid]
498
+ ----
499
+ {% comment %}This is a single line comment{% endcomment %}
500
+ ----
501
+
502
+ Multi-line comments:
503
+
504
+ [source,liquid]
505
+ ----
506
+ {% comment %}
507
+ This is a longer comment
508
+ that spans multiple lines
509
+ for detailed explanations.
510
+ {% endcomment %}
511
+ ----
512
+
513
+ == Whitespace control
514
+
515
+ Control spacing and blank lines in output.
516
+
517
+ === Default behavior
518
+
519
+ Liquid preserves whitespace:
520
+
521
+ [source,liquid]
522
+ ----
523
+ {% for entity in schema.entities %}
524
+ {{ entity.id }}
525
+ {% endfor %}
526
+ ----
527
+
528
+ Creates blank lines from the loop tags.
529
+
530
+ === Hyphen for whitespace stripping
531
+
532
+ Add `-` to strip whitespace:
533
+
534
+ [source,liquid]
535
+ ----
536
+ {%- for entity in schema.entities -%}
537
+ {{ entity.id }}
538
+ {%- endfor -%}
539
+ ----
540
+
541
+ `{%-`:: Strip whitespace before tag
542
+ `-%}`:: Strip whitespace after tag
543
+ `{{-`:: Strip whitespace before output
544
+ `-}}`:: Strip whitespace after output
545
+
546
+ .Whitespace comparison
547
+ [example]
548
+ ====
549
+ Without stripping:
550
+ [source,liquid]
551
+ ----
552
+ {% for entity in schema.entities %}
553
+ {{ entity.id }}
554
+ {% endfor %}
555
+ ----
556
+
557
+ Output (with blank lines):
558
+ [source]
559
+ ----
560
+
561
+ person
562
+
563
+ company
564
+
565
+ ----
566
+
567
+ With stripping:
568
+ [source,liquid]
569
+ ----
570
+ {%- for entity in schema.entities %}
571
+ {{ entity.id }}
572
+ {%- endfor %}
573
+ ----
574
+
575
+ Output (compact):
576
+ [source]
577
+ ----
578
+ person
579
+ company
580
+ ----
581
+ ====
582
+
583
+ === Strategic whitespace use
584
+
585
+ [source,liquid]
586
+ ----
587
+ # Schema: {{ schema.id }}
588
+ {%- comment %}No blank after title{% endcomment %}
589
+
590
+ ## Entities
591
+ {%- comment %}No blank after heading{% endcomment %}
592
+
593
+ {%- for entity in schema.entities %}
594
+ - {{ entity.id }}
595
+ {%- endfor %}
596
+ {%- comment %}Compact list{% endcomment %}
597
+ ----
598
+
599
+ == Capture for complex variables
600
+
601
+ Store template output in variables:
602
+
603
+ [source,liquid]
604
+ ----
605
+ {% capture entity_list %}
606
+ {% for entity in schema.entities %}
607
+ {{ entity.id }}{% unless forloop.last %}, {% endunless %}
608
+ {% endfor %}
609
+ {% endcapture %}
610
+
611
+ Entities: {{ entity_list }}
612
+ ----
613
+
614
+ Useful for:
615
+
616
+ * Reusing generated content
617
+ * Building complex strings
618
+ * Conditional output assembly
619
+
620
+ [source,liquid]
621
+ ----
622
+ {% capture description %}
623
+ {% if entity.remarks.size > 0 %}
624
+ {{ entity.remarks | join: " " }}
625
+ {% else %}
626
+ {{ entity.id | capitalize }} entity
627
+ {% endif %}
628
+ {% endcapture %}
629
+
630
+ Description: {{ description | strip }}
631
+ ----
632
+
633
+ == Template organization best practices
634
+
635
+ === Use descriptive variable names
636
+
637
+ [source,liquid]
638
+ ----
639
+ {% comment %}❌ Poor{% endcomment %}
640
+ {% assign t = schema.types.size %}
641
+ {% assign e = schema.entities.size %}
642
+
643
+ {% comment %}✅ Good{% endcomment %}
644
+ {% assign type_count = schema.types.size %}
645
+ {% assign entity_count = schema.entities.size %}
646
+ ----
647
+
648
+ === Group related logic
649
+
650
+ [source,liquid]
651
+ ----
652
+ {% comment %}=== Statistics Section ==={% endcomment %}
653
+ {% assign entity_count = schema.entities.size %}
654
+ {% assign type_count = schema.types.size %}
655
+ {% assign total = entity_count | plus: type_count %}
656
+
657
+ {% comment %}=== Output Section ==={% endcomment %}
658
+ Entities: {{ entity_count }}
659
+ Types: {{ type_count }}
660
+ Total: {{ total }}
661
+ ----
662
+
663
+ === Keep conditions simple
664
+
665
+ [source,liquid]
666
+ ----
667
+ {% comment %}❌ Complex{% endcomment %}
668
+ {% if entity.attributes.size > 0 and entity.where_rules.size > 0
669
+ and entity.remarks.size > 0 %}
670
+
671
+ {% comment %}✅ Better - break down{% endcomment %}
672
+ {% assign has_attributes = entity.attributes.size > 0 %}
673
+ {% assign has_rules = entity.where_rules.size > 0 %}
674
+ {% assign has_docs = entity.remarks.size > 0 %}
675
+
676
+ {% if has_attributes and has_rules and has_docs %}
677
+ ----
678
+
679
+ === Use consistent formatting
680
+
681
+ [source,liquid]
682
+ ----
683
+ {% comment %}Consistent indentation{% endcomment %}
684
+ {% for schema in repository.schemas %}
685
+ {% for entity in schema.entities %}
686
+ {% for attr in entity.attributes %}
687
+ {{ attr.id }}
688
+ {% endfor %}
689
+ {% endfor %}
690
+ {% endfor %}
691
+
692
+ {% comment %}Consistent tag spacing{% endcomment %}
693
+ {% if condition %}
694
+ content
695
+ {% endif %}
696
+
697
+ {% for item in items %}
698
+ {{ item }}
699
+ {% endfor %}
700
+ ----
701
+
702
+ === Comment complex logic
703
+
704
+ [source,liquid]
705
+ ----
706
+ {% comment %}
707
+ Build entity hierarchy showing inheritance relationships.
708
+ First pass: identify root entities (no supertypes).
709
+ Second pass: recursively show children.
710
+ {% endcomment %}
711
+
712
+ {% assign root_entities = schema.entities
713
+ | where: "subtype_of", empty %}
714
+
715
+ {% for entity in root_entities %}
716
+ {% comment %}Recursively process children{% endcomment %}
717
+ ...
718
+ {% endfor %}
719
+ ----
720
+
721
+ == Common patterns
722
+
723
+ === Conditional sections
724
+
725
+ [source,liquid]
726
+ ----
727
+ {% if entity.where_rules.size > 0 %}
728
+ ## Constraints
729
+
730
+ {% for rule in entity.where_rules %}
731
+ ### {{ rule.id }}
732
+ {{ rule.expression }}
733
+ {% endfor %}
734
+ {% endif %}
735
+ ----
736
+
737
+ === Fallback content
738
+
739
+ [source,liquid]
740
+ ----
741
+ {% if entity.remarks.size > 0 %}
742
+ {{ entity.remarks | join: " " }}
743
+ {% else %}
744
+ _No documentation available._
745
+ {% endif %}
746
+ ----
747
+
748
+ === Sorted output
749
+
750
+ [source,liquid]
751
+ ----
752
+ {% assign sorted_entities = schema.entities | sort: "id" %}
753
+
754
+ {% for entity in sorted_entities %}
755
+ - {{ entity.id }}
756
+ {% endfor %}
757
+ ----
758
+
759
+ === Filtered lists
760
+
761
+ [source,liquid]
762
+ ----
763
+ {% comment %}Show only abstract entities{% endcomment %}
764
+ {% for entity in schema.entities %}
765
+ {% if entity.abstract %}
766
+ - {{ entity.id }} (abstract)
767
+ {% endif %}
768
+ {% endfor %}
769
+ ----
770
+
771
+ === Numbered lists
772
+
773
+ [source,liquid]
774
+ ----
775
+ {% for entity in schema.entities %}
776
+ {{ forloop.index }}. {{ entity.id }}
777
+ {% if entity.abstract %}
778
+ _Abstract entity_
779
+ {% endif %}
780
+ {% endfor %}
781
+ ----
782
+
783
+ == Next steps
784
+
785
+ Build on these basics:
786
+
787
+ * link:drops-reference.html[Drops Reference] - Explore available data
788
+ * link:filters-and-tags.html[Filters and Tags] - Transform and operate
789
+ on data
790
+ * link:documentation-generation.html[Documentation Generation] -
791
+ Complete workflows
792
+
793
+ **Practice**:
794
+
795
+ * link:../../_tutorials/liquid-templates.html[Liquid Templates
796
+ Tutorial] - Hands-on examples
797
+
798
+ == Summary
799
+
800
+ Liquid template basics include:
801
+
802
+ * ✅ Output tags `{{ }}` for displaying data
803
+ * ✅ Logic tags `{% %}` for control flow
804
+ * ✅ Variables with `assign` and `capture`
805
+ * ✅ Conditionals: `if`, `elsif`, `else`, `unless`, `case`
806
+ * ✅ Loops: `for`, `break`, `continue`, `reversed`
807
+ * ✅ Loop variables: `forloop.index`, `forloop.first`, etc.
808
+ * ✅ Whitespace control with `-` hyphens
809
+ * ✅ Comments for documentation
810
+ * ✅ Organization best practices
811
+
812
+ Master these fundamentals to create effective, maintainable templates
813
+ for EXPRESS schema documentation.