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.
- checksums.yaml +4 -4
- data/.github/workflows/docs.yml +98 -0
- data/.github/workflows/links.yml +100 -0
- data/.github/workflows/rake.yml +4 -0
- data/.github/workflows/release.yml +5 -0
- data/.github/workflows/validate_schemas.yml +1 -1
- data/.gitignore +3 -0
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +244 -39
- data/Gemfile +2 -1
- data/README.adoc +621 -54
- data/docs/Gemfile +12 -0
- data/docs/_config.yml +141 -0
- data/docs/_guides/changes/changes-format.adoc +778 -0
- data/docs/_guides/changes/importing-eengine.adoc +898 -0
- data/docs/_guides/changes/index.adoc +396 -0
- data/docs/_guides/changes/programmatic-usage.adoc +1038 -0
- data/docs/_guides/changes/validating-changes.adoc +681 -0
- data/docs/_guides/cli/benchmark-performance.adoc +834 -0
- data/docs/_guides/cli/coverage-analysis.adoc +921 -0
- data/docs/_guides/cli/format-schemas.adoc +547 -0
- data/docs/_guides/cli/index.adoc +8 -0
- data/docs/_guides/cli/managing-changes.adoc +927 -0
- data/docs/_guides/cli/validate-ascii.adoc +645 -0
- data/docs/_guides/cli/validate-schemas.adoc +534 -0
- data/docs/_guides/index.adoc +165 -0
- data/docs/_guides/ler/creating-packages.adoc +664 -0
- data/docs/_guides/ler/index.adoc +305 -0
- data/docs/_guides/ler/loading-packages.adoc +707 -0
- data/docs/_guides/ler/package-formats.adoc +748 -0
- data/docs/_guides/ler/querying-packages.adoc +826 -0
- data/docs/_guides/ler/validating-packages.adoc +750 -0
- data/docs/_guides/liquid/basic-templates.adoc +813 -0
- data/docs/_guides/liquid/documentation-generation.adoc +1042 -0
- data/docs/_guides/liquid/drops-reference.adoc +829 -0
- data/docs/_guides/liquid/filters-and-tags.adoc +912 -0
- data/docs/_guides/liquid/index.adoc +468 -0
- data/docs/_guides/manifests/creating-manifests.adoc +483 -0
- data/docs/_guides/manifests/index.adoc +307 -0
- data/docs/_guides/manifests/resolving-manifests.adoc +557 -0
- data/docs/_guides/manifests/validating-manifests.adoc +713 -0
- data/docs/_guides/ruby-api/formatting-schemas.adoc +605 -0
- data/docs/_guides/ruby-api/index.adoc +257 -0
- data/docs/_guides/ruby-api/parsing-files.adoc +421 -0
- data/docs/_guides/ruby-api/search-engine.adoc +609 -0
- data/docs/_guides/ruby-api/working-with-repository.adoc +577 -0
- data/docs/_pages/data-model.adoc +665 -0
- data/docs/_pages/express-language.adoc +506 -0
- data/docs/_pages/getting-started.adoc +414 -0
- data/docs/_pages/index.adoc +116 -0
- data/docs/_pages/introduction.adoc +256 -0
- data/docs/_pages/ler-packages.adoc +837 -0
- data/docs/_pages/parsers.adoc +683 -0
- data/docs/_pages/schema-manifests.adoc +431 -0
- data/docs/_references/index.adoc +228 -0
- data/docs/_tutorials/creating-ler-package.adoc +735 -0
- data/docs/_tutorials/documentation-coverage.adoc +795 -0
- data/docs/_tutorials/index.adoc +221 -0
- data/docs/_tutorials/liquid-templates.adoc +806 -0
- data/docs/_tutorials/parsing-your-first-schema.adoc +522 -0
- data/docs/_tutorials/querying-schemas.adoc +751 -0
- data/docs/_tutorials/working-with-multiple-schemas.adoc +676 -0
- data/docs/index.adoc +242 -0
- data/docs/lychee.toml +84 -0
- data/examples/demo_ler_usage.sh +86 -0
- data/examples/ler/README.md +111 -0
- data/examples/ler/simple_example.ler +0 -0
- data/examples/ler/simple_schema.exp +33 -0
- data/examples/ler_build.rb +75 -0
- data/examples/ler_cli.rb +79 -0
- data/examples/ler_demo_complete.rb +276 -0
- data/examples/ler_query.rb +91 -0
- data/examples/ler_query_examples.rb +305 -0
- data/examples/ler_stats.rb +81 -0
- data/examples/phase3_demo.rb +159 -0
- data/examples/query_demo_simple.rb +131 -0
- data/expressir.gemspec +2 -0
- data/lib/expressir/cli.rb +12 -4
- data/lib/expressir/commands/manifest.rb +427 -0
- data/lib/expressir/commands/package.rb +1274 -0
- data/lib/expressir/commands/validate.rb +70 -37
- data/lib/expressir/commands/validate_ascii.rb +607 -0
- data/lib/expressir/commands/validate_load.rb +88 -0
- data/lib/expressir/express/formatter.rb +5 -1
- data/lib/expressir/express/formatters/remark_item_formatter.rb +25 -0
- data/lib/expressir/express/parser.rb +33 -0
- data/lib/expressir/manifest/resolver.rb +213 -0
- data/lib/expressir/manifest/validator.rb +195 -0
- data/lib/expressir/model/declarations/entity.rb +6 -0
- data/lib/expressir/model/dependency_resolver.rb +270 -0
- data/lib/expressir/model/indexes/entity_index.rb +103 -0
- data/lib/expressir/model/indexes/reference_index.rb +148 -0
- data/lib/expressir/model/indexes/type_index.rb +149 -0
- data/lib/expressir/model/interface_validator.rb +384 -0
- data/lib/expressir/model/repository.rb +400 -5
- data/lib/expressir/model/repository_validator.rb +295 -0
- data/lib/expressir/model/search_engine.rb +525 -0
- data/lib/expressir/model.rb +4 -94
- data/lib/expressir/package/builder.rb +200 -0
- data/lib/expressir/package/metadata.rb +81 -0
- data/lib/expressir/package/reader.rb +165 -0
- data/lib/expressir/schema_manifest.rb +11 -1
- data/lib/expressir/version.rb +1 -1
- data/lib/expressir.rb +15 -2
- metadata +114 -4
- data/docs/benchmarking.adoc +0 -107
- data/docs/liquid_drops.adoc +0 -1547
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Parsing Your First Schema
|
|
3
|
+
nav_order: 2
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
== Parsing Your First Schema
|
|
7
|
+
|
|
8
|
+
=== Prerequisites
|
|
9
|
+
|
|
10
|
+
Before starting this tutorial, ensure you have:
|
|
11
|
+
|
|
12
|
+
* Ruby 2.7 or later installed
|
|
13
|
+
* Expressir gem installed (see link:../pages/getting-started.html[Getting Started])
|
|
14
|
+
* Basic understanding of EXPRESS (see link:../pages/express-language.html[EXPRESS Language])
|
|
15
|
+
* A text editor for creating files
|
|
16
|
+
|
|
17
|
+
=== Learning Objectives
|
|
18
|
+
|
|
19
|
+
By the end of this tutorial, you will be able to:
|
|
20
|
+
|
|
21
|
+
* Create a simple EXPRESS schema file
|
|
22
|
+
* Parse the schema using the Expressir CLI
|
|
23
|
+
* Parse the schema using the Ruby API
|
|
24
|
+
* Navigate the parsed data model
|
|
25
|
+
* Handle parsing errors gracefully
|
|
26
|
+
* Understand the structure of a parsed repository
|
|
27
|
+
|
|
28
|
+
=== What You'll Build
|
|
29
|
+
|
|
30
|
+
You'll create a simple EXPRESS schema representing a person and organization, then parse it with Expressir to explore the resulting Ruby data model.
|
|
31
|
+
|
|
32
|
+
=== Step 1: Create a Sample Schema
|
|
33
|
+
|
|
34
|
+
Create a file named `person_schema.exp` with the following content:
|
|
35
|
+
|
|
36
|
+
[source,express]
|
|
37
|
+
----
|
|
38
|
+
SCHEMA person_schema;
|
|
39
|
+
|
|
40
|
+
ENTITY person;
|
|
41
|
+
name : STRING;
|
|
42
|
+
age : INTEGER;
|
|
43
|
+
email : OPTIONAL STRING;
|
|
44
|
+
END_ENTITY;
|
|
45
|
+
|
|
46
|
+
ENTITY organization;
|
|
47
|
+
org_name : STRING;
|
|
48
|
+
employees : SET [0:?] OF person;
|
|
49
|
+
founded : INTEGER;
|
|
50
|
+
END_ENTITY;
|
|
51
|
+
|
|
52
|
+
TYPE person_list = LIST [1:?] OF person;
|
|
53
|
+
END_TYPE;
|
|
54
|
+
|
|
55
|
+
END_SCHEMA;
|
|
56
|
+
----
|
|
57
|
+
|
|
58
|
+
This schema defines:
|
|
59
|
+
|
|
60
|
+
* **person entity**: With name, age, and optional email
|
|
61
|
+
* **organization entity**: With name, employees (set of persons), and founding year
|
|
62
|
+
* **person_list type**: A list of at least one person
|
|
63
|
+
|
|
64
|
+
=== Step 2: Parse with CLI
|
|
65
|
+
|
|
66
|
+
The simplest way to parse is using the command line.
|
|
67
|
+
|
|
68
|
+
==== Format the Schema
|
|
69
|
+
|
|
70
|
+
First, verify the schema is valid by formatting it:
|
|
71
|
+
|
|
72
|
+
[source,bash]
|
|
73
|
+
----
|
|
74
|
+
expressir format person_schema.exp
|
|
75
|
+
----
|
|
76
|
+
|
|
77
|
+
**Expected output**: The schema printed to stdout with consistent formatting.
|
|
78
|
+
|
|
79
|
+
If there are syntax errors, Expressir will report them with line numbers.
|
|
80
|
+
|
|
81
|
+
==== Validate the Schema
|
|
82
|
+
|
|
83
|
+
Check if the schema meets validation criteria:
|
|
84
|
+
|
|
85
|
+
[source,bash]
|
|
86
|
+
----
|
|
87
|
+
expressir validate person_schema.exp
|
|
88
|
+
----
|
|
89
|
+
|
|
90
|
+
**Expected o
|
|
91
|
+
|
|
92
|
+
utput**:
|
|
93
|
+
[source]
|
|
94
|
+
----
|
|
95
|
+
Validation passed for all EXPRESS schemas.
|
|
96
|
+
----
|
|
97
|
+
|
|
98
|
+
==== What Happened?
|
|
99
|
+
|
|
100
|
+
The CLI:
|
|
101
|
+
|
|
102
|
+
1. Read the file `person_schema.exp`
|
|
103
|
+
2. Parsed the EXPRESS text into an Abstract Syntax Tree (AST)
|
|
104
|
+
3. Transformed the AST into Expressir's Ruby data model
|
|
105
|
+
4. Resolved all references within the schema
|
|
106
|
+
5. Formatted or validated the result
|
|
107
|
+
|
|
108
|
+
=== Step 3: Parse with Ruby API
|
|
109
|
+
|
|
110
|
+
Now let's parse programmatically using Ruby.
|
|
111
|
+
|
|
112
|
+
==== Basic Parsing
|
|
113
|
+
|
|
114
|
+
Create a file named `parse_person.rb`:
|
|
115
|
+
|
|
116
|
+
[source,ruby]
|
|
117
|
+
----
|
|
118
|
+
require 'expressir'
|
|
119
|
+
|
|
120
|
+
# Parse the schema file
|
|
121
|
+
repository = Expressir::Express::Parser.from_file('person_schema.exp')
|
|
122
|
+
|
|
123
|
+
# Access the repository
|
|
124
|
+
puts "Parsed successfully!"
|
|
125
|
+
puts "Number of schemas: #{repository.schemas.size}"
|
|
126
|
+
|
|
127
|
+
# Get the first (and only) schema
|
|
128
|
+
schema = repository.schemas.first
|
|
129
|
+
puts "Schema name: #{schema.id}"
|
|
130
|
+
puts "Schema file: #{schema.file}"
|
|
131
|
+
----
|
|
132
|
+
|
|
133
|
+
Run it:
|
|
134
|
+
|
|
135
|
+
[source,bash]
|
|
136
|
+
----
|
|
137
|
+
ruby parse_person.rb
|
|
138
|
+
----
|
|
139
|
+
|
|
140
|
+
**Expected output**:
|
|
141
|
+
[source]
|
|
142
|
+
----
|
|
143
|
+
Parsed successfully!
|
|
144
|
+
Number of schemas: 1
|
|
145
|
+
Schema name: person_schema
|
|
146
|
+
Schema file: person_schema.exp
|
|
147
|
+
----
|
|
148
|
+
|
|
149
|
+
==== Understanding the Result
|
|
150
|
+
|
|
151
|
+
The [`from_file`](../../lib/expressir/express/parser.rb:605) method returns a [`Repository`](../../lib/expressir/model/repository.rb:10) object containing:
|
|
152
|
+
|
|
153
|
+
* **schemas**: Array of [`Schema`](../../lib/expressir/model/declarations/schema.rb:6) objects
|
|
154
|
+
* **Indexes**: Built automatically for fast lookups
|
|
155
|
+
|
|
156
|
+
Each [`Schema`](../../lib/expressir/model/declarations/schema.rb:6) contains:
|
|
157
|
+
|
|
158
|
+
* **id**: Schema name
|
|
159
|
+
* **file**: Source file path
|
|
160
|
+
* **entities**: Array of entity definitions
|
|
161
|
+
* **types**: Array of type definitions
|
|
162
|
+
* **functions**, **procedures**, **rules**: Other declarations
|
|
163
|
+
|
|
164
|
+
=== Step 4: Explore the Parsed Model
|
|
165
|
+
|
|
166
|
+
Now let's explore what was parsed.
|
|
167
|
+
|
|
168
|
+
==== List Entities
|
|
169
|
+
|
|
170
|
+
Add to `parse_person.rb`:
|
|
171
|
+
|
|
172
|
+
[source,ruby]
|
|
173
|
+
----
|
|
174
|
+
# List all entities
|
|
175
|
+
puts "\nEntities:"
|
|
176
|
+
schema.entities.each do |entity|
|
|
177
|
+
puts " - #{entity.id}"
|
|
178
|
+
|
|
179
|
+
# List entity attributes
|
|
180
|
+
entity.attributes.each do |attr|
|
|
181
|
+
optional = attr.optional ? " (optional)" : ""
|
|
182
|
+
puts " * #{attr.id}: #{attr.type}#{optional}"
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
----
|
|
186
|
+
|
|
187
|
+
**Output**:
|
|
188
|
+
[source]
|
|
189
|
+
----
|
|
190
|
+
Entities:
|
|
191
|
+
- person
|
|
192
|
+
* name: STRING
|
|
193
|
+
* age: INTEGER
|
|
194
|
+
* email: STRING (optional)
|
|
195
|
+
- organization
|
|
196
|
+
* org_name: STRING
|
|
197
|
+
* employees: SET [0:?] OF person
|
|
198
|
+
* founded: INTEGER
|
|
199
|
+
----
|
|
200
|
+
|
|
201
|
+
==== List Types
|
|
202
|
+
|
|
203
|
+
Add to `parse_person.rb`:
|
|
204
|
+
|
|
205
|
+
[source,ruby]
|
|
206
|
+
----
|
|
207
|
+
# List all types
|
|
208
|
+
puts "\nTypes:"
|
|
209
|
+
schema.types.each do |type|
|
|
210
|
+
puts " - #{type.id}: #{type.underlying_type}"
|
|
211
|
+
end
|
|
212
|
+
----
|
|
213
|
+
|
|
214
|
+
**Output**:
|
|
215
|
+
[source]
|
|
216
|
+
----
|
|
217
|
+
Types:
|
|
218
|
+
- person_list: LIST [1:?] OF person
|
|
219
|
+
----
|
|
220
|
+
|
|
221
|
+
==== Access Specific Elements
|
|
222
|
+
|
|
223
|
+
Add to `parse_person.rb`:
|
|
224
|
+
|
|
225
|
+
[source,ruby]
|
|
226
|
+
----
|
|
227
|
+
# Find a specific entity
|
|
228
|
+
person_entity = schema.entities.find { |e| e.id == "person" }
|
|
229
|
+
if person_entity
|
|
230
|
+
puts "\nFound person entity with #{person_entity.attributes.size} attributes"
|
|
231
|
+
|
|
232
|
+
# Access individual attributes
|
|
233
|
+
name_attr = person_entity.attributes.find { |a| a.id == "name" }
|
|
234
|
+
puts "Name attribute type: #{name_attr.type}"
|
|
235
|
+
end
|
|
236
|
+
----
|
|
237
|
+
|
|
238
|
+
**Output**:
|
|
239
|
+
[source]
|
|
240
|
+
----
|
|
241
|
+
Found person entity with 3 attributes
|
|
242
|
+
Name attribute type: STRING
|
|
243
|
+
----
|
|
244
|
+
|
|
245
|
+
=== Step 5: Handle Parsing Errors
|
|
246
|
+
|
|
247
|
+
Errors happen. Let's learn to handle them gracefully.
|
|
248
|
+
|
|
249
|
+
==== Create an Invalid Schema
|
|
250
|
+
|
|
251
|
+
Create `invalid_schema.exp`:
|
|
252
|
+
|
|
253
|
+
[source,express]
|
|
254
|
+
----
|
|
255
|
+
SCHEMA invalid_schema;
|
|
256
|
+
|
|
257
|
+
ENTITY person
|
|
258
|
+
name : STRING; -- Missing semicolon after person
|
|
259
|
+
END_ENTITY;
|
|
260
|
+
|
|
261
|
+
END_SCHEMA;
|
|
262
|
+
----
|
|
263
|
+
|
|
264
|
+
==== Catch and Handle Errors
|
|
265
|
+
|
|
266
|
+
Create `handle_errors.rb`:
|
|
267
|
+
|
|
268
|
+
[source,ruby]
|
|
269
|
+
----
|
|
270
|
+
require 'expressir'
|
|
271
|
+
|
|
272
|
+
begin
|
|
273
|
+
repository = Expressir::Express::Parser.from_file('invalid_schema.exp')
|
|
274
|
+
puts "Parsing succeeded!"
|
|
275
|
+
rescue Expressir::Express::Error::SchemaParseFailure => e
|
|
276
|
+
puts "❌ Parsing failed!"
|
|
277
|
+
puts "\nFile: #{e.filename}"
|
|
278
|
+
puts "\nError message:"
|
|
279
|
+
puts e.message
|
|
280
|
+
puts "\nDetailed parse tree:"
|
|
281
|
+
puts e.parse_failure_cause.ascii_tree
|
|
282
|
+
end
|
|
283
|
+
----
|
|
284
|
+
|
|
285
|
+
Run it:
|
|
286
|
+
|
|
287
|
+
[source,bash]
|
|
288
|
+
----
|
|
289
|
+
ruby handle_errors.rb
|
|
290
|
+
----
|
|
291
|
+
|
|
292
|
+
**Expected output**:
|
|
293
|
+
[source]
|
|
294
|
+
----
|
|
295
|
+
❌ Parsing failed!
|
|
296
|
+
|
|
297
|
+
File: invalid_schema.exp
|
|
298
|
+
|
|
299
|
+
Error message:
|
|
300
|
+
Failed to parse invalid_schema.exp
|
|
301
|
+
|
|
302
|
+
Detailed parse tree:
|
|
303
|
+
[Shows detailed error location with line/column]
|
|
304
|
+
----
|
|
305
|
+
|
|
306
|
+
==== Common Parsing Errors
|
|
307
|
+
|
|
308
|
+
[options="header"]
|
|
309
|
+
|===
|
|
310
|
+
| Error | Cause | Solution
|
|
311
|
+
| "Expected ';'" | Missing semicolon | Add semicolon after declaration
|
|
312
|
+
| "Expected identifier" | Invalid name | Use valid identifier (letters, numbers, underscore)
|
|
313
|
+
| "Unexpected keyword" | Misused keyword | Check EXPRESS syntax reference
|
|
314
|
+
| "Expected 'END_ENTITY'" | Mismatched END | Ensure END matches opening keyword
|
|
315
|
+
|===
|
|
316
|
+
|
|
317
|
+
=== Step 6: Working with Parsed Data
|
|
318
|
+
|
|
319
|
+
Let's do something useful with the parsed schema.
|
|
320
|
+
|
|
321
|
+
==== Generate a Summary Report
|
|
322
|
+
|
|
323
|
+
Create `schema_summary.rb`:
|
|
324
|
+
|
|
325
|
+
[source,ruby]
|
|
326
|
+
----
|
|
327
|
+
require 'expressir'
|
|
328
|
+
|
|
329
|
+
# Parse the schema
|
|
330
|
+
repo = Expressir::Express::Parser.from_file('person_schema.exp')
|
|
331
|
+
schema = repo.schemas.first
|
|
332
|
+
|
|
333
|
+
# Generate summary
|
|
334
|
+
puts "=" * 60
|
|
335
|
+
puts "Schema Summary: #{schema.id}"
|
|
336
|
+
puts "=" * 60
|
|
337
|
+
|
|
338
|
+
# Count elements
|
|
339
|
+
puts "\nStatistics:"
|
|
340
|
+
puts " Entities: #{schema.entities.size}"
|
|
341
|
+
puts " Types: #{schema.types.size}"
|
|
342
|
+
puts " Total attributes: #{schema.entities.sum { |e| e.attributes.size }}"
|
|
343
|
+
|
|
344
|
+
# Detailed entity report
|
|
345
|
+
puts "\nEntities:"
|
|
346
|
+
schema.entities.each do |entity|
|
|
347
|
+
attr_count = entity.attributes.size
|
|
348
|
+
optional_count = entity.attributes.count(&:optional)
|
|
349
|
+
|
|
350
|
+
puts "\n #{entity.id}:"
|
|
351
|
+
puts " Total attributes: #{attr_count}"
|
|
352
|
+
puts " Optional attributes: #{optional_count}"
|
|
353
|
+
puts " Required attributes: #{attr_count - optional_count}"
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# Type report
|
|
357
|
+
puts "\nType Definitions:"
|
|
358
|
+
schema.types.each do |type|
|
|
359
|
+
puts " #{type.id} -> #{type.underlying_type}"
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
puts "\n" + "=" * 60
|
|
363
|
+
----
|
|
364
|
+
|
|
365
|
+
Run it:
|
|
366
|
+
|
|
367
|
+
[source,bash]
|
|
368
|
+
----
|
|
369
|
+
ruby schema_summary.rb
|
|
370
|
+
----
|
|
371
|
+
|
|
372
|
+
**Expected output**:
|
|
373
|
+
[source]
|
|
374
|
+
----
|
|
375
|
+
============================================================
|
|
376
|
+
Schema Summary: person_schema
|
|
377
|
+
============================================================
|
|
378
|
+
|
|
379
|
+
Statistics:
|
|
380
|
+
Entities: 2
|
|
381
|
+
Types: 1
|
|
382
|
+
Total attributes: 6
|
|
383
|
+
|
|
384
|
+
Entities:
|
|
385
|
+
|
|
386
|
+
person:
|
|
387
|
+
Total attributes: 3
|
|
388
|
+
Optional attributes: 1
|
|
389
|
+
Required attributes: 2
|
|
390
|
+
|
|
391
|
+
organization:
|
|
392
|
+
Total attributes: 3
|
|
393
|
+
Optional attributes: 0
|
|
394
|
+
Required attributes: 3
|
|
395
|
+
|
|
396
|
+
Type Definitions:
|
|
397
|
+
person_list -> LIST [1:?] OF person
|
|
398
|
+
|
|
399
|
+
============================================================
|
|
400
|
+
----
|
|
401
|
+
|
|
402
|
+
=== Step 7: Practice Exercises
|
|
403
|
+
|
|
404
|
+
Now it's your turn! Try these exercises to reinforce your learning.
|
|
405
|
+
|
|
406
|
+
==== Exercise 1: Add More Entities
|
|
407
|
+
|
|
408
|
+
Modify `person_schema.exp` to add:
|
|
409
|
+
|
|
410
|
+
* A `project` entity with name and deadline attributes
|
|
411
|
+
* A `team` entity that references persons and projects
|
|
412
|
+
|
|
413
|
+
Parse it and verify:
|
|
414
|
+
* The new entities appear in the entity list
|
|
415
|
+
* The attributes are correctly parsed
|
|
416
|
+
* References between entities work
|
|
417
|
+
|
|
418
|
+
==== Exercise 2: Parse Multiple Files
|
|
419
|
+
|
|
420
|
+
Create two schema files:
|
|
421
|
+
|
|
422
|
+
* `base_schema.exp` - With basic entities
|
|
423
|
+
* `extended_schema.exp` - That uses entities from base_schema (via USE FROM)
|
|
424
|
+
|
|
425
|
+
Parse both files together:
|
|
426
|
+
|
|
427
|
+
[source,ruby]
|
|
428
|
+
----
|
|
429
|
+
files = ['base_schema.exp', 'extended_schema.exp']
|
|
430
|
+
repo = Expressir::Express::Parser.from_files(files)
|
|
431
|
+
puts "Parsed #{repo.schemas.size} schemas"
|
|
432
|
+
----
|
|
433
|
+
|
|
434
|
+
==== Exercise 3: Type Exploration
|
|
435
|
+
|
|
436
|
+
Create a schema with various type definitions:
|
|
437
|
+
|
|
438
|
+
* ENUMERATION type (e.g., status: active, inactive, pending)
|
|
439
|
+
* SELECT type (union of multiple types)
|
|
440
|
+
* Aggregate type (ARRAY, LIST, SET, BAG)
|
|
441
|
+
|
|
442
|
+
Parse and identify the type of each TYPE definition.
|
|
443
|
+
|
|
444
|
+
==== Exercise 4: Error Recovery
|
|
445
|
+
|
|
446
|
+
Create intentionally broken schemas with different errors:
|
|
447
|
+
|
|
448
|
+
* Missing END_ENTITY
|
|
449
|
+
* Invalid attribute type
|
|
450
|
+
* Circular reference
|
|
451
|
+
|
|
452
|
+
Practice catching and logging each error type appropriately.
|
|
453
|
+
|
|
454
|
+
=== Common Pitfalls
|
|
455
|
+
|
|
456
|
+
==== Forgetting Reference Resolution
|
|
457
|
+
|
|
458
|
+
[source,ruby]
|
|
459
|
+
----
|
|
460
|
+
# ❌ References not resolved
|
|
461
|
+
repo = Expressir::Express::Parser.from_file('schema.exp', skip_references: true)
|
|
462
|
+
entity.attributes.first.type.ref # => nil (not resolved!)
|
|
463
|
+
|
|
464
|
+
# ✅ References resolved (default)
|
|
465
|
+
repo = Expressir::Express::Parser.from_file('schema.exp')
|
|
466
|
+
entity.attributes.first.type.ref # => Points to actual type
|
|
467
|
+
----
|
|
468
|
+
|
|
469
|
+
==== Not Handling Parse Errors
|
|
470
|
+
|
|
471
|
+
[source,ruby]
|
|
472
|
+
----
|
|
473
|
+
# ❌ Unhandled errors crash program
|
|
474
|
+
repo = Expressir::Express::Parser.from_file('might-be-invalid.exp')
|
|
475
|
+
|
|
476
|
+
# ✅ Graceful error handling
|
|
477
|
+
begin
|
|
478
|
+
repo = Expressir::Express::Parser.from_file('might-be-invalid.exp')
|
|
479
|
+
rescue Expressir::Express::Error::SchemaParseFailure => e
|
|
480
|
+
warn "Skipping invalid schema: #{e.filename}"
|
|
481
|
+
end
|
|
482
|
+
----
|
|
483
|
+
|
|
484
|
+
==== Assuming Single Schema
|
|
485
|
+
|
|
486
|
+
[source,ruby]
|
|
487
|
+
----
|
|
488
|
+
# ❌ Assuming first schema
|
|
489
|
+
schema = repo.schemas.first # Might be wrong file!
|
|
490
|
+
|
|
491
|
+
# ✅ Finding correct schema
|
|
492
|
+
schema = repo.schemas.find { |s| s.id == "person_schema" }
|
|
493
|
+
----
|
|
494
|
+
|
|
495
|
+
=== Next Steps
|
|
496
|
+
|
|
497
|
+
Congratulations! You've learned to parse EXPRESS schemas with Expressir.
|
|
498
|
+
|
|
499
|
+
**Continue learning**:
|
|
500
|
+
|
|
501
|
+
* link:working-with-multiple-schemas.html[Working with Multiple Schemas] - Handle dependencies and interfaces
|
|
502
|
+
* link:querying-schemas.html[Querying Schemas] - Find and filter entities/types
|
|
503
|
+
* link:creating-ler-package.html[Creating LER Packages] - Package for performance
|
|
504
|
+
|
|
505
|
+
**Read more**:
|
|
506
|
+
|
|
507
|
+
* link:../pages/parsers.html[Parsers] - Deep dive into parsing architecture
|
|
508
|
+
* link:../pages/data-model.html[Data Model] - Understanding the Ruby model
|
|
509
|
+
* link:../guides/ruby-api/[Ruby API Guides] - Advanced programmatic usage
|
|
510
|
+
|
|
511
|
+
=== Summary
|
|
512
|
+
|
|
513
|
+
In this tutorial, you learned to:
|
|
514
|
+
|
|
515
|
+
* ✅ Create valid EXPRESS schema files
|
|
516
|
+
* ✅ Parse schemas using CLI and Ruby API
|
|
517
|
+
* ✅ Navigate the parsed data model
|
|
518
|
+
* ✅ Access entities, types, and attributes
|
|
519
|
+
* ✅ Handle parsing errors gracefully
|
|
520
|
+
* ✅ Generate reports from parsed schemas
|
|
521
|
+
|
|
522
|
+
You're now ready to work with more complex schemas and explore advanced Expressir features!
|