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,525 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Model
5
+ # SearchEngine for querying EXPRESS elements in a repository
6
+ # Handles pattern matching, wildcards, regex, and element collection
7
+ class SearchEngine
8
+ # Element types that can be searched
9
+ ELEMENT_TYPES = %w[
10
+ schema entity type attribute derived_attribute inverse_attribute
11
+ function procedure rule constant parameter variable
12
+ where_rule unique_rule enumeration_item interface
13
+ ].freeze
14
+
15
+ # Type categories for filtering
16
+ TYPE_CATEGORIES = %w[select enumeration aggregate defined].freeze
17
+
18
+ attr_reader :repository
19
+
20
+ # Initialize search engine with a repository
21
+ # @param repository [Repository] Repository to search
22
+ def initialize(repository)
23
+ @repository = repository
24
+ @repository.build_indexes if @repository.entity_index.nil?
25
+ end
26
+
27
+ # List all elements of a specific type
28
+ # @param type [String] Element type to list
29
+ # @param schema [String, nil] Filter by schema name
30
+ # @param category [String, nil] Type category filter (for type elements)
31
+ # @return [Array<Hash>] List of elements
32
+ def list(type:, schema: nil, category: nil)
33
+ elements = collect_elements(type: type, schema: schema,
34
+ category: category)
35
+ elements.map { |elem| element_to_hash(elem, type) }
36
+ end
37
+
38
+ # Search for elements matching a pattern
39
+ # @param pattern [String] Search pattern
40
+ # @param type [String, nil] Filter by element type
41
+ # @param schema [String, nil] Limit to specific schema
42
+ # @param category [String, nil] Type category filter
43
+ # @param case_sensitive [Boolean] Enable case-sensitive matching
44
+ # @param regex [Boolean] Treat pattern as regex
45
+ # @param exact [Boolean] Exact match only
46
+ # @return [Array<Hash>] Matching elements
47
+ def search(pattern:, type: nil, schema: nil, category: nil,
48
+ case_sensitive: false, regex: false, exact: false)
49
+ # Parse pattern
50
+ pattern_parts = parse_pattern(pattern, case_sensitive: case_sensitive)
51
+
52
+ # Determine types to search
53
+ types_to_search = type ? [type] : ELEMENT_TYPES
54
+
55
+ # Collect all matching elements
56
+ results = []
57
+ types_to_search.each do |elem_type|
58
+ elements = collect_elements(type: elem_type, schema: schema,
59
+ category: category)
60
+
61
+ elements.each do |elem|
62
+ if matches_pattern?(elem, pattern_parts, elem_type,
63
+ case_sensitive: case_sensitive,
64
+ regex: regex, exact: exact)
65
+ results << element_to_hash(elem, elem_type)
66
+ end
67
+ end
68
+ end
69
+
70
+ results
71
+ end
72
+
73
+ # Count elements matching criteria
74
+ # @param type [String] Element type
75
+ # @param schema [String, nil] Filter by schema
76
+ # @param category [String, nil] Type category filter
77
+ # @return [Integer] Count of elements
78
+ def count(type:, schema: nil, category: nil)
79
+ collect_elements(type: type, schema: schema, category: category).size
80
+ end
81
+
82
+ # Search with depth filtering
83
+ # @param pattern [String] Search pattern
84
+ # @param max_depth [Integer] Maximum path depth (1=schema, 2=entity, 3=attribute)
85
+ # @param options [Hash] Additional search options
86
+ # @return [Array<Hash>] Filtered results
87
+ def search_with_depth(pattern:, max_depth:, **options)
88
+ return [] if max_depth <= 0
89
+
90
+ results = search(pattern: pattern, **options)
91
+ filter_by_depth(results, max_depth)
92
+ end
93
+
94
+ # Search with relevance ranking
95
+ # @param pattern [String] Search pattern
96
+ # @param boost_exact [Integer] Score boost for exact matches
97
+ # @param boost_prefix [Integer] Score boost for prefix matches
98
+ # @param options [Hash] Additional search options
99
+ # @return [Array<Hash>] Ranked results with :relevance_score
100
+ def search_ranked(pattern:, boost_exact: 10, boost_prefix: 5, **options)
101
+ results = search(pattern: pattern, **options)
102
+ rank_results(results, pattern, boost_exact, boost_prefix)
103
+ end
104
+
105
+ # Advanced search with multiple filters
106
+ # @param pattern [String] Search pattern
107
+ # @param max_depth [Integer, nil] Maximum path depth
108
+ # @param ranked [Boolean] Enable relevance ranking
109
+ # @param options [Hash] Additional filters and search options
110
+ # @return [Array<Hash>] Filtered and optionally ranked results
111
+ def search_advanced(pattern:, max_depth: nil, ranked: false, **options)
112
+ results = search(pattern: pattern, **options)
113
+
114
+ results = filter_by_depth(results, max_depth) if max_depth
115
+ results = rank_results(results, pattern, 10, 5) if ranked
116
+
117
+ results
118
+ end
119
+
120
+ private
121
+
122
+ # Parse search pattern into components
123
+ # @param pattern [String] Pattern to parse
124
+ # @param case_sensitive [Boolean] Case sensitivity
125
+ # @return [Hash] Parsed pattern with parts
126
+ def parse_pattern(pattern, case_sensitive: false)
127
+ normalized = case_sensitive ? pattern : pattern.downcase
128
+ parts = normalized.split(".")
129
+
130
+ {
131
+ raw: pattern,
132
+ normalized: normalized,
133
+ parts: parts,
134
+ schema_part: parts[0],
135
+ element_parts: parts[1..] || [],
136
+ }
137
+ end
138
+
139
+ # Check if element matches pattern
140
+ # @param element [ModelElement] Element to check
141
+ # @param pattern_parts [Hash] Parsed pattern
142
+ # @param element_type [String] Type of element
143
+ # @param case_sensitive [Boolean] Case sensitivity
144
+ # @param regex [Boolean] Use regex matching
145
+ # @param exact [Boolean] Exact match only
146
+ # @return [Boolean] True if matches
147
+ def matches_pattern?(element, pattern_parts, _element_type,
148
+ case_sensitive: false, regex: false, exact: false)
149
+ element_path = element.respond_to?(:path) ? element.path : element.id
150
+ return false unless element_path
151
+
152
+ element_id = element.respond_to?(:id) ? element.id : nil
153
+
154
+ search_path = case_sensitive ? element_path : element_path.downcase
155
+ search_id = element_id && !case_sensitive ? element_id.downcase : element_id
156
+ pattern = pattern_parts[:normalized]
157
+
158
+ if regex
159
+ match_regex(search_path,
160
+ pattern) || (search_id && match_regex(search_id, pattern))
161
+ elsif exact
162
+ search_path == pattern
163
+ elsif pattern_parts[:parts].size == 1
164
+ # For simple patterns (no dots or single part), also match against just the element ID
165
+ match_wildcard(search_path, pattern_parts) ||
166
+ (search_id && match_part(search_id, pattern))
167
+ else
168
+ match_wildcard(search_path, pattern_parts)
169
+ end
170
+ end
171
+
172
+ # Match using regex
173
+ # @param path [String] Element path
174
+ # @param pattern [String] Regex pattern
175
+ # @return [Boolean] True if matches
176
+ def match_regex(path, pattern)
177
+ Regexp.new(pattern).match?(path)
178
+ rescue RegexpError
179
+ false
180
+ end
181
+
182
+ # Match using wildcard pattern
183
+ # @param path [String] Element path
184
+ # @param pattern_parts [Hash] Parsed pattern
185
+ # @return [Boolean] True if matches
186
+ def match_wildcard(path, pattern_parts)
187
+ path_parts = path.split(".")
188
+ pattern_array = pattern_parts[:parts]
189
+
190
+ # Handle different wildcard scenarios
191
+ match_wildcard_parts(path_parts, pattern_array)
192
+ end
193
+
194
+ # Match path parts against pattern parts with wildcards
195
+ # @param path_parts [Array<String>] Path components
196
+ # @param pattern_parts [Array<String>] Pattern components
197
+ # @return [Boolean] True if matches
198
+ def match_wildcard_parts(path_parts, pattern_parts)
199
+ # If pattern has no wildcards and different length, no match
200
+ unless pattern_parts.include?("*")
201
+ return false if path_parts.size != pattern_parts.size
202
+
203
+ return path_parts.zip(pattern_parts).all? do |path_part, pattern_part|
204
+ match_part(path_part, pattern_part)
205
+ end
206
+ end
207
+
208
+ # Handle wildcards
209
+ match_with_wildcards(path_parts, pattern_parts)
210
+ end
211
+
212
+ # Match individual part with potential prefix/suffix wildcards
213
+ # @param part [String] Path part
214
+ # @param pattern [String] Pattern part
215
+ # @return [Boolean] True if matches
216
+ def match_part(part, pattern)
217
+ return true if pattern == "*"
218
+
219
+ if pattern.include?("*")
220
+ # Convert wildcard to regex
221
+ regex_pattern = "^#{Regexp.escape(pattern).gsub('\\*', '.*')}$"
222
+ Regexp.new(regex_pattern).match?(part)
223
+ else
224
+ # Support substring matching for simple patterns
225
+ part.include?(pattern)
226
+ end
227
+ end
228
+
229
+ # Match path with wildcard patterns
230
+ # @param path_parts [Array<String>] Path components
231
+ # @param pattern_parts [Array<String>] Pattern components with wildcards
232
+ # @return [Boolean] True if matches
233
+ def match_with_wildcards(path_parts, pattern_parts)
234
+ # Simple implementation: match each level
235
+ # If pattern is shorter but has *, try to match flexibly
236
+
237
+ pi = 0 # pattern index
238
+ li = 0 # path index
239
+
240
+ while pi < pattern_parts.size && li < path_parts.size
241
+ if pattern_parts[pi] == "*"
242
+ # Wildcard matches one element
243
+ pi += 1
244
+ li += 1
245
+ elsif match_part(path_parts[li], pattern_parts[pi])
246
+ pi += 1
247
+ li += 1
248
+ else
249
+ return false
250
+ end
251
+ end
252
+
253
+ # Check if we consumed both arrays
254
+ pi == pattern_parts.size && li == path_parts.size
255
+ end
256
+
257
+ # Collect elements of a specific type
258
+ # @param type [String] Element type
259
+ # @param schema [String, nil] Schema filter
260
+ # @param category [String, nil] Category filter
261
+ # @return [Array<ModelElement>] Collected elements
262
+ def collect_elements(type:, schema: nil, category: nil)
263
+ # Guard against nil schemas collection
264
+ return [] unless @repository.schemas
265
+
266
+ schemas_to_search = if schema
267
+ [@repository.schemas.find do |s|
268
+ s.id == schema
269
+ end].compact
270
+ else
271
+ @repository.schemas
272
+ end
273
+
274
+ case type
275
+ when "schema"
276
+ schemas_to_search
277
+ when "entity"
278
+ collect_from_schemas(schemas_to_search, :entities)
279
+ when "type"
280
+ types = collect_from_schemas(schemas_to_search, :types)
281
+ category ? filter_types_by_category(types, category) : types
282
+ when "attribute"
283
+ collect_attributes(schemas_to_search)
284
+ when "derived_attribute"
285
+ collect_derived_attributes(schemas_to_search)
286
+ when "inverse_attribute"
287
+ collect_inverse_attributes(schemas_to_search)
288
+ when "function"
289
+ collect_from_schemas(schemas_to_search, :functions)
290
+ when "procedure"
291
+ collect_from_schemas(schemas_to_search, :procedures)
292
+ when "rule"
293
+ collect_from_schemas(schemas_to_search, :rules)
294
+ when "constant"
295
+ collect_from_schemas(schemas_to_search, :constants)
296
+ when "parameter"
297
+ collect_parameters(schemas_to_search)
298
+ when "variable"
299
+ collect_variables(schemas_to_search)
300
+ when "where_rule"
301
+ collect_where_rules(schemas_to_search)
302
+ when "unique_rule"
303
+ collect_unique_rules(schemas_to_search)
304
+ when "enumeration_item"
305
+ collect_enumeration_items(schemas_to_search)
306
+ when "interface"
307
+ collect_from_schemas(schemas_to_search, :interfaces)
308
+ else
309
+ []
310
+ end
311
+ end
312
+
313
+ # Collect elements from schemas using a method
314
+ # @param schemas [Array<Schema>] Schemas to search
315
+ # @param method [Symbol] Method to call on schema
316
+ # @return [Array] Collected elements
317
+ def collect_from_schemas(schemas, method)
318
+ # Guard against nil schemas array
319
+ return [] unless schemas
320
+
321
+ schemas.flat_map { |s| s.send(method) || [] }
322
+ end
323
+
324
+ # Collect all attributes from entities
325
+ # @param schemas [Array<Schema>] Schemas to search
326
+ # @return [Array<Attribute>] All attributes
327
+ def collect_attributes(schemas)
328
+ # Guard against nil schemas
329
+ return [] unless schemas
330
+
331
+ entities = collect_from_schemas(schemas, :entities)
332
+ entities.flat_map { |e| e.attributes || [] }.select do |attr|
333
+ !attr.is_a?(Declarations::DerivedAttribute) &&
334
+ !attr.is_a?(Declarations::InverseAttribute)
335
+ end
336
+ end
337
+
338
+ # Collect derived attributes from entities
339
+ # @param schemas [Array<Schema>] Schemas to search
340
+ # @return [Array<DerivedAttribute>] Derived attributes
341
+ def collect_derived_attributes(schemas)
342
+ # Guard against nil schemas
343
+ return [] unless schemas
344
+
345
+ entities = collect_from_schemas(schemas, :entities)
346
+ entities.flat_map { |e| e.attributes || [] }.select do |attr|
347
+ attr.is_a?(Declarations::DerivedAttribute)
348
+ end
349
+ end
350
+
351
+ # Collect inverse attributes from entities
352
+ # @param schemas [Array<Schema>] Schemas to search
353
+ # @return [Array<InverseAttribute>] Inverse attributes
354
+ def collect_inverse_attributes(schemas)
355
+ # Guard against nil schemas
356
+ return [] unless schemas
357
+
358
+ entities = collect_from_schemas(schemas, :entities)
359
+ entities.flat_map { |e| e.attributes || [] }.select do |attr|
360
+ attr.is_a?(Declarations::InverseAttribute)
361
+ end
362
+ end
363
+
364
+ # Collect parameters from functions and procedures
365
+ # @param schemas [Array<Schema>] Schemas to search
366
+ # @return [Array<Parameter>] All parameters
367
+ def collect_parameters(schemas)
368
+ # Guard against nil schemas
369
+ return [] unless schemas
370
+
371
+ functions = collect_from_schemas(schemas, :functions)
372
+ procedures = collect_from_schemas(schemas, :procedures)
373
+ (functions + procedures).flat_map { |f| f.parameters || [] }
374
+ end
375
+
376
+ # Collect variables from functions and procedures
377
+ # @param schemas [Array<Schema>] Schemas to search
378
+ # @return [Array<Variable>] All variables
379
+ def collect_variables(schemas)
380
+ # Guard against nil schemas
381
+ return [] unless schemas
382
+
383
+ functions = collect_from_schemas(schemas, :functions)
384
+ procedures = collect_from_schemas(schemas, :procedures)
385
+ (functions + procedures).flat_map { |f| f.variables || [] }
386
+ end
387
+
388
+ # Collect where rules from entities and types
389
+ # @param schemas [Array<Schema>] Schemas to search
390
+ # @return [Array<WhereRule>] All where rules
391
+ def collect_where_rules(schemas)
392
+ # Guard against nil schemas
393
+ return [] unless schemas
394
+
395
+ entities = collect_from_schemas(schemas, :entities)
396
+ types = collect_from_schemas(schemas, :types)
397
+ (entities + types).flat_map { |e| e.where_rules || [] }
398
+ end
399
+
400
+ # Collect unique rules from entities
401
+ # @param schemas [Array<Schema>] Schemas to search
402
+ # @return [Array<UniqueRule>] All unique rules
403
+ def collect_unique_rules(schemas)
404
+ # Guard against nil schemas
405
+ return [] unless schemas
406
+
407
+ entities = collect_from_schemas(schemas, :entities)
408
+ entities.flat_map { |e| e.unique_rules || [] }
409
+ end
410
+
411
+ # Collect enumeration items from enumeration types
412
+ # @param schemas [Array<Schema>] Schemas to search
413
+ # @return [Array<EnumerationItem>] All enumeration items
414
+ def collect_enumeration_items(schemas)
415
+ # Guard against nil schemas
416
+ return [] unless schemas
417
+
418
+ types = collect_from_schemas(schemas, :types)
419
+ types.flat_map { |t| t.enumeration_items || [] }
420
+ end
421
+
422
+ # Filter types by category
423
+ # @param types [Array<Type>] Types to filter
424
+ # @param category [String] Category to filter by
425
+ # @return [Array<Type>] Filtered types
426
+ def filter_types_by_category(types, category)
427
+ types.select do |type|
428
+ @repository.type_index.categorize(type) == category
429
+ end
430
+ end
431
+
432
+ # Convert element to hash representation
433
+ # @param element [ModelElement] Element to convert
434
+ # @param type [String] Element type
435
+ # @return [Hash] Element data
436
+ def element_to_hash(element, type)
437
+ hash = {
438
+ id: element.respond_to?(:id) ? element.id : nil,
439
+ type: type,
440
+ path: element.respond_to?(:path) ? element.path : nil,
441
+ }
442
+
443
+ # Add schema for non-schema elements
444
+ if type != "schema" && element.respond_to?(:parent)
445
+ schema = find_parent_schema(element)
446
+ hash[:schema] = schema.id if schema
447
+ end
448
+
449
+ # Add category for types
450
+ if type == "type"
451
+ hash[:category] = @repository.type_index.categorize(element)
452
+ end
453
+
454
+ hash
455
+ end
456
+
457
+ # Find parent schema of an element
458
+ # @param element [ModelElement] Element to find schema for
459
+ # @return [Schema, nil] Parent schema
460
+ def find_parent_schema(element)
461
+ current = element
462
+ while current
463
+ return current if current.is_a?(Declarations::Schema)
464
+
465
+ current = current.parent
466
+ end
467
+ nil
468
+ end
469
+
470
+ # Filter results by maximum path depth
471
+ # @param results [Array<Hash>] Search results
472
+ # @param max_depth [Integer] Maximum depth
473
+ # @return [Array<Hash>] Filtered results
474
+ def filter_by_depth(results, max_depth)
475
+ results.select do |result|
476
+ next true unless result[:path]
477
+
478
+ result[:path].split(".").size <= max_depth
479
+ end
480
+ end
481
+
482
+ # Rank results by relevance
483
+ # @param results [Array<Hash>] Search results
484
+ # @param pattern [String] Search pattern
485
+ # @param boost_exact [Integer] Exact match boost
486
+ # @param boost_prefix [Integer] Prefix match boost
487
+ # @return [Array<Hash>] Sorted results with scores
488
+ def rank_results(results, pattern, boost_exact, boost_prefix)
489
+ normalized_pattern = pattern.downcase
490
+
491
+ results.map do |result|
492
+ score = calculate_relevance_score(result, normalized_pattern,
493
+ boost_exact, boost_prefix)
494
+ result.merge(relevance_score: score)
495
+ end.sort_by { |r| -r[:relevance_score] }
496
+ end
497
+
498
+ # Calculate relevance score for a result
499
+ # @param result [Hash] Search result
500
+ # @param pattern [String] Normalized search pattern
501
+ # @param boost_exact [Integer] Exact match boost
502
+ # @param boost_prefix [Integer] Prefix match boost
503
+ # @return [Integer] Relevance score
504
+ def calculate_relevance_score(result, pattern, boost_exact, boost_prefix)
505
+ score = 0
506
+ id = (result[:id] || "").downcase
507
+
508
+ # Exact match gets highest score
509
+ score += boost_exact if id == pattern
510
+
511
+ # Prefix match gets medium score
512
+ score += boost_prefix if id.start_with?(pattern)
513
+
514
+ # Shorter paths rank higher (max 10 points)
515
+ path_depth = result[:path]&.split(".")&.size || 1
516
+ score += [10 - path_depth, 0].max
517
+
518
+ # Schema-level results rank higher
519
+ score += 5 if result[:type] == "schema"
520
+
521
+ score
522
+ end
523
+ end
524
+ end
525
+ end
@@ -1,95 +1,5 @@
1
- # This file is kept for backward compatibility and to ensure all model classes are loaded
2
- # when explicitly requiring 'expressir/model'. The actual autoload definitions are in the main
3
- # expressir.rb file.
1
+ # frozen_string_literal: true
4
2
 
