ea 0.6.2 → 0.6.4

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.
@@ -1,221 +1,218 @@
1
- export interface SpaCardinality {
2
- min?: string
3
- max?: string
1
+ export interface SpaStatistics {
2
+ packages: number
3
+ classes: number
4
+ attributes: number
5
+ operations: number
6
+ associations: number
7
+ diagrams: number
4
8
  }
5
9
 
6
- export interface SpaAttribute {
7
- id: string
8
- name: string
9
- type: string
10
- visibility?: string
11
- owner: string
12
- ownerName: string
13
- cardinality?: SpaCardinality
14
- definition?: string
15
- stereotypes: string[]
16
- isStatic: boolean
17
- isReadOnly: boolean
18
- defaultValue?: string
10
+ export interface SpaLogoVariant {
11
+ path?: string
12
+ url?: string
19
13
  }
20
14
 
21
- export interface SpaAssociationEnd {
22
- class?: string
23
- className?: string
24
- role?: string
25
- cardinality?: SpaCardinality
26
- aggregation?: string
15
+ export interface SpaLogoConfig {
16
+ light: SpaLogoVariant
17
+ dark: SpaLogoVariant
27
18
  }
28
19
 
29
- export interface SpaAssociation {
30
- id: string
31
- xmiId: string
32
- name: string
33
- type: string
34
- definition?: string
35
- source?: SpaAssociationEnd
36
- target?: SpaAssociationEnd
20
+ export interface SpaLogos {
21
+ square?: SpaLogoConfig
22
+ long?: SpaLogoConfig
37
23
  }
38
24
 
39
- export interface SpaOperation {
40
- id: string
41
- name: string
42
- visibility?: string
43
- returnType?: string
44
- owner: string
45
- ownerName: string
46
- parameters: SpaParameter[]
47
- isStatic: boolean
48
- isAbstract: boolean
25
+ export interface SpaAppearance {
26
+ logos?: SpaLogos
49
27
  }
50
28
 
51
- export interface SpaParameter {
52
- name: string
53
- type?: string
54
- direction?: string
29
+ export interface SpaMetadata {
30
+ id?: string
31
+ title?: string
32
+ description?: string
33
+ version?: string
34
+ generated?: string
35
+ generator?: string
36
+ createdDate?: string
37
+ modifiedDate?: string
38
+ sourceFormat?: string
39
+ sourceTool?: string
40
+ sourcePath?: string
41
+ appearance?: SpaAppearance
42
+ statistics?: SpaStatistics
55
43
  }
56
44
 
57
- export interface SpaLiteral {
45
+ export interface SpaPackageTreeNode {
46
+ id: string
58
47
  name: string
59
- definition?: string
60
- }
61
-
62
- export interface SpaInheritedAttribute {
63
- attributeId: string
64
- attribute: SpaAttribute
65
- inheritedFrom: string
66
- inheritedFromName: string
67
- parentOrder: number
48
+ parentId?: string
49
+ childIds: string[]
50
+ classifierIds: string[]
51
+ diagramIds: string[]
68
52
  }
69
53
 
70
- export interface SpaInheritedAssociation {
71
- associationId: string
72
- inheritedFrom: string
73
- inheritedFromName: string
74
- parentOrder: number
75
- localRole: string
54
+ export interface SpaPackageTree {
55
+ rootIds: string[]
56
+ nodes: SpaPackageTreeNode[]
76
57
  }
77
58
 
78
- export interface SpaClass {
59
+ export interface SpaSkeletonEntry {
79
60
  id: string
80
- xmiId: string
81
61
  name: string
82
- qualifiedName: string
83
- type: string
84
- package?: string
85
- stereotypes: string[]
86
- definition?: string
87
- attributes: string[]
88
- operations: string[]
89
- associations: string[]
90
- generalizations: string[]
91
- specializations: string[]
92
- isAbstract: boolean
93
- literals: SpaLiteral[]
94
- inheritedAttributes: SpaInheritedAttribute[]
95
- inheritedAssociations: SpaInheritedAssociation[]
62
+ kind: string
63
+ packageId?: string
64
+ qualifiedName?: string
65
+ shardUrl: string
96
66
  }
97
67
 
98
- export interface SpaPackage {
68
+ export interface SpaShard {
99
69
  id: string
100
- xmiId: string
101
- name: string
102
- path: string
103
- definition?: string
104
- stereotypes: string[]
105
- classes: string[]
106
- subPackages: string[]
107
- diagrams: string[]
108
- parent?: string
70
+ kind: string
71
+ payload: any
109
72
  }
110
73
 
111
- export interface SpaTreeClassRef {
74
+ export interface SpaSearchEntry {
112
75
  id: string
76
+ kind: string
113
77
  name: string
114
- stereotypes: string[]
78
+ qualifiedName: string
79
+ package: string
80
+ content: string
81
+ boost?: number
115
82
  }
116
83
 
117
- export interface SpaPackageTreeNode {
84
+ export interface SpaSearchIndex {
85
+ version: string
86
+ fields: string[]
87
+ entries: SpaSearchEntry[]
88
+ }
89
+
90
+ export interface SpaAnnotation {
118
91
  id: string
119
- name: string
120
- path: string
121
- stereotypes: string[]
122
- classCount: number
123
- classes: SpaTreeClassRef[]
124
- children: SpaPackageTreeNode[]
92
+ kind: string
93
+ body?: string
125
94
  }
126
95
 
127
- export interface SpaDiagram {
96
+ export interface SpaPropertyPayload {
128
97
  id: string
129
- xmiId: string
130
98
  name: string
131
- type: string
132
- package?: string
133
- objectCount: number
134
- linkCount: number
135
- svg?: string
99
+ ownerId: string
100
+ typeName?: string
101
+ qualifiedName?: string
102
+ multiplicityLower?: number
103
+ multiplicityUpper?: number
104
+ isDerived: boolean
105
+ isReadonly: boolean
106
+ visibility?: string
107
+ stereotypeRefs: string[]
108
+ annotations: SpaAnnotation[]
136
109
  }
137
110
 
138
- export interface SpaStatistics {
139
- packages: number
140
- classes: number
141
- associations: number
142
- attributes: number
143
- operations: number
111
+ export interface SpaParameterPayload {
112
+ name?: string
113
+ direction?: string
114
+ typeName?: string
115
+ multiplicityLower?: number
116
+ multiplicityUpper?: number
117
+ defaultValue?: string
144
118
  }
145
119
 
146
- export interface SpaLogoVariant {
147
- path?: string
148
- url?: string
120
+ export interface SpaOperationPayload {
121
+ id: string
122
+ name: string
123
+ ownerId: string
124
+ qualifiedName?: string
125
+ returnTypeName?: string
126
+ isStatic: boolean
127
+ isAbstract: boolean
128
+ visibility?: string
129
+ parameters: SpaParameterPayload[]
130
+ stereotypeRefs: string[]
131
+ annotations: SpaAnnotation[]
149
132
  }
150
133
 
151
- export interface SpaLogoConfig {
152
- light: SpaLogoVariant
153
- dark: SpaLogoVariant
134
+ export interface SpaGeneralizationStub {
135
+ id: string
136
+ targetId: string
154
137
  }
155
138
 
156
- export interface SpaLogos {
157
- square?: SpaLogoConfig
158
- long?: SpaLogoConfig
139
+ export interface SpaAssociationStub {
140
+ id: string
141
+ name?: string
142
+ sourceId: string
143
+ targetId: string
144
+ thisEndRoleName?: string
145
+ otherEndRoleName?: string
146
+ otherEndId: string
147
+ otherEndMultiplicity?: [number?, number?]
148
+ otherEndAggregation?: string
159
149
  }
160
150
 
161
- export interface SpaAppearance {
162
- logos?: SpaLogos
151
+ export interface SpaEnumerationLiteralPayload {
152
+ id: string
153
+ name: string
154
+ value?: string
155
+ ordinal?: number
163
156
  }
164
157
 
165
- export interface SpaMetadata {
166
- title?: string
167
- description?: string
168
- generated: string
169
- generator: string
170
- version: string
171
- homepage?: string
172
- repository?: string
173
- license?: string
174
- authors?: string
175
- tags?: string[]
176
- appearance?: SpaAppearance
177
- statistics: SpaStatistics
158
+ export interface SpaClassifierPayload {
159
+ id: string
160
+ name: string
161
+ qualifiedName: string
162
+ packageId: string
163
+ packageName?: string
164
+ isAbstract: boolean
165
+ visibility?: string
166
+ modelKind: string
167
+ properties: SpaPropertyPayload[]
168
+ operations: SpaOperationPayload[]
169
+ stereotypeRefs: string[]
170
+ taggedValues: any[]
171
+ constraints: any[]
172
+ annotations: SpaAnnotation[]
173
+ literals?: SpaEnumerationLiteralPayload[]
174
+ generalizations?: SpaGeneralizationStub[]
175
+ specializations?: SpaGeneralizationStub[]
176
+ associations?: SpaAssociationStub[]
177
+ }
178
+
179
+ export interface SpaPackagePayload {
180
+ id: string
181
+ name: string
182
+ parentId?: string
183
+ subPackageIds: string[]
184
+ classifierIds: string[]
185
+ diagramIds: string[]
186
+ stereotypeRefs: string[]
187
+ taggedValues: any[]
188
+ annotations: SpaAnnotation[]
178
189
  }
179
190
 
180
- export interface SpaSearchEntry {
191
+ export interface SpaDiagramPayload {
181
192
  id: string
182
- type: string
183
- entityType: string
184
- entityId: string
185
193
  name: string
186
- qualifiedName: string
187
- package: string
188
- content: string
189
- boost: number
194
+ packageId?: string
195
+ diagramType?: string
196
+ elements: any[]
197
+ connectors: any[]
198
+ annotations: SpaAnnotation[]
199
+ svg?: string
190
200
  }
191
201
 
192
- export interface SpaSearchIndex {
193
- version: string
194
- fields: { name: string; boost: number }[]
195
- ref: string
196
- documentStore: SpaSearchEntry[]
197
- pipeline: string[]
202
+ export interface SpaViewExtras {
203
+ ui?: Record<string, any>
204
+ appearance?: SpaAppearance
205
+ diagrams?: { enabled?: boolean }
198
206
  }
199
207
 
200
- export interface SpaDocument {
208
+ export interface SpaSkeleton {
201
209
  metadata: SpaMetadata
202
- packageTree: SpaPackageTreeNode
203
- packages: Record<string, SpaPackage>
204
- classes: Record<string, SpaClass>
205
- attributes: Record<string, SpaAttribute>
206
- associations: Record<string, SpaAssociation>
207
- operations: Record<string, SpaOperation>
208
- diagrams: Record<string, SpaDiagram>
210
+ packageTree: SpaPackageTree
211
+ entries: SpaSkeletonEntry[]
212
+ viewExtras?: SpaViewExtras
209
213
  }
210
214
 
211
- export interface SpaData {
212
- metadata: SpaMetadata
213
- packageTree: SpaPackageTreeNode
214
- packages: Record<string, SpaPackage>
215
- classes: Record<string, SpaClass>
216
- attributes: Record<string, SpaAttribute>
217
- associations: Record<string, SpaAssociation>
218
- operations: Record<string, SpaOperation>
219
- diagrams: Record<string, SpaDiagram>
215
+ export interface SpaData extends SpaSkeleton {
220
216
  searchIndex: SpaSearchIndex
217
+ shards: SpaShard[]
221
218
  }
@@ -19,7 +19,7 @@ module Ea
19
19
  payload = {
20
20
  metadata: skeleton.metadata,
21
21
  packageTree: skeleton.package_tree,
22
- skeletonEntries: skeleton.entries,
22
+ entries: skeleton.entries,
23
23
  searchIndex: projector.search_index,
24
24
  shards: projector.each_shard.to_a
25
25
  }
@@ -76,7 +76,22 @@ module Ea
76
76
  def metadata_hash
77
77
  base = document.metadata
78
78
  merged = configuration ? configuration.apply_to_metadata(base) : base
79
- JSON.parse(merged.to_json)
79
+ hash = JSON.parse(merged.to_json)
80
+ hash["statistics"] = statistics_hash
81
+ hash
82
+ end
83
+
84
+ def statistics_hash
85
+ {
86
+ "packages" => document.packages.size,
87
+ "classes" => document.classifiers.size,
88
+ "attributes" => document.classifiers.sum { |c| c.properties.size },
89
+ "operations" => document.classifiers.sum { |c| c.operations.size },
90
+ "associations" => document.relationships.count do |r|
91
+ r.is_a?(Ea::Model::Association)
92
+ end,
93
+ "diagrams" => render_diagrams? ? document.diagrams.size : 0
94
+ }
80
95
  end
81
96
 
82
97
  def view_extras
@@ -84,14 +99,16 @@ module Ea
84
99
  end
85
100
 
86
101
  def build_package_tree
102
+ sub_ids_by_parent = document.packages.group_by(&:parent_id)
87
103
  nodes = document.packages.map do |pkg|
88
104
  PackageTreeNode.new(
89
105
  id: pkg.id,
90
106
  name: pkg.name,
91
107
  parent_id: pkg.parent_id,
92
- child_ids: pkg.sub_package_ids,
108
+ child_ids: (pkg.sub_package_ids +
109
+ (sub_ids_by_parent[pkg.id] || []).map(&:id)).uniq,
93
110
  classifier_ids: classifiers_in_package_ids(pkg.id),
94
- diagram_ids: pkg.diagram_ids
111
+ diagram_ids: diagram_ids_for_package(pkg.id)
95
112
  )
96
113
  end
97
114
  PackageTree.new(
@@ -105,7 +122,7 @@ module Ea
105
122
  end
106
123
 
107
124
  def build_entries
108
- document.classifiers.map do |c|
125
+ classifier_entries = document.classifiers.map do |c|
109
126
  SkeletonEntry.new(
110
127
  id: c.id,
111
128
  name: c.name,
@@ -115,6 +132,27 @@ module Ea
115
132
  shard_url: shard_url_for.call(c)
116
133
  )
117
134
  end
135
+ package_entries = document.packages.map do |p|
136
+ SkeletonEntry.new(
137
+ id: p.id,
138
+ name: p.name,
139
+ kind: "package",
140
+ package_id: p.parent_id,
141
+ qualified_name: p.qualified_name,
142
+ shard_url: shard_url_for.call(p)
143
+ )
144
+ end
145
+ diagram_entries = render_diagrams? ? document.diagrams.map do |d|
146
+ SkeletonEntry.new(
147
+ id: d.id,
148
+ name: d.name,
149
+ kind: "diagram",
150
+ package_id: d.package_id,
151
+ qualified_name: d.name,
152
+ shard_url: shard_url_for.call(d)
153
+ )
154
+ end : []
155
+ classifier_entries + package_entries + diagram_entries
118
156
  end
119
157
 
120
158
  def build_search_entries
@@ -191,8 +229,72 @@ module Ea
191
229
  end
192
230
  end
193
231
 
194
- def payload_for(element)
195
- JSON.parse(element.to_json)
232
+ def payload_for(model_element)
233
+ payload = JSON.parse(model_element.to_json)
234
+ case model_element
235
+ when Ea::Model::Classifier
236
+ augment_classifier_payload!(payload, model_element)
237
+ when Ea::Model::Package
238
+ payload["classifierIds"] = classifiers_in_package_ids(model_element.id)
239
+ payload["diagramIds"] = diagram_ids_for_package(model_element.id)
240
+ when Ea::Model::Diagram
241
+ augment_diagram_payload!(payload, model_element)
242
+ end
243
+ payload
244
+ end
245
+
246
+ def diagram_ids_for_package(package_id)
247
+ (document.packages.find { |p| p.id == package_id }&.diagram_ids ||
248
+ []) + document.diagrams.select { |d| d.package_id == package_id }.map(&:id)
249
+ end
250
+
251
+ def augment_classifier_payload!(payload, classifier)
252
+ rels = document.relationships_for(classifier.id)
253
+ payload["generalizations"] = rels
254
+ .select { |r| r.is_a?(Ea::Model::Generalization) && r.specific_id == classifier.id }
255
+ .map { |r| { "id" => r.id, "targetId" => r.general_id } }
256
+ payload["specializations"] = rels
257
+ .select { |r| r.is_a?(Ea::Model::Generalization) && r.general_id == classifier.id }
258
+ .map { |r| { "id" => r.id, "targetId" => r.specific_id } }
259
+ payload["associations"] = rels
260
+ .select { |r| r.is_a?(Ea::Model::Association) }
261
+ .map { |r| association_stub(r, classifier.id) }
262
+ payload
263
+ end
264
+
265
+ def association_stub(assoc, classifier_id)
266
+ {
267
+ "id" => assoc.id,
268
+ "name" => assoc.name,
269
+ "sourceId" => assoc.source_id,
270
+ "targetId" => assoc.target_id,
271
+ "thisEndRoleName" => assoc.source_id == classifier_id ? assoc.source_role_name : assoc.target_role_name,
272
+ "otherEndRoleName" => assoc.source_id == classifier_id ? assoc.target_role_name : assoc.source_role_name,
273
+ "otherEndId" => assoc.source_id == classifier_id ? assoc.target_id : assoc.source_id,
274
+ "otherEndMultiplicity" => cardinality(assoc, classifier_id),
275
+ "otherEndAggregation" => assoc.source_id == classifier_id ? assoc.target_aggregation : assoc.source_aggregation
276
+ }
277
+ end
278
+
279
+ def cardinality(assoc, classifier_id)
280
+ if assoc.source_id == classifier_id
281
+ [assoc.target_multiplicity_lower, assoc.target_multiplicity_upper]
282
+ else
283
+ [assoc.source_multiplicity_lower, assoc.source_multiplicity_upper]
284
+ end
285
+ end
286
+
287
+ def augment_diagram_payload!(payload, diagram)
288
+ payload["svg"] = Ea::Svg::EaEmitter::Document.new(
289
+ diagram,
290
+ model_index: document.index_by_id,
291
+ document: document
292
+ ).render
293
+ payload
294
+ rescue StandardError => e
295
+ warn "ea: skipping SVG render for diagram #{diagram.id} " \
296
+ "(#{diagram.name.inspect}): #{e.class}: #{e.message}"
297
+ payload
196
298
  end
197
299
  end
198
300
  end
data/lib/ea/version.rb CHANGED
@@ -4,5 +4,5 @@ module Ea
4
4
  # Gem release version. Extracted to its own file so the
5
5
  # `gem-release` tool's `gem bump` can find and update it via the
6
6
  # standard `lib/<gem>/version.rb` convention.
7
- VERSION = "0.6.2"
7
+ VERSION = "0.6.4"
8
8
  end
@@ -57,6 +57,37 @@ module Ea
57
57
  def stereotype
58
58
  @model.stereotype&.first
59
59
  end
60
+
61
+ def owned_attributes
62
+ Array(@model.attributes).filter_map do |owned_attr|
63
+ ::Ea::Xmi::LiquidDrops::AttributeDrop.new(owned_attr, @options)
64
+ end
65
+ end
66
+
67
+ def generalization
68
+ if @model.generalization
69
+ ::Ea::Xmi::LiquidDrops::GeneralizationDrop.new(
70
+ @model.generalization, nil, @options
71
+ )
72
+ end
73
+ end
74
+
75
+ def upper_packaged_element
76
+ e = @lookup.find_upper_level_packaged_element(@model.xmi_id)
77
+ e&.name
78
+ end
79
+
80
+ def subtype_of
81
+ @lookup.find_subtype_of_from_generalization(@model.xmi_id) ||
82
+ @lookup.find_subtype_of_from_owned_attribute_type(@model.xmi_id)
83
+ end
84
+
85
+ def inheritances
86
+ Array(@inheritance_ids).filter_map do |inheritance_id|
87
+ connector = @lookup.fetch_connector(inheritance_id)
88
+ ::Ea::Xmi::LiquidDrops::ConnectorDrop.new(connector, @options)
89
+ end
90
+ end
60
91
  end
61
92
  end
62
93
  end
data/lib/ea/xmi/parser.rb CHANGED
@@ -442,6 +442,11 @@ module Ea
442
442
  data_type.operations = build_operations(dt)
443
443
  data_type.associations = build_associations(dt.id)
444
444
  data_type.constraints = build_constraints(dt.id)
445
+ data_type.association_generalization = build_assoc_generalizations(dt)
446
+ # build_generalization always returns a Generalization for the
447
+ # element itself, so only attach when XMI declares a parent.
448
+ data_type.generalization =
449
+ build_generalization(dt) if dt.generalization&.any?
445
450
  end
446
451
  end
447
452
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ea
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-09-22 00:00:00.000000000 Z
11
+ date: 2026-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -140,22 +140,16 @@ dependencies:
140
140
  name: lutaml-uml
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - "~>"
144
- - !ruby/object:Gem::Version
145
- version: '0.5'
146
143
  - - ">="
147
144
  - !ruby/object:Gem::Version
148
- version: 0.5.2
145
+ version: '0'
149
146
  type: :development
150
147
  prerelease: false
151
148
  version_requirements: !ruby/object:Gem::Requirement
152
149
  requirements:
153
- - - "~>"
154
- - !ruby/object:Gem::Version
155
- version: '0.5'
156
150
  - - ">="
157
151
  - !ruby/object:Gem::Version
158
- version: 0.5.2
152
+ version: '0'
159
153
  - !ruby/object:Gem::Dependency
160
154
  name: rubocop
161
155
  requirement: !ruby/object:Gem::Requirement