lutaml-path 0.1.0 → 0.1.1
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/rake.yml +15 -0
- data/.github/workflows/release.yml +30 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +7 -0
- data/README.adoc +651 -36
- data/lib/lutaml/path/abstract_path.rb +24 -0
- data/lib/lutaml/path/condition.rb +100 -0
- data/lib/lutaml/path/condition_evaluator.rb +109 -0
- data/lib/lutaml/path/element_path.rb +66 -8
- data/lib/lutaml/path/errors.rb +12 -0
- data/lib/lutaml/path/instance_path.rb +63 -0
- data/lib/lutaml/path/parser.rb +164 -13
- data/lib/lutaml/path/path_segment.rb +31 -5
- data/lib/lutaml/path/resolver.rb +154 -0
- data/lib/lutaml/path/step.rb +50 -0
- data/lib/lutaml/path/transformer.rb +63 -21
- data/lib/lutaml/path/version.rb +1 -1
- data/lib/lutaml/path.rb +34 -3
- data/lutaml-path.gemspec +2 -1
- data/spec/lutaml/path_spec.rb +674 -29
- data/spec/lutaml/resolver_spec.rb +182 -0
- metadata +29 -6
- data/.github/workflows/main.yml +0 -27
data/README.adoc
CHANGED
|
@@ -7,18 +7,621 @@ image:https://img.shields.io/gem/v/lutaml-path.svg["Gem Version", link="https://
|
|
|
7
7
|
|
|
8
8
|
toc::[]
|
|
9
9
|
|
|
10
|
-
==
|
|
10
|
+
== Introduction
|
|
11
11
|
|
|
12
|
-
LutaML Path
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
hierarchies.
|
|
12
|
+
"`LutaML Path`" is a query language for LutaML Models. It allows for referencing
|
|
13
|
+
and locating elements within information models defined using the LutaML Model
|
|
14
|
+
system.
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
referencing any UML element including packages, classes, interfaces, properties,
|
|
19
|
-
and operations.
|
|
16
|
+
There are two types of LutaML Path syntaxes that share a common syntax:
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
* Model definition query (e.g. LutaML Model and UML definitions) (see <<lutaml-path-models>>)
|
|
19
|
+
* Instance data query (information model instances) (see <<lutaml-path-instance>>)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
== Common syntax
|
|
23
|
+
|
|
24
|
+
=== General
|
|
25
|
+
|
|
26
|
+
The LutaML path syntax is designed to be concise and expressive. It supports
|
|
27
|
+
both absolute and relative paths, wildcards, and pattern matching.
|
|
28
|
+
|
|
29
|
+
The path syntax is inspired by the Object Constraint Language (OCL) and extends
|
|
30
|
+
beyond it to provide a mechanism for uniquely identifying model elements within
|
|
31
|
+
the LutaML Model hierarchy.
|
|
32
|
+
|
|
33
|
+
LutaML Path fully supports Unicode characters in package and element names.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
=== Expression
|
|
37
|
+
|
|
38
|
+
==== General
|
|
39
|
+
|
|
40
|
+
An expression can be a single element, a hierarchy, or a pattern.
|
|
41
|
+
|
|
42
|
+
The path syntax supports the following features:
|
|
43
|
+
|
|
44
|
+
* Wildcard matching
|
|
45
|
+
* Pattern matching
|
|
46
|
+
* Path segments with a regular expression-like syntax
|
|
47
|
+
|
|
48
|
+
In LutaML Models, models are typically referred using TitleCase and attributes
|
|
49
|
+
in snake_case.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
==== Wildcard matching
|
|
54
|
+
|
|
55
|
+
Wildcard characters are used to match multiple elements.
|
|
56
|
+
|
|
57
|
+
The path syntax supports the following wildcards:
|
|
58
|
+
|
|
59
|
+
Wildcard match character `*`::
|
|
60
|
+
matches any sequence of characters. When used as double asterisks `**`, it
|
|
61
|
+
matches any nested depth of the hierarchy.
|
|
62
|
+
+
|
|
63
|
+
[example]
|
|
64
|
+
====
|
|
65
|
+
[source]
|
|
66
|
+
----
|
|
67
|
+
# Wildcard matching
|
|
68
|
+
## Matches any class starting with "Base"
|
|
69
|
+
Base*
|
|
70
|
+
|
|
71
|
+
## Matches any class ending with "Model"
|
|
72
|
+
*Model
|
|
73
|
+
|
|
74
|
+
## Matches any class starting with "Base" in the "Core" package
|
|
75
|
+
Core::Base*
|
|
76
|
+
|
|
77
|
+
## Matches any class ending with "Model" in the "Core" package
|
|
78
|
+
Core::*Model
|
|
79
|
+
|
|
80
|
+
## Matches any class in the "Core" package
|
|
81
|
+
Core::*
|
|
82
|
+
----
|
|
83
|
+
====
|
|
84
|
+
|
|
85
|
+
Any match character `?`::
|
|
86
|
+
matches any single character
|
|
87
|
+
+
|
|
88
|
+
[example]
|
|
89
|
+
====
|
|
90
|
+
[source]
|
|
91
|
+
----
|
|
92
|
+
# Single character matching
|
|
93
|
+
## Matches any class starting with "Bas" and one more character, such as "Base"
|
|
94
|
+
Bas?
|
|
95
|
+
|
|
96
|
+
## Matches any class ending with "Model" and one more character, such as "BaseModel"
|
|
97
|
+
*Model?
|
|
98
|
+
----
|
|
99
|
+
====
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
==== Pattern matching
|
|
103
|
+
|
|
104
|
+
The path syntax supports several kinds of patterns. These patterns are used to
|
|
105
|
+
match elements based on specific criteria. The patterns are similar to regular
|
|
106
|
+
expressions but are more concise and easier to read.
|
|
107
|
+
|
|
108
|
+
The path syntax is case-sensitive and follows the source model's character
|
|
109
|
+
cases.
|
|
110
|
+
|
|
111
|
+
Set match expression `[` character expression `]`::
|
|
112
|
+
The delimiters of `[` and `]` are used to define a set of characters. The set
|
|
113
|
+
can contain any characters, including Unicode characters.
|
|
114
|
+
|
|
115
|
+
Character match expression `[abc]`:::
|
|
116
|
+
matches any character in the set.
|
|
117
|
+
|
|
118
|
+
Negative match expression `[!...]`:::
|
|
119
|
+
matches any character not in the set.
|
|
120
|
+
|
|
121
|
+
Range match expression `[a-z]`:::
|
|
122
|
+
The `-` character is used to define a range of characters. It
|
|
123
|
+
matches any character in the range according to Unicode code points.
|
|
124
|
+
|
|
125
|
+
Alternatives match expression `{expression1,expression2}`::
|
|
126
|
+
matches any of the comma-separated patterns
|
|
127
|
+
|
|
128
|
+
[example]
|
|
129
|
+
====
|
|
130
|
+
[source]
|
|
131
|
+
----
|
|
132
|
+
# Pattern matching
|
|
133
|
+
## Matches any class ending with "ase", such as "Base", "Case" but not "Vase"
|
|
134
|
+
[BC]ase
|
|
135
|
+
|
|
136
|
+
## Matches any class starting with "Base" or "Case"
|
|
137
|
+
{Base,Case}
|
|
138
|
+
|
|
139
|
+
## Matches any class starting with "Base" or "Case" and ending with "Radius"
|
|
140
|
+
{Base,Case}*Radius
|
|
141
|
+
|
|
142
|
+
# Range matching
|
|
143
|
+
## Matches any class of "Vase" and "vase"
|
|
144
|
+
[Vv]ase
|
|
145
|
+
|
|
146
|
+
# Negative matching
|
|
147
|
+
## Matches any class ending with "ase" but not "Vase" and "Case"
|
|
148
|
+
[!CV]ase
|
|
149
|
+
|
|
150
|
+
# Unicode matching
|
|
151
|
+
## Matches any class starting with "建物"
|
|
152
|
+
建物*
|
|
153
|
+
|
|
154
|
+
## Matches exactly "ドア" or "窓"
|
|
155
|
+
{ドア,窓}
|
|
156
|
+
----
|
|
157
|
+
====
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
=== Path
|
|
162
|
+
|
|
163
|
+
==== General
|
|
164
|
+
|
|
165
|
+
A path is a sequence of path segments separated by hierarchy separators.
|
|
166
|
+
|
|
167
|
+
A path segment is a single element or a pattern that matches a single element.
|
|
168
|
+
|
|
169
|
+
----
|
|
170
|
+
╔════════════════╗
|
|
171
|
+
║ Path ║
|
|
172
|
+
║ (segments) ║
|
|
173
|
+
╚════╦══════╦════╝
|
|
174
|
+
│ │
|
|
175
|
+
▼ ▼
|
|
176
|
+
╔════════════════╗
|
|
177
|
+
║ Path Segment ║
|
|
178
|
+
╚═══════╦════════╝
|
|
179
|
+
│
|
|
180
|
+
▼
|
|
181
|
+
╔════════════════╗
|
|
182
|
+
║ Expression ║
|
|
183
|
+
╚════════════════╝
|
|
184
|
+
----
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
==== Hierarchy separators
|
|
188
|
+
|
|
189
|
+
The hierarchy separators are used to separate path segments within a path:
|
|
190
|
+
|
|
191
|
+
`::`:: for model definitions
|
|
192
|
+
`.`:: for attributes and instance data.
|
|
193
|
+
|
|
194
|
+
The separators can be escaped with a backslash inside an expression.
|
|
195
|
+
Single colons (`:`) are not used as separators and behave as part of the
|
|
196
|
+
segment.
|
|
197
|
+
|
|
198
|
+
[example]
|
|
199
|
+
====
|
|
200
|
+
[source]
|
|
201
|
+
----
|
|
202
|
+
# Referencing the "Rectangle::Shape" object
|
|
203
|
+
::Rectangle\::Shape
|
|
204
|
+
|
|
205
|
+
# Accessing the "width.length" attribute of the "Rectangle" object
|
|
206
|
+
::Rectangle.width\.length
|
|
207
|
+
----
|
|
208
|
+
====
|
|
209
|
+
|
|
210
|
+
The leading hierarchy separator, indicating absolute paths, cannot be escaped.
|
|
211
|
+
|
|
212
|
+
[example]
|
|
213
|
+
====
|
|
214
|
+
[source]
|
|
215
|
+
----
|
|
216
|
+
\::Rectangle::Shape # This is invalid
|
|
217
|
+
|
|
218
|
+
\.width.length # This is invalid
|
|
219
|
+
----
|
|
220
|
+
====
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
==== Absolute and relative paths
|
|
225
|
+
|
|
226
|
+
The path syntax supports both absolute and relative paths:
|
|
227
|
+
|
|
228
|
+
* Absolute paths start with `::` and begin at the model root
|
|
229
|
+
* Relative paths start without `::` and are resolved from the current context
|
|
230
|
+
|
|
231
|
+
The target element type may be a class, property, operation, or any other model
|
|
232
|
+
element.
|
|
233
|
+
|
|
234
|
+
The separator can be escaped with a backslash: `\::`, if the package name
|
|
235
|
+
contains a double colon.
|
|
236
|
+
|
|
237
|
+
[example]
|
|
238
|
+
====
|
|
239
|
+
[source]
|
|
240
|
+
----
|
|
241
|
+
# Absolute path
|
|
242
|
+
## Locates a model called "Rectangle" in the "Shapes" package at root
|
|
243
|
+
::Shapes::Rectangle
|
|
244
|
+
|
|
245
|
+
## Locates an attribute called "width" in the "Rectangle" class at root
|
|
246
|
+
::Shapes::Rectangle.width
|
|
247
|
+
|
|
248
|
+
## Locates a model called "図形" in the "Geometry" package at root
|
|
249
|
+
::Geometry::図形
|
|
250
|
+
|
|
251
|
+
## Locates an attribute called "高" in the "図形" class
|
|
252
|
+
::Geometry::図形.高
|
|
253
|
+
|
|
254
|
+
# Relative path
|
|
255
|
+
## Locates a model called "Rectangle" in the current package
|
|
256
|
+
Shapes::Rectangle
|
|
257
|
+
|
|
258
|
+
## Locates an attribute called "width" in the current class
|
|
259
|
+
Shapes::Rectangle.width
|
|
260
|
+
|
|
261
|
+
## Locates a model called "図形" under the "Geometry" model in the current model
|
|
262
|
+
Geometry::図形
|
|
263
|
+
|
|
264
|
+
## Locates an attribute called "高" in the "図形" model
|
|
265
|
+
Geometry::図形.高
|
|
266
|
+
----
|
|
267
|
+
====
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
[[lutaml-path-models]]
|
|
272
|
+
== LutaML path for model definitions
|
|
273
|
+
|
|
274
|
+
=== Overview
|
|
275
|
+
|
|
276
|
+
The LutaML path for model definition query syntax ("LutaML model path") is used
|
|
277
|
+
to reference elements within model definitions. These paths are used to locate
|
|
278
|
+
classes, properties, operations, and other model elements within the model
|
|
279
|
+
hierarchy.
|
|
280
|
+
|
|
281
|
+
While the LutaML path syntax is designed to work with LutaML Models, it can also
|
|
282
|
+
be used with UML models.
|
|
283
|
+
|
|
284
|
+
It implements a path notation similar to the Object Constraint Language
|
|
285
|
+
(OCL) to locate UML model elements across package hierarchies.
|
|
286
|
+
|
|
287
|
+
// This gem is specifically designed to work with OMG UML models and supports
|
|
288
|
+
// referencing any UML element including packages, classes, interfaces, properties,
|
|
289
|
+
// and operations.
|
|
290
|
+
|
|
291
|
+
The UML element path specification extends the OCL 2.4 specification to provide
|
|
292
|
+
a mechanism for uniquely identifying model elements (classes, interfaces,
|
|
293
|
+
enumerations, etc.) within the UML package hierarchy. It provides both relative
|
|
294
|
+
and absolute path references.
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
=== Hierarchical paths
|
|
298
|
+
|
|
299
|
+
An element path can be specified in these forms:
|
|
300
|
+
|
|
301
|
+
* Single element: `ElementName`
|
|
302
|
+
* Relative path: `Package1::Package2::ElementName`
|
|
303
|
+
* Absolute path: `::Package1::Package2::ElementName`
|
|
304
|
+
|
|
305
|
+
The absolute path variant starts with `::` to indicate the path begins at the model root.
|
|
306
|
+
|
|
307
|
+
[source]
|
|
308
|
+
----
|
|
309
|
+
# Japanese package and class names
|
|
310
|
+
建物::窓::ガラス
|
|
311
|
+
::建築モデル::建物::窓
|
|
312
|
+
|
|
313
|
+
# Mixed language names
|
|
314
|
+
building::窓::Window
|
|
315
|
+
geometry::図形::円
|
|
316
|
+
|
|
317
|
+
# Patterns with Unicode
|
|
318
|
+
建物::部品*
|
|
319
|
+
*部::Base*
|
|
320
|
+
----
|
|
321
|
+
|
|
322
|
+
=== Path segment patterns
|
|
323
|
+
|
|
324
|
+
Path segment wildcards can be used to match package hierarchy.
|
|
325
|
+
|
|
326
|
+
* Single segment: `Package1::*::Element` matches Element in any subpackage of Package1
|
|
327
|
+
* Multiple segments: `Package1::**::Element` matches Element in Package1 or any nested depth
|
|
328
|
+
|
|
329
|
+
[source]
|
|
330
|
+
----
|
|
331
|
+
# Wildcard matching
|
|
332
|
+
Package1::*::Element
|
|
333
|
+
Package1::**::Element
|
|
334
|
+
----
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
=== Resolution rules
|
|
338
|
+
|
|
339
|
+
* Single element name or pattern matches in any package
|
|
340
|
+
* Relative paths are resolved from current context
|
|
341
|
+
* Absolute paths are resolved from model root
|
|
342
|
+
* Path segments must match patterns exactly
|
|
343
|
+
* Empty segments are invalid
|
|
344
|
+
* Multiple matches are allowed with wildcards/patterns
|
|
345
|
+
* Without wildcards/patterns, first match is used for multiple matches
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
[[lutaml-path-instance]]
|
|
349
|
+
== LutaML path for instance data
|
|
350
|
+
|
|
351
|
+
=== Overview
|
|
352
|
+
|
|
353
|
+
The LutaML path for instance data query syntax ("LutaML instance path") is used
|
|
354
|
+
to navigate and query data within model instances. These paths are used to
|
|
355
|
+
access attributes, filter data, and navigate complex structures within model
|
|
356
|
+
instances.
|
|
357
|
+
|
|
358
|
+
Model instances are instances of data that conform to a model definition.
|
|
359
|
+
|
|
360
|
+
The LutaML model data syntax uses dot notation and filters to navigate and query
|
|
361
|
+
data within model instances.
|
|
362
|
+
|
|
363
|
+
Model instances can be queried using path expressions to access attributes,
|
|
364
|
+
filter data, and navigate complex structures.
|
|
365
|
+
|
|
366
|
+
Model are typically referred using TitleCase and attributes in snake_case.
|
|
367
|
+
|
|
368
|
+
An instance path expression when resolved provides two types of return values:
|
|
369
|
+
|
|
370
|
+
* A single element (e.g. attribute value)
|
|
371
|
+
* A collection of elements (e.g. filtered data)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
=== Hierarchical paths
|
|
375
|
+
|
|
376
|
+
An instance path can be specified in these forms:
|
|
377
|
+
|
|
378
|
+
* Collection of model instances: `{model_path}`
|
|
379
|
+
* Attribute: `{class_path}.{attribute}`
|
|
380
|
+
|
|
381
|
+
[example]
|
|
382
|
+
====
|
|
383
|
+
[source]
|
|
384
|
+
----
|
|
385
|
+
# Simple attribute access
|
|
386
|
+
obj.title
|
|
387
|
+
obj.edition.number
|
|
388
|
+
----
|
|
389
|
+
====
|
|
390
|
+
|
|
391
|
+
=== Wildcard matching
|
|
392
|
+
|
|
393
|
+
Wildcard symbols can be used to match characters in the path.
|
|
394
|
+
|
|
395
|
+
These include:
|
|
396
|
+
|
|
397
|
+
* path segments
|
|
398
|
+
* attribute names
|
|
399
|
+
* attribute values in conditions
|
|
400
|
+
|
|
401
|
+
[example]
|
|
402
|
+
====
|
|
403
|
+
[source]
|
|
404
|
+
----
|
|
405
|
+
# Wildcard matching path segments
|
|
406
|
+
obj.*.docidentifier[type!='ISBN']
|
|
407
|
+
|
|
408
|
+
# Wildcard matching attribute names
|
|
409
|
+
obj.contributor.*[type='author']
|
|
410
|
+
|
|
411
|
+
# Wildcard in path segments
|
|
412
|
+
## Matches all contributors with the name 'ISO'
|
|
413
|
+
## e.g. matches obj.contributor.publisher.organization.name('ISO')
|
|
414
|
+
obj.contributor.**[name='ISO']
|
|
415
|
+
|
|
416
|
+
# Wildcard usage in conditions
|
|
417
|
+
## Matches all authors from standards organizations
|
|
418
|
+
obj.contributor[role.type='author' && organization.type='stand*']
|
|
419
|
+
----
|
|
420
|
+
====
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
=== Filtering
|
|
425
|
+
|
|
426
|
+
==== General
|
|
427
|
+
|
|
428
|
+
Filters can be applied to model instances to query data based on specific
|
|
429
|
+
criteria. Filters are enclosed in square brackets `[]` and can contain
|
|
430
|
+
conditions.
|
|
431
|
+
|
|
432
|
+
A filter "condition" is a comparison expression that evaluates to a boolean value.
|
|
433
|
+
|
|
434
|
+
Syntax:
|
|
435
|
+
|
|
436
|
+
[source]
|
|
437
|
+
----
|
|
438
|
+
attribute[condition]
|
|
439
|
+
----
|
|
440
|
+
|
|
441
|
+
The result of a filter is a collection of elements that match the condition.
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
==== Conditions
|
|
445
|
+
|
|
446
|
+
Conditions are used to filter data based on specific criteria.
|
|
447
|
+
|
|
448
|
+
A filter "condition" is a comparison expression that evaluates to a boolean value.
|
|
449
|
+
|
|
450
|
+
Conditions can include:
|
|
451
|
+
|
|
452
|
+
* Value comparison: `property='value'`
|
|
453
|
+
* Multiple conditions: `condition1 && condition2`
|
|
454
|
+
* List membership: `property in ('value1', 'value2')`
|
|
455
|
+
* Logical operators: `condition1 && (condition2 || condition3)`
|
|
456
|
+
* Negation: `!condition`
|
|
457
|
+
* Existence: `exists`
|
|
458
|
+
|
|
459
|
+
Logical operators that can conjoin conditions:
|
|
460
|
+
|
|
461
|
+
* Comparison: `=`, `!=`, `>`, `<`, `>=`, `<=`
|
|
462
|
+
* Logical: `&&`, `||`, `!`
|
|
463
|
+
* List membership: `in`
|
|
464
|
+
|
|
465
|
+
Operator precedence, from tightest to loosest: `!`, then the comparison,
|
|
466
|
+
membership and existence predicates (`=`, `!=`, `>`, `<`, `>=`, `<=`, `in`,
|
|
467
|
+
`exists`), then `&&`, then `||`. Use parentheses to override it.
|
|
468
|
+
|
|
469
|
+
So `a in ('x','y') && b = 'z'` groups as `(a in ('x','y')) && (b = 'z')`, and
|
|
470
|
+
`a = 'x' || b = 'y' && c = 'z'` groups as `(a = 'x') || ((b = 'y') && (c = 'z'))`.
|
|
471
|
+
|
|
472
|
+
`and` is accepted as an alias for `&&`.
|
|
473
|
+
|
|
474
|
+
Comparison values are single-quoted strings (`'author'`) or bare numbers
|
|
475
|
+
(`30`). Use `\'` for a literal quote inside a value.
|
|
476
|
+
|
|
477
|
+
Within a segment, `[` opens a filter only when all three hold:
|
|
478
|
+
|
|
479
|
+
. it is not the segment's first character;
|
|
480
|
+
. the bracket group ends the segment; and
|
|
481
|
+
. its body contains a condition operator.
|
|
482
|
+
|
|
483
|
+
Otherwise `[` is an ordinary wildcard character set. So `Class[A-Z]` remains a
|
|
484
|
+
pattern because its body has no operator, and `[exists]` remains a pattern
|
|
485
|
+
because its `[` is the segment's first character, while `contributor[exists]`
|
|
486
|
+
is a filter.
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
[example]
|
|
490
|
+
====
|
|
491
|
+
[source]
|
|
492
|
+
----
|
|
493
|
+
# Condition for attribute value
|
|
494
|
+
obj.contributor[role.type='publisher']
|
|
495
|
+
# => Returns all contributors with role type 'publisher'
|
|
496
|
+
|
|
497
|
+
# Multiple conditions
|
|
498
|
+
obj.contributor[role.type='author' && organization.type='standards']
|
|
499
|
+
# => Returns all authors from standards organizations
|
|
500
|
+
|
|
501
|
+
# List membership
|
|
502
|
+
obj.docidentifier[type in ('ISBN','ISSN','DOI')]
|
|
503
|
+
# => Returns all document identifiers of type ISBN, ISSN, or DOI
|
|
504
|
+
|
|
505
|
+
# Existence
|
|
506
|
+
obj.contributor[exists]
|
|
507
|
+
# => Returns all contributors
|
|
508
|
+
|
|
509
|
+
# Negation
|
|
510
|
+
obj.docidentifier[type!='ISBN']
|
|
511
|
+
# => Returns all document identifiers not of type ISBN
|
|
512
|
+
|
|
513
|
+
# Complex conditions
|
|
514
|
+
obj.contributor[role.type='author' && organization.type='standards']
|
|
515
|
+
# => Returns all authors from standards organizations
|
|
516
|
+
----
|
|
517
|
+
====
|
|
518
|
+
|
|
519
|
+
==== Applying filters
|
|
520
|
+
|
|
521
|
+
Filters are applied to model instances to query data based on specific criteria.
|
|
522
|
+
|
|
523
|
+
Filters are enclosed in square brackets `[]` and can contain conditions.
|
|
524
|
+
|
|
525
|
+
[example]
|
|
526
|
+
====
|
|
527
|
+
[source]
|
|
528
|
+
----
|
|
529
|
+
# Filter by date type
|
|
530
|
+
## Steps:
|
|
531
|
+
## 1. The first portion of the path navigates to the date element
|
|
532
|
+
## 2. The filter condition is applied to the type attribute
|
|
533
|
+
obj.date[type='updated']
|
|
534
|
+
|
|
535
|
+
# Filter by document identifier type
|
|
536
|
+
## Steps:
|
|
537
|
+
## 1. The first portion of the path navigates to the docidentifier element
|
|
538
|
+
## 2. The filter condition is applied to the type attribute
|
|
539
|
+
obj.docidentifier[type!='ISBN']
|
|
540
|
+
----
|
|
541
|
+
====
|
|
542
|
+
|
|
543
|
+
Filters can be chained to navigate complex structures and query data based on
|
|
544
|
+
specific criteria.
|
|
545
|
+
|
|
546
|
+
[example]
|
|
547
|
+
====
|
|
548
|
+
[source]
|
|
549
|
+
----
|
|
550
|
+
# Nested navigation with filtering
|
|
551
|
+
## Filter by role type
|
|
552
|
+
## Steps:
|
|
553
|
+
## 1. The first portion of the path navigates to the contributor element
|
|
554
|
+
## 2. The filter condition is applied to the role type attribute
|
|
555
|
+
## 3. The last portion of the path navigates to the organization name attribute
|
|
556
|
+
obj.contributor[role.type='publisher'].organization.name
|
|
557
|
+
|
|
558
|
+
# Filter by multiple conditions
|
|
559
|
+
## Filter by role type and organization type
|
|
560
|
+
## Steps:
|
|
561
|
+
## 1. The first portion of the path navigates to the contributor element
|
|
562
|
+
## 2. The filter conditions are applied to the role type and organization type attributes
|
|
563
|
+
## 3. The last portion of the path navigates to the organization name attribute
|
|
564
|
+
obj.contributor[role.type='author' && organization.type='standards'].organization.name
|
|
565
|
+
|
|
566
|
+
# Hierarchical filtering (nested conditions)
|
|
567
|
+
## Filter by role type and organization type
|
|
568
|
+
## Steps:
|
|
569
|
+
## 1. The first portion of the path navigates to the contributor element
|
|
570
|
+
## 2. The filter conditions are applied to the role type and organization type attributes
|
|
571
|
+
## 3. The path navigates to the organization name attribute
|
|
572
|
+
## 4. The filter condition is applied to the organization name attribute
|
|
573
|
+
obj.contributor[role.type='author' && organization.type='standards'].organization[organization.name in ('ISO','IEC')].name
|
|
574
|
+
----
|
|
575
|
+
====
|
|
576
|
+
|
|
577
|
+
==== Path expressions
|
|
578
|
+
|
|
579
|
+
Path expressions are used to navigate through model attributes and filter data
|
|
580
|
+
based on specific criteria.
|
|
581
|
+
|
|
582
|
+
The path expressions are evaluated against the current model context.
|
|
583
|
+
|
|
584
|
+
[example]
|
|
585
|
+
====
|
|
586
|
+
[source]
|
|
587
|
+
----
|
|
588
|
+
# Navigate through model attributes
|
|
589
|
+
contributor.role.type
|
|
590
|
+
organization.name
|
|
591
|
+
|
|
592
|
+
# Filter by attribute values
|
|
593
|
+
contributor[role.type = 'publisher'].organization.name
|
|
594
|
+
date[type = 'updated']
|
|
595
|
+
docidentifier[type != 'ISBN']
|
|
596
|
+
|
|
597
|
+
# Multiple conditions
|
|
598
|
+
contributor[role.type = 'author' and organization.type = 'standards']
|
|
599
|
+
----
|
|
600
|
+
====
|
|
601
|
+
|
|
602
|
+
=== Resolution rules
|
|
603
|
+
|
|
604
|
+
* Single element name or pattern matches in any package
|
|
605
|
+
* Relative paths are resolved from current context
|
|
606
|
+
* Absolute paths are resolved from model root
|
|
607
|
+
* Path segments must match patterns exactly
|
|
608
|
+
* Empty segments are invalid
|
|
609
|
+
* Multiple matches are allowed with wildcards/patterns
|
|
610
|
+
* Without wildcards/patterns, first match is used for multiple matches
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
== Ruby API
|
|
615
|
+
|
|
616
|
+
=== Introduction
|
|
617
|
+
|
|
618
|
+
The LutaML Path gem provides a simple API for parsing and matching paths.
|
|
619
|
+
|
|
620
|
+
WARNING: Instance data paths are parsed but not resolved. `Lutaml::Path.parse`
|
|
621
|
+
returns an `InstancePath` carrying the parsed structure; evaluating one against
|
|
622
|
+
model instance data is not yet supported.
|
|
623
|
+
|
|
624
|
+
=== How to install
|
|
22
625
|
|
|
23
626
|
[source,ruby]
|
|
24
627
|
----
|
|
@@ -32,7 +635,9 @@ Or add this line to your application's Gemfile:
|
|
|
32
635
|
gem 'lutaml-path'
|
|
33
636
|
----
|
|
34
637
|
|
|
35
|
-
|
|
638
|
+
=== Basic usage
|
|
639
|
+
|
|
640
|
+
The LutaML Path gem provides a simple API for parsing and matching paths.
|
|
36
641
|
|
|
37
642
|
The path syntax follows UML namespace conventions using `::` as a separator:
|
|
38
643
|
|
|
@@ -40,29 +645,34 @@ The path syntax follows UML namespace conventions using `::` as a separator:
|
|
|
40
645
|
----
|
|
41
646
|
require 'lutaml/path'
|
|
42
647
|
|
|
43
|
-
#
|
|
648
|
+
# Model definition path
|
|
649
|
+
## Simple element reference
|
|
44
650
|
path = Lutaml::Path.parse("Package::Class")
|
|
45
651
|
|
|
46
|
-
|
|
652
|
+
## Absolute path (starts from root namespace)
|
|
47
653
|
path = Lutaml::Path.parse("::Root::Package::Class")
|
|
48
654
|
|
|
49
|
-
|
|
655
|
+
## Path with wildcards
|
|
50
656
|
path = Lutaml::Path.parse("Package::*::BaseClass*")
|
|
51
657
|
----
|
|
52
658
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
659
|
+
[source,ruby]
|
|
660
|
+
----
|
|
661
|
+
# Model instance data path
|
|
662
|
+
## Parse model data path
|
|
663
|
+
path = Lutaml::Path.parse("obj.contributor.organization.name")
|
|
56
664
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
* `{pattern1,pattern2}` - matches any of the comma-separated patterns
|
|
665
|
+
## Parse model data path with filter
|
|
666
|
+
path = Lutaml::Path.parse("obj.contributor[role.type='publisher']")
|
|
667
|
+
----
|
|
61
668
|
|
|
62
|
-
|
|
669
|
+
=== Working with patterns
|
|
63
670
|
|
|
671
|
+
[example]
|
|
672
|
+
====
|
|
64
673
|
[source,ruby]
|
|
65
674
|
----
|
|
675
|
+
# Model location matching
|
|
66
676
|
# Match any class starting with "Base"
|
|
67
677
|
path = Lutaml::Path.parse("Base*")
|
|
68
678
|
|
|
@@ -72,8 +682,17 @@ path = Lutaml::Path.parse("Package::[A-Z]*::Interface")
|
|
|
72
682
|
# Match multiple alternatives
|
|
73
683
|
path = Lutaml::Path.parse("model::{Abstract,Base}Class")
|
|
74
684
|
----
|
|
685
|
+
====
|
|
686
|
+
|
|
687
|
+
// TODO: enable
|
|
688
|
+
// [source,ruby]
|
|
689
|
+
// ----
|
|
690
|
+
// # Model data matching
|
|
691
|
+
// path = Lutaml::Path.parse("contributor[role.type='publisher']")
|
|
692
|
+
// path.match?(data) # Matches against model instance data
|
|
693
|
+
// ----
|
|
75
694
|
|
|
76
|
-
|
|
695
|
+
=== How to match paths
|
|
77
696
|
|
|
78
697
|
The parsed path can be used to match against actual element paths:
|
|
79
698
|
|
|
@@ -103,18 +722,6 @@ relative.match?(["model", "Class"]) # => true
|
|
|
103
722
|
relative.match?(["root", "model", "Class"]) # => true
|
|
104
723
|
----
|
|
105
724
|
|
|
106
|
-
== Path syntax reference
|
|
107
|
-
|
|
108
|
-
The path expression grammar follows these rules:
|
|
109
|
-
|
|
110
|
-
* Path segments are separated by `::`
|
|
111
|
-
* The separator can be escaped with a backslash: `\::`
|
|
112
|
-
* An absolute path starts with `::`
|
|
113
|
-
* Each segment can contain:
|
|
114
|
-
** Regular characters (including Unicode)
|
|
115
|
-
** Wildcards (`*`, `?`)
|
|
116
|
-
** Character classes (`[abc]`)
|
|
117
|
-
** Alternatives (`{pattern1,pattern2}`)
|
|
118
725
|
|
|
119
726
|
== Matching paths with escaped colons
|
|
120
727
|
|
|
@@ -129,7 +736,7 @@ path.match?(["model", "std::string"]) # => true
|
|
|
129
736
|
path.match?(["model", "std", "string"]) # => false
|
|
130
737
|
----
|
|
131
738
|
|
|
132
|
-
|
|
739
|
+
=== Examples of UML element references
|
|
133
740
|
|
|
134
741
|
[source,ruby]
|
|
135
742
|
----
|
|
@@ -150,4 +757,12 @@ path.match?(["model", "std", "string"]) # => false
|
|
|
150
757
|
----
|
|
151
758
|
|
|
152
759
|
These paths can be used to locate elements across UML model hierarchies, making
|
|
153
|
-
it easier to reference and work with model elements programmatically.
|
|
760
|
+
it easier to reference and work with model elements programmatically.
|
|
761
|
+
|
|
762
|
+
== License
|
|
763
|
+
|
|
764
|
+
Copyright Ribose.
|
|
765
|
+
|
|
766
|
+
The `lutaml-path` gem is available as open source under the terms of the MIT
|
|
767
|
+
License.
|
|
768
|
+
|