5
- # Ensure all model classes are loaded by referencing them
6
- # This triggers the autoload mechanism for each class
7
-
8
- # Core model classes
9
- Expressir::Model::ModelElement
10
- Expressir::Model::Cache
11
- Expressir::Model::Identifier
12
- Expressir::Model::Repository
13
-
14
- # Data types
15
- Expressir::Model::DataTypes::Aggregate
16
- Expressir::Model::DataTypes::Array
17
- Expressir::Model::DataTypes::Bag
18
- Expressir::Model::DataTypes::Binary
19
- Expressir::Model::DataTypes::Boolean
20
- Expressir::Model::DataTypes::Enumeration
21
- Expressir::Model::DataTypes::EnumerationItem
22
- Expressir::Model::DataTypes::GenericEntity
23
- Expressir::Model::DataTypes::Generic
24
- Expressir::Model::DataTypes::Integer
25
- Expressir::Model::DataTypes::List
26
- Expressir::Model::DataTypes::Logical
27
- Expressir::Model::DataTypes::Number
28
- Expressir::Model::DataTypes::Real
29
- Expressir::Model::DataTypes::Select
30
- Expressir::Model::DataTypes::Set
31
- Expressir::Model::DataTypes::String
32
-
33
- # Declarations
34
- Expressir::Model::Declarations::Attribute
35
- Expressir::Model::Declarations::Constant
36
- Expressir::Model::Declarations::DerivedAttribute
37
- Expressir::Model::Declarations::Entity
38
- Expressir::Model::Declarations::Function
39
- Expressir::Model::Declarations::Interface
40
- Expressir::Model::Declarations::InterfaceItem
41
- Expressir::Model::Declarations::InterfacedItem
42
- Expressir::Model::Declarations::InverseAttribute
43
- Expressir::Model::Declarations::Parameter
44
- Expressir::Model::Declarations::Procedure
45
- Expressir::Model::Declarations::RemarkItem
46
- Expressir::Model::Declarations::Rule
47
- Expressir::Model::Declarations::Schema
48
- Expressir::Model::Declarations::SchemaVersion
49
- Expressir::Model::Declarations::SchemaVersionItem
50
- Expressir::Model::Declarations::SubtypeConstraint
51
- Expressir::Model::Declarations::Type
52
- Expressir::Model::Declarations::UniqueRule
53
- Expressir::Model::Declarations::Variable
54
- Expressir::Model::Declarations::WhereRule
55
-
56
- # Expressions
57
- Expressir::Model::Expressions::AggregateInitializer
58
- Expressir::Model::Expressions::AggregateInitializerItem
59
- Expressir::Model::Expressions::BinaryExpression
60
- Expressir::Model::Expressions::EntityConstructor
61
- Expressir::Model::Expressions::FunctionCall
62
- Expressir::Model::Expressions::Interval
63
- Expressir::Model::Expressions::QueryExpression
64
- Expressir::Model::Expressions::UnaryExpression
65
-
66
- # Literals
67
- Expressir::Model::Literals::Binary
68
- Expressir::Model::Literals::Integer
69
- Expressir::Model::Literals::Logical
70
- Expressir::Model::Literals::Real
71
- Expressir::Model::Literals::String
72
-
73
- # References
74
- Expressir::Model::References::AttributeReference
75
- Expressir::Model::References::GroupReference
76
- Expressir::Model::References::IndexReference
77
- Expressir::Model::References::SimpleReference
78
-
79
- # Statements
80
- Expressir::Model::Statements::Alias
81
- Expressir::Model::Statements::Assignment
82
- Expressir::Model::Statements::Case
83
- Expressir::Model::Statements::CaseAction
84
- Expressir::Model::Statements::Compound
85
- Expressir::Model::Statements::Escape
86
- Expressir::Model::Statements::If
87
- Expressir::Model::Statements::Null
88
- Expressir::Model::Statements::ProcedureCall
89
- Expressir::Model::Statements::Repeat
90
- Expressir::Model::Statements::Return
91
- Expressir::Model::Statements::Skip
92
-
93
- # Supertype expressions
94
- Expressir::Model::SupertypeExpressions::BinarySupertypeExpression
95
- Expressir::Model::SupertypeExpressions::OneofSupertypeExpression
3
+ # This file is kept for backward compatibility when explicitly requiring 'expressir/model'
4
+ # The actual autoload definitions are in lib/expressir.rb
5
+ # This file does nothing - autoload handles everything automatically