expressir 2.1.26 → 2.1.28
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/.rubocop_todo.yml +34 -30
- data/README.adoc +290 -0
- data/expressir.gemspec +1 -0
- data/lib/expressir/changes/edition_change.rb +30 -0
- data/lib/expressir/changes/item_change.rb +22 -0
- data/lib/expressir/changes/mapping_change.rb +18 -0
- data/lib/expressir/changes/schema_change.rb +78 -0
- data/lib/expressir/changes.rb +10 -0
- data/lib/expressir/cli.rb +4 -0
- data/lib/expressir/commands/changes.rb +36 -0
- data/lib/expressir/commands/changes_import_eengine.rb +137 -0
- data/lib/expressir/commands/changes_validate.rb +67 -0
- data/lib/expressir/version.rb +1 -1
- data/lib/expressir.rb +9 -0
- metadata +24 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2818d10a5113fdafcb93fef22c7520b2747b0aa6767003239e99266fbe28aacb
|
4
|
+
data.tar.gz: 04d9cc82b902107a07f93114ee0a59f551a0969e158e0fa50a125d49d92dd097
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b435f41cfa13599b1547769c1052c2e9ac800f86c3282ad6ef0a5608ba77b0ee86bf31fceed7c21595019aa79e87c8327f001dea6d03ba14d1dea7a024adc5a8
|
7
|
+
data.tar.gz: 6c93af83e066f0288d41ec7552b4d512a0aa29626dfb8101f7268e92741989704e4131fbf76a976088b05a22187a952459461764bc18cda1f624380a46c92faf
|
data/.rubocop_todo.yml
CHANGED
@@ -1,51 +1,38 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2025-
|
3
|
+
# on 2025-10-13 04:55:00 UTC using RuboCop version 1.81.1.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
8
8
|
|
9
|
-
# Offense count:
|
9
|
+
# Offense count: 565
|
10
10
|
# This cop supports safe autocorrection (--autocorrect).
|
11
11
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
|
12
12
|
# URISchemes: http, https
|
13
13
|
Layout/LineLength:
|
14
14
|
Enabled: false
|
15
15
|
|
16
|
-
# Offense count:
|
16
|
+
# Offense count: 2
|
17
17
|
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
|
18
18
|
Lint/DuplicateBranch:
|
19
19
|
Exclude:
|
20
|
+
- 'lib/expressir/commands/changes_import_eengine.rb'
|
20
21
|
- 'lib/expressir/coverage.rb'
|
21
22
|
|
22
23
|
# Offense count: 2
|
23
24
|
# This cop supports safe autocorrection (--autocorrect).
|
24
|
-
# Configuration parameters:
|
25
|
+
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
|
25
26
|
# NotImplementedExceptions: NotImplementedError
|
26
27
|
Lint/UnusedMethodArgument:
|
27
28
|
Exclude:
|
28
29
|
- 'lib/expressir/express/cache.rb'
|
29
30
|
- 'lib/expressir/express/parser.rb'
|
30
31
|
|
31
|
-
# Offense count:
|
32
|
+
# Offense count: 83
|
32
33
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
33
34
|
Metrics/AbcSize:
|
34
|
-
|
35
|
-
- 'lib/expressir/benchmark.rb'
|
36
|
-
- 'lib/expressir/commands/benchmark.rb'
|
37
|
-
- 'lib/expressir/commands/benchmark_cache.rb'
|
38
|
-
- 'lib/expressir/commands/coverage.rb'
|
39
|
-
- 'lib/expressir/commands/validate.rb'
|
40
|
-
- 'lib/expressir/config.rb'
|
41
|
-
- 'lib/expressir/coverage.rb'
|
42
|
-
- 'lib/expressir/express/formatter.rb'
|
43
|
-
- 'lib/expressir/express/hyperlink_formatter.rb'
|
44
|
-
- 'lib/expressir/express/parser.rb'
|
45
|
-
- 'lib/expressir/express/resolve_references_model_visitor.rb'
|
46
|
-
- 'lib/expressir/express/visitor.rb'
|
47
|
-
- 'lib/expressir/model/declarations/schema.rb'
|
48
|
-
- 'lib/expressir/model/model_element.rb'
|
35
|
+
Enabled: false
|
49
36
|
|
50
37
|
# Offense count: 1
|
51
38
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
|
@@ -53,11 +40,14 @@ Metrics/AbcSize:
|
|
53
40
|
Metrics/BlockLength:
|
54
41
|
Max: 46
|
55
42
|
|
56
|
-
# Offense count:
|
43
|
+
# Offense count: 59
|
57
44
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
58
45
|
Metrics/CyclomaticComplexity:
|
59
46
|
Exclude:
|
60
47
|
- 'lib/expressir/benchmark.rb'
|
48
|
+
- 'lib/expressir/changes/schema_change.rb'
|
49
|
+
- 'lib/expressir/commands/changes_import_eengine.rb'
|
50
|
+
- 'lib/expressir/commands/changes_validate.rb'
|
61
51
|
- 'lib/expressir/commands/coverage.rb'
|
62
52
|
- 'lib/expressir/coverage.rb'
|
63
53
|
- 'lib/expressir/express/formatter.rb'
|
@@ -68,16 +58,19 @@ Metrics/CyclomaticComplexity:
|
|
68
58
|
- 'lib/expressir/model/model_element.rb'
|
69
59
|
- 'spec/support/model_element_helper.rb'
|
70
60
|
|
71
|
-
# Offense count:
|
61
|
+
# Offense count: 110
|
72
62
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
73
63
|
Metrics/MethodLength:
|
74
64
|
Max: 106
|
75
65
|
|
76
|
-
# Offense count:
|
66
|
+
# Offense count: 47
|
77
67
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
78
68
|
Metrics/PerceivedComplexity:
|
79
69
|
Exclude:
|
80
70
|
- 'lib/expressir/benchmark.rb'
|
71
|
+
- 'lib/expressir/changes/schema_change.rb'
|
72
|
+
- 'lib/expressir/commands/changes_import_eengine.rb'
|
73
|
+
- 'lib/expressir/commands/changes_validate.rb'
|
81
74
|
- 'lib/expressir/commands/coverage.rb'
|
82
75
|
- 'lib/expressir/coverage.rb'
|
83
76
|
- 'lib/expressir/express/formatter.rb'
|
@@ -104,7 +97,7 @@ Performance/MapMethodChain:
|
|
104
97
|
- 'spec/expressir/commands/coverage_ignore_files_spec.rb'
|
105
98
|
- 'spec/expressir/coverage_spec.rb'
|
106
99
|
|
107
|
-
# Offense count:
|
100
|
+
# Offense count: 126
|
108
101
|
# Configuration parameters: CountAsOne.
|
109
102
|
RSpec/ExampleLength:
|
110
103
|
Max: 123
|
@@ -120,12 +113,7 @@ RSpec/IndexedLet:
|
|
120
113
|
- 'spec/expressir/model/data_types/set_spec.rb'
|
121
114
|
- 'spec/expressir/model/data_types/string_spec.rb'
|
122
115
|
|
123
|
-
# Offense count:
|
124
|
-
RSpec/IteratedExpectation:
|
125
|
-
Exclude:
|
126
|
-
- 'spec/expressir/schema_manifest_spec.rb'
|
127
|
-
|
128
|
-
# Offense count: 235
|
116
|
+
# Offense count: 257
|
129
117
|
RSpec/MultipleExpectations:
|
130
118
|
Max: 114
|
131
119
|
|
@@ -146,7 +134,23 @@ RSpec/RepeatedExample:
|
|
146
134
|
Exclude:
|
147
135
|
- 'spec/expressir/model/data_types/logical_spec.rb'
|
148
136
|
|
137
|
+
# Offense count: 1
|
138
|
+
# This cop supports safe autocorrection (--autocorrect).
|
139
|
+
# Configuration parameters: EnforcedStyle, AllowComments.
|
140
|
+
# SupportedStyles: empty, nil, both
|
141
|
+
Style/EmptyElse:
|
142
|
+
Exclude:
|
143
|
+
- 'lib/expressir/commands/changes_validate.rb'
|
144
|
+
|
149
145
|
# Offense count: 1
|
150
146
|
Style/MissingRespondToMissing:
|
151
147
|
Exclude:
|
152
148
|
- 'lib/expressir/express/visitor.rb'
|
149
|
+
|
150
|
+
# Offense count: 1
|
151
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
152
|
+
# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments.
|
153
|
+
# AllowedMethods: define_method
|
154
|
+
Style/SymbolProc:
|
155
|
+
Exclude:
|
156
|
+
- 'spec/expressir/commands/changes_import_eengine_spec.rb'
|
data/README.adoc
CHANGED
@@ -76,6 +76,7 @@ $ expressir
|
|
76
76
|
Commands:
|
77
77
|
expressir benchmark FILE_OR_YAML # Benchmark schema loading performance for a file or list of files from YAML
|
78
78
|
expressir benchmark-cache FILE_OR_YAML # Benchmark schema loading with caching
|
79
|
+
expressir changes SUBCOMMAND # Commands for EXPRESS Changes files
|
79
80
|
expressir clean PATH # Strip remarks and prettify EXPRESS schema at PATH
|
80
81
|
expressir format PATH # pretty print EXPRESS schema located at PATH
|
81
82
|
expressir help [COMMAND] # Describe available commands or one specific command
|
@@ -667,6 +668,107 @@ The default text output displays:
|
|
667
668
|
This helps identify areas of your EXPRESS schemas that need documentation
|
668
669
|
improvement.
|
669
670
|
|
671
|
+
=== EXPRESS Changes files
|
672
|
+
|
673
|
+
Expressir provides commands for working with EXPRESS Changes files that track
|
674
|
+
schema modifications across versions.
|
675
|
+
|
676
|
+
==== Validating change files
|
677
|
+
|
678
|
+
The `changes validate` command validates EXPRESS Changes YAML files and
|
679
|
+
optionally normalizes them through round-trip serialization.
|
680
|
+
|
681
|
+
[source, sh]
|
682
|
+
----
|
683
|
+
# Validate a changes file
|
684
|
+
expressir changes validate schema.changes.yaml
|
685
|
+
|
686
|
+
# Validate with verbose output
|
687
|
+
expressir changes validate schema.changes.yaml --verbose
|
688
|
+
|
689
|
+
# Validate and normalize (outputs to stdout)
|
690
|
+
expressir changes validate schema.changes.yaml --normalize
|
691
|
+
|
692
|
+
# Validate and normalize in-place
|
693
|
+
expressir changes validate schema.changes.yaml --normalize --in-place
|
694
|
+
|
695
|
+
# Validate and save normalized output to a new file
|
696
|
+
expressir changes validate schema.changes.yaml --normalize --output normalized.yaml
|
697
|
+
----
|
698
|
+
|
699
|
+
[options="header"]
|
700
|
+
|===
|
701
|
+
| Option | Description
|
702
|
+
| `--normalize` | Normalize file through round-trip serialization
|
703
|
+
| `--in-place` | Update file in place (requires `--normalize`)
|
704
|
+
| `--output PATH` | Output file path for normalized output
|
705
|
+
| `--verbose` | Show verbose output with validation details
|
706
|
+
|===
|
707
|
+
|
708
|
+
The validate command performs the following checks:
|
709
|
+
|
710
|
+
. Verifies the YAML file can be parsed
|
711
|
+
. Validates against the SchemaChange model structure
|
712
|
+
. Ensures all required fields are present
|
713
|
+
. Checks that change items have valid types
|
714
|
+
|
715
|
+
When using `--normalize`, the command:
|
716
|
+
|
717
|
+
. Loads the file and validates it
|
718
|
+
. Serializes it back to YAML with consistent formatting
|
719
|
+
. Either outputs to stdout, saves in-place, or writes to a new file
|
720
|
+
|
721
|
+
This is useful for:
|
722
|
+
|
723
|
+
* **Standardizing formatting**: Ensures consistent YAML structure
|
724
|
+
* **Catching errors early**: Validates before committing changes
|
725
|
+
* **Cleaning up files**: Removes inconsistencies in formatting
|
726
|
+
|
727
|
+
==== Importing from eengine XML
|
728
|
+
|
729
|
+
The `changes import-eengine` command converts eengine comparison XML files to
|
730
|
+
EXPRESS Changes YAML format.
|
731
|
+
|
732
|
+
The eengine compare XML format is created through
|
733
|
+
`exp-engine-engine/kernel/compare.lisp` in the EXPRESS Engine code. Expressir is
|
734
|
+
compatible with v5.2.7 of eengine output.
|
735
|
+
|
736
|
+
[source, sh]
|
737
|
+
----
|
738
|
+
# Import and output to stdout
|
739
|
+
expressir changes import-eengine comparison.xml schema_name "2"
|
740
|
+
|
741
|
+
# Import and save to file
|
742
|
+
expressir changes import-eengine comparison.xml schema_name "2" -o output.yaml
|
743
|
+
|
744
|
+
# Import with verbose output
|
745
|
+
expressir changes import-eengine comparison.xml schema_name "2" -o output.yaml --verbose
|
746
|
+
|
747
|
+
# Append to existing changes file
|
748
|
+
expressir changes import-eengine comparison.xml schema_name "3" -o existing.yaml
|
749
|
+
----
|
750
|
+
|
751
|
+
[options="header"]
|
752
|
+
|===
|
753
|
+
| Option | Description
|
754
|
+
| `-o, --output PATH` | Output YAML file path (stdout if not specified)
|
755
|
+
| `--verbose` | Show verbose output
|
756
|
+
|===
|
757
|
+
|
758
|
+
The import command:
|
759
|
+
|
760
|
+
. Parses the eengine XML comparison file
|
761
|
+
. Extracts additions, modifications, and deletions
|
762
|
+
. Creates or updates an EXPRESS Changes YAML file
|
763
|
+
. Supports appending new versions to existing files
|
764
|
+
|
765
|
+
When the output file already exists:
|
766
|
+
|
767
|
+
* **Same version**: Replaces the existing edition with that version
|
768
|
+
* **New version**: Adds a new edition to the file
|
769
|
+
|
770
|
+
This allows you to build up a complete change history incrementally.
|
771
|
+
|
670
772
|
|
671
773
|
== Usage: Ruby
|
672
774
|
|
@@ -1063,6 +1165,194 @@ expressir coverage schemas.yml --format json --exclude=TYPE:SELECT
|
|
1063
1165
|
|
1064
1166
|
|
1065
1167
|
|
1168
|
+
== Working with EXPRESS Changes
|
1169
|
+
|
1170
|
+
=== General
|
1171
|
+
|
1172
|
+
Expressir provides the `Changes` module for managing and tracking changes to
|
1173
|
+
EXPRESS schemas across versions. This module implements the EXPRESS Changes YAML
|
1174
|
+
format defined by ELF (Express Language Foundation).
|
1175
|
+
|
1176
|
+
The Changes module enables:
|
1177
|
+
|
1178
|
+
* Loading and saving schema change records from/to YAML files
|
1179
|
+
* Programmatic creation and manipulation of change records
|
1180
|
+
* Smart edition handling (replace same version, add new version)
|
1181
|
+
* Support for all change types: additions, modifications, deletions
|
1182
|
+
* Support for mapping changes in ARM/MIM schemas
|
1183
|
+
|
1184
|
+
=== Reading change files
|
1185
|
+
|
1186
|
+
Load an existing schema change file:
|
1187
|
+
|
1188
|
+
[source,ruby]
|
1189
|
+
----
|
1190
|
+
require "expressir/changes"
|
1191
|
+
|
1192
|
+
# Load from file
|
1193
|
+
change_schema = Expressir::Changes::SchemaChange.from_file("schema.changes.yaml")
|
1194
|
+
|
1195
|
+
# Access schema name
|
1196
|
+
puts "Schema: #{change_schema.schema}"
|
1197
|
+
|
1198
|
+
# Iterate through change editions
|
1199
|
+
change_schema.editions.each do |edition|
|
1200
|
+
puts "Version #{edition.version}: #{edition.description}"
|
1201
|
+
|
1202
|
+
# Access changes by type
|
1203
|
+
puts " Additions: #{edition.additions.size}" if edition.additions
|
1204
|
+
puts " Modifications: #{edition.modifications.size}" if edition.modifications
|
1205
|
+
puts " Deletions: #{edition.deletions.size}" if edition.deletions
|
1206
|
+
end
|
1207
|
+
----
|
1208
|
+
|
1209
|
+
=== Creating change records
|
1210
|
+
|
1211
|
+
Create a new change schema programmatically:
|
1212
|
+
|
1213
|
+
[source,ruby]
|
1214
|
+
----
|
1215
|
+
# Create a new empty change schema
|
1216
|
+
change_schema = Expressir::Changes::SchemaChange.new(schema: "my_schema")
|
1217
|
+
|
1218
|
+
# Create change items
|
1219
|
+
new_entity = Expressir::Changes::ItemChange.new(
|
1220
|
+
type: "ENTITY",
|
1221
|
+
name: "new_entity_name"
|
1222
|
+
)
|
1223
|
+
|
1224
|
+
modified_function = Expressir::Changes::ItemChange.new(
|
1225
|
+
type: "FUNCTION",
|
1226
|
+
name: "modified_function",
|
1227
|
+
description: "Updated parameters"
|
1228
|
+
)
|
1229
|
+
|
1230
|
+
# Add a change edition
|
1231
|
+
changes = {
|
1232
|
+
additions: [new_entity],
|
1233
|
+
modifications: [modified_function],
|
1234
|
+
deletions: []
|
1235
|
+
}
|
1236
|
+
|
1237
|
+
change_schema.add_or_update_edition(
|
1238
|
+
"2",
|
1239
|
+
"Added new entity and modified function",
|
1240
|
+
changes
|
1241
|
+
)
|
1242
|
+
|
1243
|
+
# Save to file
|
1244
|
+
change_schema.to_file("my_schema.changes.yaml")
|
1245
|
+
----
|
1246
|
+
|
1247
|
+
=== Updating existing change files
|
1248
|
+
|
1249
|
+
The `add_or_update_edition` method provides smart handling:
|
1250
|
+
|
1251
|
+
* **Same version**: Replaces the existing edition
|
1252
|
+
* **Different version**: Adds a new edition
|
1253
|
+
|
1254
|
+
[source,ruby]
|
1255
|
+
----
|
1256
|
+
# Load existing change file
|
1257
|
+
change_schema = Expressir::Changes::SchemaChange.from_file("schema.changes.yaml")
|
1258
|
+
|
1259
|
+
# Add a new version
|
1260
|
+
changes = {
|
1261
|
+
modifications: [
|
1262
|
+
Expressir::Changes::ItemChange.new(type: "TYPE", name: "updated_type")
|
1263
|
+
]
|
1264
|
+
}
|
1265
|
+
change_schema.add_or_update_edition("3", "Modified type definition", changes)
|
1266
|
+
|
1267
|
+
# Or replace existing version
|
1268
|
+
change_schema.add_or_update_edition("2", "Revised description", changes)
|
1269
|
+
|
1270
|
+
# Save changes
|
1271
|
+
change_schema.to_file("schema.changes.yaml")
|
1272
|
+
----
|
1273
|
+
|
1274
|
+
=== Change item fields
|
1275
|
+
|
1276
|
+
Change items support the following fields:
|
1277
|
+
|
1278
|
+
`type`:: (Required) The EXPRESS construct type (ENTITY, TYPE, FUNCTION, etc.)
|
1279
|
+
`name`:: (Required) The name of the construct
|
1280
|
+
`description`:: (Optional) Additional details about the change
|
1281
|
+
`interfaced_items`:: (Optional) For REFERENCE_FROM items
|
1282
|
+
|
1283
|
+
[source,ruby]
|
1284
|
+
----
|
1285
|
+
item = Expressir::Changes::ItemChange.new(
|
1286
|
+
type: "REFERENCE_FROM",
|
1287
|
+
name: "measure_schema",
|
1288
|
+
interfaced_items: "length_measure"
|
1289
|
+
)
|
1290
|
+
----
|
1291
|
+
|
1292
|
+
=== Change edition fields
|
1293
|
+
|
1294
|
+
Change editions support categorizing changes into:
|
1295
|
+
|
1296
|
+
`additions`:: New elements added to the schema
|
1297
|
+
`modifications`:: Existing elements that were modified
|
1298
|
+
`deletions`:: Elements removed from the schema
|
1299
|
+
`mapping`:: Mapping-related changes (for ARM/MIM modules)
|
1300
|
+
`changes`:: General changes (alternative to mapping)
|
1301
|
+
|
1302
|
+
[source,ruby]
|
1303
|
+
----
|
1304
|
+
edition = Expressir::Changes::EditionChange.new(
|
1305
|
+
version: "2",
|
1306
|
+
description: "Added support for new functionality",
|
1307
|
+
additions: [item1, item2],
|
1308
|
+
modifications: [item3],
|
1309
|
+
deletions: [item4],
|
1310
|
+
mapping: [mapping_change]
|
1311
|
+
)
|
1312
|
+
----
|
1313
|
+
|
1314
|
+
=== Mapping changes
|
1315
|
+
|
1316
|
+
For ARM/MIM schema mappings, use `MappingChange`:
|
1317
|
+
|
1318
|
+
[source,ruby]
|
1319
|
+
----
|
1320
|
+
mapping_change = Expressir::Changes::MappingChange.new(
|
1321
|
+
change: "Entity_name ENTITY mapping updated"
|
1322
|
+
)
|
1323
|
+
----
|
1324
|
+
|
1325
|
+
=== Example change file format
|
1326
|
+
|
1327
|
+
[source,yaml]
|
1328
|
+
----
|
1329
|
+
---
|
1330
|
+
schema: support_resource_schema
|
1331
|
+
editions:
|
1332
|
+
- version: '2'
|
1333
|
+
description: |-
|
1334
|
+
The definitions of the following EXPRESS entity data types were modified:
|
1335
|
+
|
1336
|
+
* action;
|
1337
|
+
* action_directive;
|
1338
|
+
* action_method.
|
1339
|
+
additions:
|
1340
|
+
- type: FUNCTION
|
1341
|
+
name: type_check_function
|
1342
|
+
modifications:
|
1343
|
+
- type: FUNCTION
|
1344
|
+
name: bag_to_set
|
1345
|
+
- version: '4'
|
1346
|
+
description: |-
|
1347
|
+
Added support for external element references.
|
1348
|
+
additions:
|
1349
|
+
- type: ENTITY
|
1350
|
+
name: component_path_shape_aspect
|
1351
|
+
modifications:
|
1352
|
+
- type: FUNCTION
|
1353
|
+
name: type_check_function
|
1354
|
+
----
|
1355
|
+
|
1066
1356
|
== Contributing
|
1067
1357
|
|
1068
1358
|
First, thank you for contributing! We love pull requests from everyone. By
|
data/expressir.gemspec
CHANGED
@@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_dependency "csv"
|
37
37
|
spec.add_dependency "liquid"
|
38
38
|
spec.add_dependency "lutaml-model"
|
39
|
+
spec.add_dependency "moxml"
|
39
40
|
spec.add_dependency "parslet", "~> 2.0"
|
40
41
|
spec.add_dependency "ruby-progressbar", "~> 1.11"
|
41
42
|
spec.add_dependency "table_tennis"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "lutaml/model"
|
4
|
+
require_relative "item_change"
|
5
|
+
require_relative "mapping_change"
|
6
|
+
|
7
|
+
module Expressir
|
8
|
+
module Changes
|
9
|
+
# Represents a version edition of schema changes
|
10
|
+
class EditionChange < Lutaml::Model::Serializable
|
11
|
+
attribute :version, :string
|
12
|
+
attribute :description, :string
|
13
|
+
attribute :additions, ItemChange, collection: true
|
14
|
+
attribute :modifications, ItemChange, collection: true
|
15
|
+
attribute :deletions, ItemChange, collection: true
|
16
|
+
attribute :mapping, MappingChange, collection: true
|
17
|
+
attribute :changes, MappingChange, collection: true
|
18
|
+
|
19
|
+
yaml do
|
20
|
+
map "version", to: :version
|
21
|
+
map "description", to: :description
|
22
|
+
map "additions", to: :additions
|
23
|
+
map "modifications", to: :modifications
|
24
|
+
map "deletions", to: :deletions
|
25
|
+
map "mapping", to: :mapping
|
26
|
+
map "changes", to: :changes
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "lutaml/model"
|
4
|
+
|
5
|
+
module Expressir
|
6
|
+
module Changes
|
7
|
+
# Represents a single change to an EXPRESS construct
|
8
|
+
class ItemChange < Lutaml::Model::Serializable
|
9
|
+
attribute :type, :string
|
10
|
+
attribute :name, :string
|
11
|
+
attribute :description, :string
|
12
|
+
attribute :interfaced_items, :string
|
13
|
+
|
14
|
+
yaml do
|
15
|
+
map "type", to: :type
|
16
|
+
map "name", to: :name
|
17
|
+
map "description", to: :description
|
18
|
+
map "interfaced_items", to: :interfaced_items
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "lutaml/model"
|
4
|
+
|
5
|
+
module Expressir
|
6
|
+
module Changes
|
7
|
+
# Represents a mapping change entry
|
8
|
+
class MappingChange < Lutaml::Model::Serializable
|
9
|
+
attribute :change, :string
|
10
|
+
attribute :description, :string
|
11
|
+
|
12
|
+
yaml do
|
13
|
+
map "change", to: :change
|
14
|
+
map "description", to: :description
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "lutaml/model"
|
4
|
+
require_relative "edition_change"
|
5
|
+
|
6
|
+
module Expressir
|
7
|
+
module Changes
|
8
|
+
# Represents changes to an EXPRESS schema across multiple versions
|
9
|
+
class SchemaChange < Lutaml::Model::Serializable
|
10
|
+
attribute :schema, :string
|
11
|
+
attribute :editions, EditionChange, collection: true
|
12
|
+
|
13
|
+
yaml do
|
14
|
+
map "schema", to: :schema
|
15
|
+
map "editions", to: :editions
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
# Load a SchemaChange from a YAML file
|
20
|
+
#
|
21
|
+
# @param path [String] Path to the YAML file
|
22
|
+
# @return [SchemaChange] The loaded schema changes
|
23
|
+
def from_file(path)
|
24
|
+
content = File.read(path)
|
25
|
+
# Handle empty or minimal YAML files
|
26
|
+
return new if content.strip == "---" || content.strip.empty?
|
27
|
+
|
28
|
+
from_yaml(content)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add or update a change edition in this schema
|
33
|
+
#
|
34
|
+
# @param version [String] Version number
|
35
|
+
# @param description [String] Description of changes
|
36
|
+
# @param changes [Hash] Hash with :additions, :modifications, :deletions
|
37
|
+
# @return [EditionChange] The added or updated edition
|
38
|
+
def add_or_update_edition(version, description, changes)
|
39
|
+
version_str = version.to_s
|
40
|
+
|
41
|
+
# Initialize editions array if nil
|
42
|
+
self.editions ||= []
|
43
|
+
|
44
|
+
# Find existing edition with this version
|
45
|
+
existing_index = editions.find_index do |ed|
|
46
|
+
ed.version == version_str
|
47
|
+
end
|
48
|
+
|
49
|
+
# Create new edition
|
50
|
+
edition = EditionChange.new(
|
51
|
+
version: version_str,
|
52
|
+
description: description,
|
53
|
+
additions: changes[:additions] || [],
|
54
|
+
modifications: changes[:modifications] || [],
|
55
|
+
deletions: changes[:deletions] || [],
|
56
|
+
)
|
57
|
+
|
58
|
+
if existing_index
|
59
|
+
# Replace existing edition with same version
|
60
|
+
editions[existing_index] = edition
|
61
|
+
else
|
62
|
+
# Add new edition
|
63
|
+
editions << edition
|
64
|
+
end
|
65
|
+
|
66
|
+
edition
|
67
|
+
end
|
68
|
+
|
69
|
+
# Save this SchemaChange to a YAML file
|
70
|
+
#
|
71
|
+
# @param path [String] Path where to save the file
|
72
|
+
# @return [Integer] Number of bytes written
|
73
|
+
def to_file(path)
|
74
|
+
File.write(path, to_yaml)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/expressir/cli.rb
CHANGED
@@ -8,6 +8,7 @@ require_relative "commands/benchmark"
|
|
8
8
|
require_relative "commands/benchmark_cache"
|
9
9
|
require_relative "commands/validate"
|
10
10
|
require_relative "commands/coverage"
|
11
|
+
require_relative "commands/changes"
|
11
12
|
require_relative "commands/version"
|
12
13
|
|
13
14
|
module Expressir
|
@@ -73,6 +74,9 @@ module Expressir
|
|
73
74
|
Commands::Coverage.new(options).run(paths)
|
74
75
|
end
|
75
76
|
|
77
|
+
desc "changes SUBCOMMAND", "Commands for EXPRESS Changes files"
|
78
|
+
subcommand "changes", Commands::Changes
|
79
|
+
|
76
80
|
desc "version", "Expressir Version"
|
77
81
|
def version
|
78
82
|
Commands::Version.new(options).run
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
|
5
|
+
module Expressir
|
6
|
+
module Commands
|
7
|
+
# Thor subcommand for EXPRESS Changes file operations
|
8
|
+
class Changes < Thor
|
9
|
+
desc "validate PATH", "Validate EXPRESS Changes YAML file"
|
10
|
+
method_option :normalize, type: :boolean,
|
11
|
+
desc: "Normalize file through round-trip serialization"
|
12
|
+
method_option :in_place, type: :boolean,
|
13
|
+
desc: "Update file in place (requires --normalize)"
|
14
|
+
method_option :output, type: :string,
|
15
|
+
desc: "Output file path (for normalized output)"
|
16
|
+
method_option :verbose, type: :boolean,
|
17
|
+
desc: "Show verbose output"
|
18
|
+
def validate(path)
|
19
|
+
require_relative "changes_validate"
|
20
|
+
ChangesValidate.new(options).run(path)
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "import-eengine INPUT_XML SCHEMA_NAME VERSION",
|
24
|
+
"Import eengine comparison XML to EXPRESS Changes YAML"
|
25
|
+
method_option :output, type: :string, aliases: "-o",
|
26
|
+
desc: "Output YAML file path (stdout if not specified)"
|
27
|
+
method_option :verbose, type: :boolean,
|
28
|
+
desc: "Show verbose output"
|
29
|
+
def import_eengine(input_xml, schema_name, version)
|
30
|
+
require_relative "changes_import_eengine"
|
31
|
+
ChangesImportEengine.call(input_xml, options[:output], schema_name,
|
32
|
+
version, **options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require "moxml"
|
5
|
+
|
6
|
+
module Expressir
|
7
|
+
module Commands
|
8
|
+
# Command to import eengine comparison XML to EXPRESS Changes YAML
|
9
|
+
class ChangesImportEengine < Base
|
10
|
+
def self.call(input_file, output_file, schema_name, version, **options)
|
11
|
+
new.call(input_file, output_file, schema_name, version, **options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(input_file, output_file, schema_name, version, **options)
|
15
|
+
require "expressir/changes"
|
16
|
+
|
17
|
+
# Parse the eengine XML using Moxml
|
18
|
+
xml_content = File.read(input_file)
|
19
|
+
xml_doc = Moxml.new.parse(xml_content)
|
20
|
+
|
21
|
+
# Detect XML mode from root element
|
22
|
+
xml_mode = detect_xml_mode(xml_doc)
|
23
|
+
|
24
|
+
# Extract changes from XML
|
25
|
+
changes = extract_changes(xml_doc, xml_mode)
|
26
|
+
description = generate_description(xml_doc, xml_mode)
|
27
|
+
|
28
|
+
# Load or create change schema
|
29
|
+
change_schema = if output_file && File.exist?(output_file) && File.size(output_file).positive?
|
30
|
+
Expressir::Changes::SchemaChange.from_file(output_file)
|
31
|
+
else
|
32
|
+
Expressir::Changes::SchemaChange.new(schema: schema_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Add or update edition
|
36
|
+
change_schema.add_or_update_edition(version, description, changes)
|
37
|
+
|
38
|
+
# Save to file
|
39
|
+
if output_file
|
40
|
+
change_schema.to_file(output_file)
|
41
|
+
puts "Change YAML file written to: #{output_file}" if options[:verbose]
|
42
|
+
else
|
43
|
+
puts change_schema.to_yaml
|
44
|
+
end
|
45
|
+
|
46
|
+
change_schema
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Detect XML mode from root element (arm, mim, or schema)
|
52
|
+
def detect_xml_mode(xml_doc)
|
53
|
+
root = xml_doc.root
|
54
|
+
return nil unless root
|
55
|
+
|
56
|
+
case root.name
|
57
|
+
when "arm.changes"
|
58
|
+
"arm"
|
59
|
+
when "mim.changes"
|
60
|
+
"mim"
|
61
|
+
when "schema.changes"
|
62
|
+
"schema"
|
63
|
+
else
|
64
|
+
# Default to schema mode if unrecognized
|
65
|
+
"schema"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def extract_changes(xml_doc, xml_mode)
|
70
|
+
{
|
71
|
+
additions: extract_added_objects(xml_doc, xml_mode),
|
72
|
+
modifications: extract_modified_objects(xml_doc, xml_mode),
|
73
|
+
deletions: extract_deleted_objects(xml_doc, xml_mode),
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def extract_modified_objects(xml_doc, xml_mode)
|
78
|
+
xpath = "//#{xml_mode}.modifications/modified.object"
|
79
|
+
xml_doc.xpath(xpath).map do |node|
|
80
|
+
extract_item_change(node)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def extract_added_objects(xml_doc, xml_mode)
|
85
|
+
xpath = "//#{xml_mode}.additions/modified.object"
|
86
|
+
xml_doc.xpath(xpath).map do |node|
|
87
|
+
extract_item_change(node)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def extract_deleted_objects(xml_doc, xml_mode)
|
92
|
+
xpath = "//#{xml_mode}.deletions/modified.object"
|
93
|
+
xml_doc.xpath(xpath).map do |node|
|
94
|
+
extract_item_change(node)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def extract_item_change(node)
|
99
|
+
item_change = Expressir::Changes::ItemChange.new(
|
100
|
+
type: node["type"],
|
101
|
+
name: node["name"],
|
102
|
+
)
|
103
|
+
|
104
|
+
# Extract interfaced.items attribute if present (for interface changes)
|
105
|
+
if node["interfaced.items"]
|
106
|
+
item_change.interfaced_items = node["interfaced.items"]
|
107
|
+
end
|
108
|
+
|
109
|
+
item_change
|
110
|
+
end
|
111
|
+
|
112
|
+
def generate_description(xml_doc, xml_mode)
|
113
|
+
parts = []
|
114
|
+
|
115
|
+
# Get descriptions from modifications
|
116
|
+
xml_doc.xpath("//#{xml_mode}.modifications/modified.object/description").each do |desc|
|
117
|
+
text = desc.text.strip
|
118
|
+
parts << text unless text.empty?
|
119
|
+
end
|
120
|
+
|
121
|
+
# Get descriptions from additions
|
122
|
+
xml_doc.xpath("//#{xml_mode}.additions/modified.object/description").each do |desc|
|
123
|
+
text = desc.text.strip
|
124
|
+
parts << text unless text.empty?
|
125
|
+
end
|
126
|
+
|
127
|
+
# Get descriptions from deletions
|
128
|
+
xml_doc.xpath("//#{xml_mode}.deletions/modified.object/description").each do |desc|
|
129
|
+
text = desc.text.strip
|
130
|
+
parts << text unless text.empty?
|
131
|
+
end
|
132
|
+
|
133
|
+
parts.join("\n\n")
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
module Expressir
|
6
|
+
module Commands
|
7
|
+
# Command to validate and normalize EXPRESS Changes YAML files
|
8
|
+
class ChangesValidate < Base
|
9
|
+
def run(path)
|
10
|
+
require "expressir/changes"
|
11
|
+
|
12
|
+
# Check if file exists
|
13
|
+
unless File.exist?(path)
|
14
|
+
exit_with_error("File not found: #{path}")
|
15
|
+
end
|
16
|
+
|
17
|
+
# Validate --in-place requires --normalize
|
18
|
+
if options[:in_place] && !options[:normalize]
|
19
|
+
exit_with_error("--in-place requires --normalize flag")
|
20
|
+
end
|
21
|
+
|
22
|
+
# Validate --in-place and --output are mutually exclusive
|
23
|
+
if options[:in_place] && options[:output]
|
24
|
+
exit_with_error("Cannot use both --in-place and --output")
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
# Load and validate the file
|
29
|
+
say "Validating #{path}..." if options[:verbose]
|
30
|
+
schema_change = Expressir::Changes::SchemaChange.from_file(path)
|
31
|
+
|
32
|
+
say "✓ File is valid" if options[:verbose]
|
33
|
+
say " Schema: #{schema_change.schema}" if options[:verbose]
|
34
|
+
say " Editions: #{schema_change.editions.length}" if options[:verbose]
|
35
|
+
|
36
|
+
# Normalize if requested
|
37
|
+
if options[:normalize]
|
38
|
+
say "Normalizing through round-trip serialization..." if options[:verbose]
|
39
|
+
|
40
|
+
output_path = if options[:in_place]
|
41
|
+
path
|
42
|
+
elsif options[:output]
|
43
|
+
options[:output]
|
44
|
+
else
|
45
|
+
# Output to stdout
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
if output_path
|
50
|
+
schema_change.to_file(output_path)
|
51
|
+
say "✓ Normalized file written to: #{output_path}"
|
52
|
+
else
|
53
|
+
# Output to stdout
|
54
|
+
puts schema_change.to_yaml
|
55
|
+
end
|
56
|
+
else
|
57
|
+
say "✓ File is valid"
|
58
|
+
end
|
59
|
+
rescue Psych::SyntaxError => e
|
60
|
+
exit_with_error("Invalid YAML syntax: #{e.message}")
|
61
|
+
rescue StandardError => e
|
62
|
+
exit_with_error("Validation failed: #{e.message}")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/expressir/version.rb
CHANGED
data/lib/expressir.rb
CHANGED
@@ -170,11 +170,20 @@ module Expressir
|
|
170
170
|
autoload :Benchmark, "expressir/commands/benchmark"
|
171
171
|
autoload :BenchmarkCache, "expressir/commands/benchmark_cache"
|
172
172
|
autoload :Clean, "expressir/commands/clean"
|
173
|
+
autoload :ImportComparison, "expressir/commands/import_comparison"
|
173
174
|
autoload :Coverage, "expressir/commands/coverage"
|
174
175
|
autoload :Format, "expressir/commands/format"
|
175
176
|
autoload :Validate, "expressir/commands/validate"
|
176
177
|
autoload :Version, "expressir/commands/version"
|
177
178
|
end
|
179
|
+
|
180
|
+
# Autoload for Changes module classes
|
181
|
+
module Changes
|
182
|
+
autoload :SchemaChange, "expressir/changes/schema_change"
|
183
|
+
autoload :EditionChange, "expressir/changes/edition_change"
|
184
|
+
autoload :ItemChange, "expressir/changes/item_change"
|
185
|
+
autoload :MappingChange, "expressir/changes/mapping_change"
|
186
|
+
end
|
178
187
|
end
|
179
188
|
|
180
189
|
require_relative "expressir/model"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: expressir
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.28
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-10-
|
11
|
+
date: 2025-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: moxml
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: parslet
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -169,10 +183,18 @@ files:
|
|
169
183
|
- expressir.gemspec
|
170
184
|
- lib/expressir.rb
|
171
185
|
- lib/expressir/benchmark.rb
|
186
|
+
- lib/expressir/changes.rb
|
187
|
+
- lib/expressir/changes/edition_change.rb
|
188
|
+
- lib/expressir/changes/item_change.rb
|
189
|
+
- lib/expressir/changes/mapping_change.rb
|
190
|
+
- lib/expressir/changes/schema_change.rb
|
172
191
|
- lib/expressir/cli.rb
|
173
192
|
- lib/expressir/commands/base.rb
|
174
193
|
- lib/expressir/commands/benchmark.rb
|
175
194
|
- lib/expressir/commands/benchmark_cache.rb
|
195
|
+
- lib/expressir/commands/changes.rb
|
196
|
+
- lib/expressir/commands/changes_import_eengine.rb
|
197
|
+
- lib/expressir/commands/changes_validate.rb
|
176
198
|
- lib/expressir/commands/clean.rb
|
177
199
|
- lib/expressir/commands/coverage.rb
|
178
200
|
- lib/expressir/commands/format.rb
|