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,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Ruby API
|
|
3
|
+
nav_order: 2
|
|
4
|
+
has_children: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
= Ruby API
|
|
8
|
+
|
|
9
|
+
== Purpose
|
|
10
|
+
|
|
11
|
+
The Expressir Ruby API provides programmatic access to EXPRESS schema
|
|
12
|
+
parsing, analysis, and manipulation. This section covers the core APIs for
|
|
13
|
+
working with EXPRESS models in Ruby applications.
|
|
14
|
+
|
|
15
|
+
== When to use the API vs CLI
|
|
16
|
+
|
|
17
|
+
Use the Ruby API when:
|
|
18
|
+
|
|
19
|
+
* Integrating Expressir into your Ruby application
|
|
20
|
+
* Building custom tools or workflows
|
|
21
|
+
* Performing programmatic schema analysis
|
|
22
|
+
* Creating automated processing pipelines
|
|
23
|
+
* Generating custom reports or documentation
|
|
24
|
+
|
|
25
|
+
Use the CLI when:
|
|
26
|
+
|
|
27
|
+
* Performing one-off operations
|
|
28
|
+
* Quick validation or formatting tasks
|
|
29
|
+
* Command-line scripting
|
|
30
|
+
* CI/CD pipeline integration
|
|
31
|
+
* Interactive exploration of schemas
|
|
32
|
+
|
|
33
|
+
== Installation
|
|
34
|
+
|
|
35
|
+
Add Expressir to your application's `Gemfile`:
|
|
36
|
+
|
|
37
|
+
[source,ruby]
|
|
38
|
+
----
|
|
39
|
+
gem "expressir", "~> 0.2"
|
|
40
|
+
----
|
|
41
|
+
|
|
42
|
+
Then execute:
|
|
43
|
+
|
|
44
|
+
[source,bash]
|
|
45
|
+
----
|
|
46
|
+
bundle install
|
|
47
|
+
----
|
|
48
|
+
|
|
49
|
+
Or install directly:
|
|
50
|
+
|
|
51
|
+
[source,bash]
|
|
52
|
+
----
|
|
53
|
+
gem install expressir
|
|
54
|
+
----
|
|
55
|
+
|
|
56
|
+
== Basic usage
|
|
57
|
+
|
|
58
|
+
.Parsing a schema and accessing its contents
|
|
59
|
+
[example]
|
|
60
|
+
====
|
|
61
|
+
[source,ruby]
|
|
62
|
+
----
|
|
63
|
+
require "expressir"
|
|
64
|
+
|
|
65
|
+
# Parse a schema file
|
|
66
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
67
|
+
|
|
68
|
+
# Access schemas and entities
|
|
69
|
+
repository.schemas.each do |schema|
|
|
70
|
+
puts "Schema: #{schema.id}"
|
|
71
|
+
|
|
72
|
+
schema.entities&.each do |entity|
|
|
73
|
+
puts " Entity: #{entity.id}"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
----
|
|
77
|
+
====
|
|
78
|
+
|
|
79
|
+
== Core API components
|
|
80
|
+
|
|
81
|
+
The Expressir Ruby API consists of several key components:
|
|
82
|
+
|
|
83
|
+
=== Parser API
|
|
84
|
+
|
|
85
|
+
The link:parsing-files.html[Parser] provides methods for reading and parsing
|
|
86
|
+
EXPRESS schema files into Ruby model objects.
|
|
87
|
+
|
|
88
|
+
Key classes:
|
|
89
|
+
|
|
90
|
+
* `Expressir::Express::Parser` - Main parser interface
|
|
91
|
+
* `Expressir::Express::Error::SchemaParseFailure` - Parse error handling
|
|
92
|
+
|
|
93
|
+
=== Repository API
|
|
94
|
+
|
|
95
|
+
The link:working-with-repository.html[Repository] represents a collection of
|
|
96
|
+
schemas with methods for querying, validation, and export.
|
|
97
|
+
|
|
98
|
+
Key classes:
|
|
99
|
+
|
|
100
|
+
* `Expressir::Model::Repository` - Schema collection container
|
|
101
|
+
* `Expressir::Model::Declarations::Schema` - Individual schema representation
|
|
102
|
+
|
|
103
|
+
=== SearchEngine API
|
|
104
|
+
|
|
105
|
+
The link:search-engine.html[SearchEngine] enables powerful querying and
|
|
106
|
+
filtering of EXPRESS elements within a repository.
|
|
107
|
+
|
|
108
|
+
Key classes:
|
|
109
|
+
|
|
110
|
+
* `Expressir::Model::SearchEngine` - Main search interface
|
|
111
|
+
* Pattern matching and type filtering capabilities
|
|
112
|
+
|
|
113
|
+
=== Formatter API
|
|
114
|
+
|
|
115
|
+
The link:formatting-schemas.html[Formatter] converts EXPRESS models back to
|
|
116
|
+
formatted EXPRESS language text.
|
|
117
|
+
|
|
118
|
+
Key classes:
|
|
119
|
+
|
|
120
|
+
* `Expressir::Express::Formatter` - Main formatting interface
|
|
121
|
+
* `Expressir::Express::HyperLinkFormatter` - HTML hyperlink generation
|
|
122
|
+
|
|
123
|
+
== Common patterns
|
|
124
|
+
|
|
125
|
+
=== Parse and validate
|
|
126
|
+
|
|
127
|
+
[source,ruby]
|
|
128
|
+
----
|
|
129
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
130
|
+
|
|
131
|
+
# Validate the repository
|
|
132
|
+
result = repository.validate(strict: true)
|
|
133
|
+
|
|
134
|
+
if result[:valid?]
|
|
135
|
+
puts "Schema is valid"
|
|
136
|
+
else
|
|
137
|
+
puts "Validation errors:"
|
|
138
|
+
result[:errors].each { |err| puts " - #{err}" }
|
|
139
|
+
end
|
|
140
|
+
----
|
|
141
|
+
|
|
142
|
+
=== Search for entities
|
|
143
|
+
|
|
144
|
+
[source,ruby]
|
|
145
|
+
----
|
|
146
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
147
|
+
engine = Expressir::Model::SearchEngine.new(repository)
|
|
148
|
+
|
|
149
|
+
# Find all entities matching a pattern
|
|
150
|
+
results = engine.search(
|
|
151
|
+
pattern: "action*",
|
|
152
|
+
type: "entity",
|
|
153
|
+
case_sensitive: false
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
results.each do |result|
|
|
157
|
+
puts "Found: #{result[:path]}"
|
|
158
|
+
end
|
|
159
|
+
----
|
|
160
|
+
|
|
161
|
+
=== Format and output
|
|
162
|
+
|
|
163
|
+
[source,ruby]
|
|
164
|
+
----
|
|
165
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
166
|
+
formatter = Expressir::Express::Formatter.new(no_remarks: true)
|
|
167
|
+
|
|
168
|
+
# Format each schema
|
|
169
|
+
repository.schemas.each do |schema|
|
|
170
|
+
output = formatter.format(schema)
|
|
171
|
+
File.write("formatted_#{schema.id}.exp", output)
|
|
172
|
+
end
|
|
173
|
+
----
|
|
174
|
+
|
|
175
|
+
== Error handling
|
|
176
|
+
|
|
177
|
+
Expressir provides specific exception classes for different error scenarios:
|
|
178
|
+
|
|
179
|
+
[source,ruby]
|
|
180
|
+
----
|
|
181
|
+
begin
|
|
182
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
183
|
+
rescue Expressir::Express::Error::SchemaParseFailure => e
|
|
184
|
+
puts "Parse error in #{e.filename}"
|
|
185
|
+
puts "Cause: #{e.parse_failure_cause.message}"
|
|
186
|
+
puts e.parse_failure_cause.ascii_tree
|
|
187
|
+
rescue Errno::ENOENT => e
|
|
188
|
+
puts "File not found: #{e.message}"
|
|
189
|
+
end
|
|
190
|
+
----
|
|
191
|
+
|
|
192
|
+
== Performance considerations
|
|
193
|
+
|
|
194
|
+
=== Caching
|
|
195
|
+
|
|
196
|
+
For repeatedly parsing the same files, consider using caching:
|
|
197
|
+
|
|
198
|
+
[source,ruby]
|
|
199
|
+
----
|
|
200
|
+
# Enable caching for better performance
|
|
201
|
+
Expressir::Express::Cache.cache_path = ".cache"
|
|
202
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
203
|
+
----
|
|
204
|
+
|
|
205
|
+
=== Skipping references
|
|
206
|
+
|
|
207
|
+
When you don't need resolved references, skip them for better performance:
|
|
208
|
+
|
|
209
|
+
[source,ruby]
|
|
210
|
+
----
|
|
211
|
+
repository = Expressir::Express::Parser.from_file(
|
|
212
|
+
"schema.exp",
|
|
213
|
+
skip_references: true
|
|
214
|
+
)
|
|
215
|
+
----
|
|
216
|
+
|
|
217
|
+
=== Batch processing
|
|
218
|
+
|
|
219
|
+
Use `from_files` with progress tracking for processing multiple schemas:
|
|
220
|
+
|
|
221
|
+
[source,ruby]
|
|
222
|
+
----
|
|
223
|
+
files = Dir.glob("schemas/**/*.exp")
|
|
224
|
+
|
|
225
|
+
repository = Expressir::Express::Parser.from_files(files) do |file, schemas, error|
|
|
226
|
+
if error
|
|
227
|
+
warn "Error parsing #{file}: #{error.message}"
|
|
228
|
+
else
|
|
229
|
+
puts "Loaded #{schemas.size} schemas from #{file}"
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
----
|
|
233
|
+
|
|
234
|
+
== Next steps
|
|
235
|
+
|
|
236
|
+
Explore the detailed guides for each API component:
|
|
237
|
+
|
|
238
|
+
* link:parsing-files.html[Parsing Files] - Learn about the Parser API
|
|
239
|
+
* link:working-with-repository.html[Working with Repository] - Manage schema collections
|
|
240
|
+
* link:search-engine.html[Search Engine] - Query and filter schemas
|
|
241
|
+
* link:formatting-schemas.html[Formatting Schemas] - Generate EXPRESS output
|
|
242
|
+
|
|
243
|
+
== Summary
|
|
244
|
+
|
|
245
|
+
The Expressir Ruby API provides a comprehensive set of tools for working with
|
|
246
|
+
EXPRESS schemas programmatically. The API is designed to be intuitive and
|
|
247
|
+
follows Ruby best practices while providing powerful capabilities for schema
|
|
248
|
+
analysis and manipulation.
|
|
249
|
+
|
|
250
|
+
Key takeaways:
|
|
251
|
+
|
|
252
|
+
* Parser API handles all EXPRESS file parsing
|
|
253
|
+
* Repository API manages schema collections
|
|
254
|
+
* SearchEngine API enables powerful querying
|
|
255
|
+
* Formatter API generates EXPRESS output
|
|
256
|
+
* Proper error handling ensures robust applications
|
|
257
|
+
* Performance optimizations available for large schemas
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Parsing Files
|
|
3
|
+
parent: Ruby API
|
|
4
|
+
grand_parent: Guides
|
|
5
|
+
nav_order: 1
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
= Parsing Files
|
|
9
|
+
|
|
10
|
+
== Purpose
|
|
11
|
+
|
|
12
|
+
This guide covers using the Expressir Parser API to read and parse EXPRESS
|
|
13
|
+
schema files into Ruby model objects. The Parser handles all aspects of
|
|
14
|
+
EXPRESS language parsing and provides options for reference resolution and
|
|
15
|
+
source preservation.
|
|
16
|
+
|
|
17
|
+
== References
|
|
18
|
+
|
|
19
|
+
* link:../express-language.html[EXPRESS Language] - Understanding EXPRESS syntax
|
|
20
|
+
* link:working-with-repository.html[Working with Repository] - Using parsed results
|
|
21
|
+
* link:../../tutorials/parsing-your-first-schema.html[Tutorial: Parsing Your First Schema]
|
|
22
|
+
|
|
23
|
+
== Concepts
|
|
24
|
+
|
|
25
|
+
parser:: The component that reads EXPRESS files and converts them to Ruby models
|
|
26
|
+
repository:: The container object that holds all parsed schemas
|
|
27
|
+
reference_resolution:: The process of linking references between schemas
|
|
28
|
+
skip_references:: Option to disable reference resolution for faster parsing
|
|
29
|
+
include_source:: Option to preserve original source code in the model
|
|
30
|
+
|
|
31
|
+
== The Parser API
|
|
32
|
+
|
|
33
|
+
=== Main methods
|
|
34
|
+
|
|
35
|
+
The `Expressir::Express::Parser` class provides three main parsing methods:
|
|
36
|
+
|
|
37
|
+
`from_file(file, options)`:: Parse a single EXPRESS file
|
|
38
|
+
`from_files(files, options, &block)`:: Parse multiple EXPRESS files
|
|
39
|
+
`from_exp(content, options)`:: Parse EXPRESS content from a string
|
|
40
|
+
|
|
41
|
+
All methods return a `Expressir::Model::Repository` object containing the
|
|
42
|
+
parsed schemas.
|
|
43
|
+
|
|
44
|
+
== Parsing a single file
|
|
45
|
+
|
|
46
|
+
=== Basic usage
|
|
47
|
+
|
|
48
|
+
[source,ruby]
|
|
49
|
+
----
|
|
50
|
+
require "expressir"
|
|
51
|
+
|
|
52
|
+
# Parse a single schema file
|
|
53
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
54
|
+
|
|
55
|
+
# Access the parsed schemas
|
|
56
|
+
repository.schemas.each do |schema|
|
|
57
|
+
puts "Schema: #{schema.id}"
|
|
58
|
+
puts "Version: #{schema.version&.value}"
|
|
59
|
+
end
|
|
60
|
+
----
|
|
61
|
+
|
|
62
|
+
=== With options
|
|
63
|
+
|
|
64
|
+
The parser accepts several options to control parsing behavior:
|
|
65
|
+
|
|
66
|
+
[source,ruby]
|
|
67
|
+
----
|
|
68
|
+
repository = Expressir::Express::Parser.from_file(
|
|
69
|
+
"schema.exp",
|
|
70
|
+
skip_references: false, # Resolve references between schemas
|
|
71
|
+
include_source: true, # Preserve original source in model
|
|
72
|
+
root_path: "/base/path" # Base path for relative file paths
|
|
73
|
+
)
|
|
74
|
+
----
|
|
75
|
+
|
|
76
|
+
==== Option: skip_references
|
|
77
|
+
|
|
78
|
+
When `skip_references` is `false` (default), the parser resolves all
|
|
79
|
+
references between schemas, entities, and types. This connects
|
|
80
|
+
`USE FROM` and `REFERENCE FROM` statements to their targets.
|
|
81
|
+
|
|
82
|
+
Set to `true` for faster parsing when you don't need resolved references:
|
|
83
|
+
|
|
84
|
+
[source,ruby]
|
|
85
|
+
----
|
|
86
|
+
# Faster parsing without reference resolution
|
|
87
|
+
repository = Expressir::Express::Parser.from_file(
|
|
88
|
+
"schema.exp",
|
|
89
|
+
skip_references: true
|
|
90
|
+
)
|
|
91
|
+
----
|
|
92
|
+
|
|
93
|
+
==== Option: include_source
|
|
94
|
+
|
|
95
|
+
When `include_source` is `true`, the parser preserves the original source
|
|
96
|
+
code text in model element objects. This is useful for:
|
|
97
|
+
|
|
98
|
+
* Generating documentation with original formatting
|
|
99
|
+
* Preserving comments and remarks
|
|
100
|
+
* Source code analysis
|
|
101
|
+
|
|
102
|
+
[source,ruby]
|
|
103
|
+
----
|
|
104
|
+
repository = Expressir::Express::Parser.from_file(
|
|
105
|
+
"schema.exp",
|
|
106
|
+
include_source: true
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Access original source
|
|
110
|
+
schema = repository.schemas.first
|
|
111
|
+
puts schema.source if schema.respond_to?(:source)
|
|
112
|
+
----
|
|
113
|
+
|
|
114
|
+
==== Option: root_path
|
|
115
|
+
|
|
116
|
+
The `root_path` option sets a base directory for resolving relative file
|
|
117
|
+
paths in the model:
|
|
118
|
+
|
|
119
|
+
[source,ruby]
|
|
120
|
+
----
|
|
121
|
+
repository = Expressir::Express::Parser.from_file(
|
|
122
|
+
"/full/path/to/schema.exp",
|
|
123
|
+
root_path: "/full/path/to"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Schema file will be stored as relative path: "schema.exp"
|
|
127
|
+
puts repository.schemas.first.file # => "schema.exp"
|
|
128
|
+
----
|
|
129
|
+
|
|
130
|
+
== Parsing multiple files
|
|
131
|
+
|
|
132
|
+
=== Basic usage
|
|
133
|
+
|
|
134
|
+
Use `from_files` to parse multiple EXPRESS files in one operation:
|
|
135
|
+
|
|
136
|
+
[source,ruby]
|
|
137
|
+
----
|
|
138
|
+
files = [
|
|
139
|
+
"schema1.exp",
|
|
140
|
+
"schema2.exp",
|
|
141
|
+
"schema3.exp"
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
repository = Expressir::Express::Parser.from_files(files)
|
|
145
|
+
|
|
146
|
+
# All schemas are in one repository
|
|
147
|
+
puts "Loaded #{repository.schemas.size} schemas"
|
|
148
|
+
----
|
|
149
|
+
|
|
150
|
+
=== With progress tracking
|
|
151
|
+
|
|
152
|
+
Provide a block to track parsing progress and handle errors:
|
|
153
|
+
|
|
154
|
+
[source,ruby]
|
|
155
|
+
----
|
|
156
|
+
files = Dir.glob("schemas/**/*.exp")
|
|
157
|
+
|
|
158
|
+
repository = Expressir::Express::Parser.from_files(files) do |filename, schemas, error|
|
|
159
|
+
if error
|
|
160
|
+
warn "Failed to parse #{filename}"
|
|
161
|
+
warn " Error: #{error.message}"
|
|
162
|
+
# Continue with remaining files
|
|
163
|
+
else
|
|
164
|
+
puts "✓ Loaded #{schemas.size} schemas from #{filename}"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
puts "Total schemas loaded: #{repository.schemas.size}"
|
|
169
|
+
----
|
|
170
|
+
|
|
171
|
+
The block receives three parameters:
|
|
172
|
+
|
|
173
|
+
`filename`:: The file being processed
|
|
174
|
+
`schemas`:: Array of schemas parsed from the file (nil if error)
|
|
175
|
+
`error`:: Exception that occurred (nil if successful)
|
|
176
|
+
|
|
177
|
+
=== Error handling behavior
|
|
178
|
+
|
|
179
|
+
When parsing multiple files:
|
|
180
|
+
|
|
181
|
+
* Individual file failures don't stop the entire operation
|
|
182
|
+
* `SchemaParseFailure` exceptions are caught and passed to the block
|
|
183
|
+
* Other exceptions are re-raised
|
|
184
|
+
* Successfully parsed schemas are included in the final repository
|
|
185
|
+
|
|
186
|
+
[source,ruby]
|
|
187
|
+
----
|
|
188
|
+
successful = []
|
|
189
|
+
failed = []
|
|
190
|
+
|
|
191
|
+
repository = Expressir::Express::Parser.from_files(files) do |file, schemas, error|
|
|
192
|
+
if error && error.is_a?(Expressir::Express::Error::SchemaParseFailure)
|
|
193
|
+
failed << { file: file, error: error.message }
|
|
194
|
+
elsif schemas
|
|
195
|
+
successful << file
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
puts "Successfully parsed: #{successful.size}"
|
|
200
|
+
puts "Failed to parse: #{failed.size}"
|
|
201
|
+
----
|
|
202
|
+
|
|
203
|
+
== Parsing from strings
|
|
204
|
+
|
|
205
|
+
Parse EXPRESS content directly from a string:
|
|
206
|
+
|
|
207
|
+
[source,ruby]
|
|
208
|
+
----
|
|
209
|
+
express_code = <<~EXPRESS
|
|
210
|
+
SCHEMA example_schema;
|
|
211
|
+
ENTITY example_entity;
|
|
212
|
+
name : STRING;
|
|
213
|
+
END_ENTITY;
|
|
214
|
+
END_SCHEMA;
|
|
215
|
+
EXPRESS
|
|
216
|
+
|
|
217
|
+
repository = Expressir::Express::Parser.from_exp(express_code)
|
|
218
|
+
|
|
219
|
+
schema = repository.schemas.first
|
|
220
|
+
puts "Schema: #{schema.id}" # => "example_schema"
|
|
221
|
+
----
|
|
222
|
+
|
|
223
|
+
This is useful for:
|
|
224
|
+
|
|
225
|
+
* Testing and development
|
|
226
|
+
* Generating schemas programmatically
|
|
227
|
+
* Processing schemas from non-file sources
|
|
228
|
+
|
|
229
|
+
== Error handling
|
|
230
|
+
|
|
231
|
+
=== SchemaParseFailure exception
|
|
232
|
+
|
|
233
|
+
When parsing fails, the parser raises
|
|
234
|
+
`Expressir::Express::Error::SchemaParseFailure`:
|
|
235
|
+
|
|
236
|
+
[source,ruby]
|
|
237
|
+
----
|
|
238
|
+
begin
|
|
239
|
+
repository = Expressir::Express::Parser.from_file("bad_schema.exp")
|
|
240
|
+
rescue Expressir::Express::Error::SchemaParseFailure => e
|
|
241
|
+
puts "Parse failed: #{e.filename}"
|
|
242
|
+
puts "Root cause: #{e.parse_failure_cause.message}"
|
|
243
|
+
|
|
244
|
+
# Display parse tree for debugging
|
|
245
|
+
puts "\nParse error details:"
|
|
246
|
+
puts e.parse_failure_cause.ascii_tree
|
|
247
|
+
end
|
|
248
|
+
----
|
|
249
|
+
|
|
250
|
+
The exception provides:
|
|
251
|
+
|
|
252
|
+
`filename`:: The file that failed to parse
|
|
253
|
+
`parse_failure_cause`:: The underlying Parslet parse error
|
|
254
|
+
`message`:: Human-readable error description
|
|
255
|
+
|
|
256
|
+
=== Other common errors
|
|
257
|
+
|
|
258
|
+
[source,ruby]
|
|
259
|
+
----
|
|
260
|
+
begin
|
|
261
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
262
|
+
rescue Expressir::Express::Error::SchemaParseFailure => e
|
|
263
|
+
# Handle parse errors
|
|
264
|
+
puts "Invalid EXPRESS syntax: #{e.message}"
|
|
265
|
+
rescue Errno::ENOENT => e
|
|
266
|
+
# Handle missing files
|
|
267
|
+
puts "File not found: #{e.message}"
|
|
268
|
+
rescue Errno::EACCES => e
|
|
269
|
+
# Handle permission errors
|
|
270
|
+
puts "Permission denied: #{e.message}"
|
|
271
|
+
rescue => e
|
|
272
|
+
# Handle unexpected errors
|
|
273
|
+
puts "Unexpected error: #{e.class.name} - #{e.message}"
|
|
274
|
+
end
|
|
275
|
+
----
|
|
276
|
+
|
|
277
|
+
== Performance considerations
|
|
278
|
+
|
|
279
|
+
=== Skip references for speed
|
|
280
|
+
|
|
281
|
+
Reference resolution can be time-consuming for large schema sets. Skip it
|
|
282
|
+
when not needed:
|
|
283
|
+
|
|
284
|
+
[source,ruby]
|
|
285
|
+
----
|
|
286
|
+
# Fast parsing without references
|
|
287
|
+
repository = Expressir::Express::Parser.from_file(
|
|
288
|
+
"large_schema.exp",
|
|
289
|
+
skip_references: true
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Resolve references later if needed
|
|
293
|
+
repository.resolve_all_references
|
|
294
|
+
----
|
|
295
|
+
|
|
296
|
+
=== Process files in parallel
|
|
297
|
+
|
|
298
|
+
For very large schema sets, consider parallel processing:
|
|
299
|
+
|
|
300
|
+
[source,ruby]
|
|
301
|
+
----
|
|
302
|
+
require "parallel"
|
|
303
|
+
|
|
304
|
+
files = Dir.glob("schemas/**/*.exp")
|
|
305
|
+
|
|
306
|
+
# Parse files in parallel
|
|
307
|
+
repositories = Parallel.map(files, in_threads: 4) do |file|
|
|
308
|
+
Expressir::Express::Parser.from_file(file, skip_references: true)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Combine into single repository
|
|
312
|
+
combined = Expressir::Model::Repository.new
|
|
313
|
+
repositories.each do |repo|
|
|
314
|
+
combined.schemas.concat(repo.schemas)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Resolve references once at the end
|
|
318
|
+
combined.resolve_all_references
|
|
319
|
+
----
|
|
320
|
+
|
|
321
|
+
=== Use caching for repeated operations
|
|
322
|
+
|
|
323
|
+
Enable caching for schemas that are parsed repeatedly:
|
|
324
|
+
|
|
325
|
+
[source,ruby]
|
|
326
|
+
----
|
|
327
|
+
# Set cache directory
|
|
328
|
+
Expressir::Express::Cache.cache_path = ".cache"
|
|
329
|
+
|
|
330
|
+
# First parse writes to cache
|
|
331
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
332
|
+
|
|
333
|
+
# Subsequent parses use cache (much faster)
|
|
334
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
335
|
+
----
|
|
336
|
+
|
|
337
|
+
== Working with parsed results
|
|
338
|
+
|
|
339
|
+
=== Accessing schemas
|
|
340
|
+
|
|
341
|
+
[source,ruby]
|
|
342
|
+
----
|
|
343
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
344
|
+
|
|
345
|
+
# Iterate through all schemas
|
|
346
|
+
repository.schemas.each do |schema|
|
|
347
|
+
puts "Schema: #{schema.id}"
|
|
348
|
+
puts "File: #{schema.file}"
|
|
349
|
+
puts "Version: #{schema.version&.value}"
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Find a specific schema
|
|
353
|
+
schema = repository.schemas.find { |s| s.id == "action_schema" }
|
|
354
|
+
----
|
|
355
|
+
|
|
356
|
+
=== Accessing entities and types
|
|
357
|
+
|
|
358
|
+
[source,ruby]
|
|
359
|
+
----
|
|
360
|
+
schema = repository.schemas.first
|
|
361
|
+
|
|
362
|
+
# Access entities
|
|
363
|
+
schema.entities&.each do |entity|
|
|
364
|
+
puts "Entity: #{entity.id}"
|
|
365
|
+
|
|
366
|
+
# Access attributes
|
|
367
|
+
entity.attributes&.each do |attr|
|
|
368
|
+
puts " Attribute: #{attr.id}"
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Access types
|
|
373
|
+
schema.types&.each do |type|
|
|
374
|
+
puts "Type: #{type.id}"
|
|
375
|
+
end
|
|
376
|
+
----
|
|
377
|
+
|
|
378
|
+
=== Checking parse results
|
|
379
|
+
|
|
380
|
+
[source,ruby]
|
|
381
|
+
----
|
|
382
|
+
repository = Expressir::Express::Parser.from_file("schema.exp")
|
|
383
|
+
|
|
384
|
+
puts "Schemas parsed: #{repository.schemas.size}"
|
|
385
|
+
puts "Total entities: #{repository.schemas.sum { |s| s.entities&.size || 0 }}"
|
|
386
|
+
puts "Total types: #{repository.schemas.sum { |s| s.types&.size || 0 }}"
|
|
387
|
+
|
|
388
|
+
# Validate completeness
|
|
389
|
+
repository.schemas.each do |schema|
|
|
390
|
+
if schema.entities&.empty? && schema.types&.empty?
|
|
391
|
+
warn "Warning: Schema #{schema.id} appears to be empty"
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
----
|
|
395
|
+
|
|
396
|
+
== Next steps
|
|
397
|
+
|
|
398
|
+
After parsing files, you'll want to:
|
|
399
|
+
|
|
400
|
+
* link:working-with-repository.html[Work with Repository] - Query and manipulate schemas
|
|
401
|
+
* link:search-engine.html[Use Search Engine] - Find specific elements
|
|
402
|
+
* link:formatting-schemas.html[Format Schemas] - Convert back to EXPRESS
|
|
403
|
+
|
|
404
|
+
== Summary
|
|
405
|
+
|
|
406
|
+
The Parser API provides flexible options for reading EXPRESS schemas:
|
|
407
|
+
|
|
408
|
+
* `from_file` for single files
|
|
409
|
+
* `from_files` for multiple files with progress tracking
|
|
410
|
+
* `from_exp` for string content
|
|
411
|
+
* Options control reference resolution and source preservation
|
|
412
|
+
* Robust error handling with detailed parse failure information
|
|
413
|
+
* Performance optimizations available for large schemas
|
|
414
|
+
|
|
415
|
+
Key takeaways:
|
|
416
|
+
|
|
417
|
+
* Always handle `SchemaParseFailure` exceptions
|
|
418
|
+
* Use `skip_references: true` for faster parsing when references aren't needed
|
|
419
|
+
* Track progress with blocks when parsing multiple files
|
|
420
|
+
* Consider caching for frequently-parsed schemas
|
|
421
|
+
* The parsed `Repository` contains all schemas and provides querying capabilities
|