ea 0.2.5 → 0.3.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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/TODO.refactor/01-ea-model-domain-layer.md +64 -0
  3. data/TODO.refactor/02-ea-model-qea-source-adapter.md +58 -0
  4. data/TODO.refactor/03-ea-model-xmi-source-adapter.md +33 -0
  5. data/TODO.refactor/04-ea-model-annotations-tagged-values.md +34 -0
  6. data/TODO.refactor/05-ea-model-diagrams-coordinates.md +37 -0
  7. data/TODO.refactor/06-spa-projection-layer.md +45 -0
  8. data/TODO.refactor/07-spa-sharded-output-strategy.md +28 -0
  9. data/TODO.refactor/08-spa-build-time-search-index.md +24 -0
  10. data/TODO.refactor/09-spa-frontend-data-contract.md +25 -0
  11. data/TODO.refactor/10-spa-cli-command-rework.md +34 -0
  12. data/TODO.refactor/11-spa-perf-benchmark.md +23 -0
  13. data/TODO.refactor/12-lutaml-uml-static-site-deprecation.md +28 -0
  14. data/lib/ea/cli/app.rb +13 -7
  15. data/lib/ea/cli/command/spa.rb +33 -48
  16. data/lib/ea/cli/command/svg.rb +68 -0
  17. data/lib/ea/cli/command.rb +1 -0
  18. data/lib/ea/model/annotation.rb +29 -0
  19. data/lib/ea/model/association.rb +40 -0
  20. data/lib/ea/model/base.rb +27 -0
  21. data/lib/ea/model/bounds.rb +22 -0
  22. data/lib/ea/model/classifier.rb +41 -0
  23. data/lib/ea/model/data_type.rb +9 -0
  24. data/lib/ea/model/dependency.rb +19 -0
  25. data/lib/ea/model/diagram.rb +28 -0
  26. data/lib/ea/model/diagram_connector.rb +30 -0
  27. data/lib/ea/model/diagram_element.rb +23 -0
  28. data/lib/ea/model/document.rb +107 -0
  29. data/lib/ea/model/enumeration.rb +15 -0
  30. data/lib/ea/model/enumeration_literal.rb +16 -0
  31. data/lib/ea/model/generalization.rb +19 -0
  32. data/lib/ea/model/interface.rb +9 -0
  33. data/lib/ea/model/klass.rb +16 -0
  34. data/lib/ea/model/metadata.rb +31 -0
  35. data/lib/ea/model/operation.rb +33 -0
  36. data/lib/ea/model/package.rb +36 -0
  37. data/lib/ea/model/parameter.rb +24 -0
  38. data/lib/ea/model/point.rb +16 -0
  39. data/lib/ea/model/polymorphic_maps.rb +30 -0
  40. data/lib/ea/model/primitive_type.rb +9 -0
  41. data/lib/ea/model/property.rb +49 -0
  42. data/lib/ea/model/realization.rb +19 -0
  43. data/lib/ea/model/relationship.rb +21 -0
  44. data/lib/ea/model/stereotype.rb +24 -0
  45. data/lib/ea/model/tagged_value.rb +20 -0
  46. data/lib/ea/model/waypoint.rb +19 -0
  47. data/lib/ea/model.rb +48 -0
  48. data/lib/ea/sources/qea/adapter.rb +63 -0
  49. data/lib/ea/sources/qea/annotation_builder.rb +36 -0
  50. data/lib/ea/sources/qea/classifier_builder.rb +119 -0
  51. data/lib/ea/sources/qea/connector_relationship_map.rb +42 -0
  52. data/lib/ea/sources/qea/diagram_builder.rb +214 -0
  53. data/lib/ea/sources/qea/diagram_style_parser.rb +43 -0
  54. data/lib/ea/sources/qea/id_normalizer.rb +24 -0
  55. data/lib/ea/sources/qea/metadata_builder.rb +42 -0
  56. data/lib/ea/sources/qea/object_classifier_map.rb +36 -0
  57. data/lib/ea/sources/qea/operation_builder.rb +79 -0
  58. data/lib/ea/sources/qea/package_builder.rb +44 -0
  59. data/lib/ea/sources/qea/property_builder.rb +86 -0
  60. data/lib/ea/sources/qea/relationship_builder.rb +113 -0
  61. data/lib/ea/sources/qea/stereotype_builder.rb +44 -0
  62. data/lib/ea/sources/qea/tagged_value_builder.rb +39 -0
  63. data/lib/ea/sources/qea.rb +31 -0
  64. data/lib/ea/sources/xmi/adapter.rb +62 -0
  65. data/lib/ea/sources/xmi/annotation_builder.rb +49 -0
  66. data/lib/ea/sources/xmi/classifier_builder.rb +89 -0
  67. data/lib/ea/sources/xmi/diagram_builder.rb +82 -0
  68. data/lib/ea/sources/xmi/id_normalizer.rb +27 -0
  69. data/lib/ea/sources/xmi/metadata_builder.rb +32 -0
  70. data/lib/ea/sources/xmi/operation_builder.rb +59 -0
  71. data/lib/ea/sources/xmi/package_builder.rb +73 -0
  72. data/lib/ea/sources/xmi/property_builder.rb +77 -0
  73. data/lib/ea/sources/xmi/relationship_builder.rb +142 -0
  74. data/lib/ea/sources/xmi/relationship_type_map.rb +34 -0
  75. data/lib/ea/sources/xmi/type_classifier_map.rb +33 -0
  76. data/lib/ea/sources/xmi.rb +29 -0
  77. data/lib/ea/sources.rb +15 -0
  78. data/lib/ea/spa/configuration.rb +110 -0
  79. data/lib/ea/spa/generator.rb +45 -0
  80. data/lib/ea/spa/lazy_ref.rb +18 -0
  81. data/lib/ea/spa/output/sharded_multi_file_strategy.rb +71 -0
  82. data/lib/ea/spa/output/single_file_strategy.rb +56 -0
  83. data/lib/ea/spa/output/strategy.rb +36 -0
  84. data/lib/ea/spa/output.rb +11 -0
  85. data/lib/ea/spa/package_tree.rb +15 -0
  86. data/lib/ea/spa/package_tree_node.rb +26 -0
  87. data/lib/ea/spa/projector.rb +199 -0
  88. data/lib/ea/spa/search_entry.rb +28 -0
  89. data/lib/ea/spa/search_index.rb +18 -0
  90. data/lib/ea/spa/shard.rb +19 -0
  91. data/lib/ea/spa/skeleton.rb +22 -0
  92. data/lib/ea/spa/skeleton_entry.rb +27 -0
  93. data/lib/ea/spa.rb +31 -0
  94. data/lib/ea/svg/bounds_calculator.rb +74 -0
  95. data/lib/ea/svg/connector_path.rb +138 -0
  96. data/lib/ea/svg/element_box.rb +256 -0
  97. data/lib/ea/svg/renderer.rb +77 -0
  98. data/lib/ea/svg/style_resolver.rb +72 -0
  99. data/lib/ea/svg.rb +21 -0
  100. data/lib/ea/version.rb +1 -1
  101. data/lib/ea.rb +4 -0
  102. metadata +97 -2
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Model
5
+ # 2D point in diagram pixel coordinates.
6
+ class Point < Base
7
+ attribute :x, :integer
8
+ attribute :y, :integer
9
+
10
+ json do
11
+ map "x", to: :x
12
+ map "y", to: :y
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Model
5
+ # Polymorphic dispatch maps for collections that hold subclasses
6
+ # of an abstract base. The discriminator field carries the
7
+ # concrete subclass name on every instance; lutaml-model uses
8
+ # this map to pick the right class on deserialize.
9
+ CLASSIFIER_POLYMORPHIC_MAP = {
10
+ attribute: "modelKind",
11
+ class_map: {
12
+ "class" => "Ea::Model::Klass",
13
+ "data_type" => "Ea::Model::DataType",
14
+ "primitive_type" => "Ea::Model::PrimitiveType",
15
+ "enumeration" => "Ea::Model::Enumeration",
16
+ "interface" => "Ea::Model::Interface"
17
+ }
18
+ }.freeze
19
+
20
+ RELATIONSHIP_POLYMORPHIC_MAP = {
21
+ attribute: "relationshipKind",
22
+ class_map: {
23
+ "association" => "Ea::Model::Association",
24
+ "generalization" => "Ea::Model::Generalization",
25
+ "realization" => "Ea::Model::Realization",
26
+ "dependency" => "Ea::Model::Dependency"
27
+ }
28
+ }.freeze
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Model
5
+ class PrimitiveType < Classifier
6
+ attribute :model_kind, :string, default: -> { "primitive_type" }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Model
5
+ # A Property is a typed slot on a Classifier (attribute or
6
+ # navigable association end). References its type by id when
7
+ # possible (model-internal), falling back to `type_name` for
8
+ # primitives or external types.
9
+ class Property < Base
10
+ attribute :owner_id, :string # classifier that owns it
11
+ attribute :type_ref, :string # id of Classifier (nil if external)
12
+ attribute :type_name, :string # name shown when type is external/primitive
13
+ attribute :qualified_name, :string
14
+ attribute :multiplicity_lower, :integer
15
+ attribute :multiplicity_upper, :integer # -1 means "many"
16
+ attribute :default_value, :string
17
+ attribute :is_derived, :boolean, default: false
18
+ attribute :is_readonly, :boolean, default: false
19
+ attribute :is_ordered, :boolean, default: false
20
+ attribute :is_unique, :boolean, default: true
21
+ attribute :aggregation, :string, default: -> { "none" } # none|shared|composite
22
+ attribute :visibility, :string # public|protected|private|package
23
+ attribute :stereotype_refs, :string, collection: true, initialize_empty: true
24
+ attribute :tagged_values, TaggedValue, collection: true, initialize_empty: true
25
+ attribute :annotations, Annotation, collection: true, initialize_empty: true
26
+
27
+ json do
28
+ map "id", to: :id
29
+ map "name", to: :name
30
+ map "ownerId", to: :owner_id
31
+ map "typeRef", to: :type_ref
32
+ map "typeName", to: :type_name
33
+ map "qualifiedName", to: :qualified_name
34
+ map "multiplicityLower", to: :multiplicity_lower
35
+ map "multiplicityUpper", to: :multiplicity_upper
36
+ map "defaultValue", to: :default_value
37
+ map "isDerived", to: :is_derived, render_default: true
38
+ map "isReadonly", to: :is_readonly, render_default: true
39
+ map "isOrdered", to: :is_ordered, render_default: true
40
+ map "isUnique", to: :is_unique, render_default: true
41
+ map "aggregation", to: :aggregation, render_default: true
42
+ map "visibility", to: :visibility
43
+ map "stereotypeRefs", to: :stereotype_refs, render_empty: true
44
+ map "taggedValues", to: :tagged_values, render_empty: true
45
+ map "annotations", to: :annotations, render_empty: true
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Model
5
+ # Realization: a classifier implements the contract defined by
6
+ # an interface.
7
+ class Realization < Relationship
8
+ attribute :realizing_id, :string
9
+ attribute :contract_id, :string
10
+
11
+ attribute :relationship_kind, :string, default: -> { "realization" }
12
+
13
+ json do
14
+ map "realizingId", to: :realizing_id
15
+ map "contractId", to: :contract_id
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Model
5
+ # Abstract base for typed relationships between classifiers.
6
+ # Concrete subclasses: Association, Generalization, Realization,
7
+ # Dependency. The discriminator (`relationship_kind`) lets
8
+ # consumers dispatch without `is_a?` ladders.
9
+ class Relationship < Base
10
+ attribute :qualified_name, :string
11
+ attribute :relationship_kind, :string, default: -> { "relationship" }
12
+
13
+ json do
14
+ map "id", to: :id
15
+ map "name", to: :name
16
+ map "qualifiedName", to: :qualified_name
17
+ map "relationshipKind", to: :relationship_kind, render_default: true
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Model
5
+ # A Stereotype declaration. Stereotypes carry a name, optional
6
+ # profile, and may declare a schema of tagged-value keys.
7
+ #
8
+ # A classifier/property/operation "has" a stereotype by
9
+ # reference (stereotype_ref on the element or its tagged values).
10
+ class Stereotype < Base
11
+ attribute :qualified_name, :string
12
+ attribute :profile, :string
13
+ attribute :declared_tagged_value_keys, :string, collection: true,
14
+ initialize_empty: true
15
+
16
+ json do
17
+ map "qualifiedName", to: :qualified_name
18
+ map "profile", to: :profile
19
+ map "declaredTaggedValueKeys", to: :declared_tagged_value_keys,
20
+ render_empty: true
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Model
5
+ # A tagged value: key/value metadata attached to a model element.
6
+ # Optionally scoped to a Stereotype application via
7
+ # `stereotype_ref`.
8
+ class TaggedValue < Base
9
+ attribute :key, :string
10
+ attribute :value, :string
11
+ attribute :stereotype_ref, :string
12
+
13
+ json do
14
+ map "key", to: :key
15
+ map "value", to: :value
16
+ map "stereotypeRef", to: :stereotype_ref
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Model
5
+ # A single waypoint on a DiagramConnector's path. Connectors
6
+ # route between source and target diagram elements through zero
7
+ # or more waypoints; the source adapter parses the EA `SeqNo`
8
+ # packed list into ordered Waypoints.
9
+ class Waypoint < Base
10
+ attribute :position, Point
11
+ attribute :routing, :string, default: -> { "line" }
12
+
13
+ json do
14
+ map "position", to: :position
15
+ map "routing", to: :routing
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/ea/model.rb ADDED
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Ea
6
+ # Ea::Model is the canonical domain model for what Sparx Enterprise
7
+ # Architect models.
8
+ #
9
+ # It is intentionally independent of any source format. QEA and XMI
10
+ # are translated into Ea::Model by source adapters
11
+ # (Ea::Sources::Qea, Ea::Sources::Xmi). Consumers (Ea::Spa, future
12
+ # exporters, validators, differs) project from Ea::Model — they do
13
+ # not redefine the modeling types.
14
+ #
15
+ # Architectural principle: Hexagonal / Ports & Adapters. Ea::Model
16
+ # is the hexagon. Source adapters are driving ports; consumer
17
+ # adapters are driven ports. Dependencies point inward only.
18
+ module Model
19
+ autoload :Base, "ea/model/base"
20
+ autoload :Metadata, "ea/model/metadata"
21
+ autoload :Point, "ea/model/point"
22
+ autoload :Bounds, "ea/model/bounds"
23
+ autoload :Waypoint, "ea/model/waypoint"
24
+ autoload :Annotation, "ea/model/annotation"
25
+ autoload :TaggedValue, "ea/model/tagged_value"
26
+ autoload :Stereotype, "ea/model/stereotype"
27
+ autoload :EnumerationLiteral, "ea/model/enumeration_literal"
28
+ autoload :Parameter, "ea/model/parameter"
29
+ autoload :Property, "ea/model/property"
30
+ autoload :Operation, "ea/model/operation"
31
+ autoload :Classifier, "ea/model/classifier"
32
+ autoload :Klass, "ea/model/klass"
33
+ autoload :DataType, "ea/model/data_type"
34
+ autoload :PrimitiveType, "ea/model/primitive_type"
35
+ autoload :Interface, "ea/model/interface"
36
+ autoload :Enumeration, "ea/model/enumeration"
37
+ autoload :Package, "ea/model/package"
38
+ autoload :Relationship, "ea/model/relationship"
39
+ autoload :Association, "ea/model/association"
40
+ autoload :Generalization, "ea/model/generalization"
41
+ autoload :Realization, "ea/model/realization"
42
+ autoload :Dependency, "ea/model/dependency"
43
+ autoload :DiagramElement, "ea/model/diagram_element"
44
+ autoload :DiagramConnector, "ea/model/diagram_connector"
45
+ autoload :Diagram, "ea/model/diagram"
46
+ autoload :Document, "ea/model/document"
47
+ end
48
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ # Driving port: produces an Ea::Model::Document from a parsed
7
+ # Ea::Qea::Database. Walks the SQLite-derived collections once
8
+ # and delegates to per-domain builders (OCP/MECE — new model
9
+ # types or source columns touch one builder).
10
+ class Adapter
11
+ attr_reader :database, :qea_path
12
+
13
+ def initialize(database, qea_path = nil)
14
+ @database = database
15
+ @qea_path = qea_path
16
+ end
17
+
18
+ # Convenience: build a document directly from a .qea file
19
+ # path. The file is parsed via the existing Ea::Qea pipeline.
20
+ def self.from_path(qea_path)
21
+ new(Ea.parse(qea_path), qea_path).to_document
22
+ end
23
+
24
+ def to_document
25
+ Ea::Model::Document.new(
26
+ metadata: metadata,
27
+ packages: packages,
28
+ classifiers: classifiers,
29
+ relationships: relationships,
30
+ stereotypes: stereotypes,
31
+ diagrams: diagrams
32
+ )
33
+ end
34
+
35
+ private
36
+
37
+ def metadata
38
+ @metadata ||= MetadataBuilder.new(database, qea_path).build
39
+ end
40
+
41
+ def packages
42
+ @packages ||= PackageBuilder.new(database).build_all
43
+ end
44
+
45
+ def classifiers
46
+ @classifiers ||= ClassifierBuilder.new(database).build_all
47
+ end
48
+
49
+ def relationships
50
+ @relationships ||= RelationshipBuilder.new(database).build_all
51
+ end
52
+
53
+ def stereotypes
54
+ @stereotypes ||= StereotypeBuilder.new(database).build_all
55
+ end
56
+
57
+ def diagrams
58
+ @diagrams ||= DiagramBuilder.new(database).build_all
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ # Builds Ea::Model::Annotation instances from EA Note text and
7
+ # t_document rows. EA stores most documentation as a Note
8
+ # field directly on the element (t_object.Note, t_package.notes,
9
+ # t_connector.notes, t_attribute.notes, etc.); we lift each
10
+ # non-empty note into a typed Annotation.
11
+ class AnnotationBuilder
12
+ attr_reader :owner_guid
13
+
14
+ def initialize(owner_guid)
15
+ @owner_guid = owner_guid
16
+ end
17
+
18
+ def self.from_note(note_text, owner_guid, kind: "documentation")
19
+ return [] if note_text.nil? || note_text.empty?
20
+
21
+ [new(owner_guid).build(kind, note_text)]
22
+ end
23
+
24
+ def build(kind, body, author: nil, modified_date: nil)
25
+ Ea::Model::Annotation.new(
26
+ id: IdNormalizer.synthetic("annotation", owner_guid, kind),
27
+ kind: kind,
28
+ body: body,
29
+ author: author,
30
+ modified_date: modified_date
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ # Translates EA t_object rows (filtered to classifier types)
7
+ # into the appropriate Ea::Model::Classifier subclass. Uses
8
+ # ObjectClassifierMap for dispatch (OCP — new classifier
9
+ # kinds need no edit here).
10
+ class ClassifierBuilder
11
+ attr_reader :database
12
+
13
+ def initialize(database)
14
+ @database = database
15
+ end
16
+
17
+ def build_all
18
+ classifiers = []
19
+ each_classifier_object do |object|
20
+ classifiers << build_one(object)
21
+ end
22
+ classifiers
23
+ end
24
+
25
+ def build_one(object)
26
+ klass = ObjectClassifierMap.class_for(object.object_type)
27
+ args = common_args(object)
28
+ args.merge!(enumeration_args(object)) if klass == Ea::Model::Enumeration
29
+ klass.new(**args)
30
+ end
31
+
32
+ private
33
+
34
+ def each_classifier_object
35
+ objects = database.collections[:objects] || []
36
+ objects.each do |obj|
37
+ next unless classifier_type?(obj.object_type)
38
+
39
+ yield obj
40
+ end
41
+ end
42
+
43
+ def classifier_type?(object_type)
44
+ ObjectClassifierMap::LOOKUP.key?(object_type)
45
+ end
46
+
47
+ def common_args(object)
48
+ {
49
+ id: IdNormalizer.from_guid(object.ea_guid),
50
+ name: object.name,
51
+ package_id: package_id_for(object),
52
+ qualified_name: qualified_name_for(object),
53
+ is_abstract: abstract?(object),
54
+ visibility: visibility_from_scope(object.scope),
55
+ properties: PropertyBuilder.new(database).build_all_for(object),
56
+ operations: OperationBuilder.new(database).build_all_for(object),
57
+ stereotype_refs: stereotype_refs_for(object),
58
+ tagged_values: TaggedValueBuilder.new(database).for_object(object.ea_guid),
59
+ annotations: AnnotationBuilder.from_note(object.note, object.ea_guid,
60
+ kind: "documentation")
61
+ }
62
+ end
63
+
64
+ def enumeration_args(object)
65
+ literals = enumeration_literals_for(object)
66
+ { literals: literals }
67
+ end
68
+
69
+ def enumeration_literals_for(object)
70
+ # EA stores enum literals as attributes with no type, or as
71
+ # child t_object rows tagged as Literal. We pick them up
72
+ # from attributes where type is empty/nil and treat the
73
+ # attribute name as the literal value.
74
+ attrs = database.attributes_for_object(object.ea_object_id) || []
75
+ attrs.map.with_index do |attr, idx|
76
+ Ea::Model::EnumerationLiteral.new(
77
+ id: IdNormalizer.from_guid(attr.ea_guid),
78
+ name: attr.name,
79
+ value: attr.default || attr.name,
80
+ ordinal: idx
81
+ )
82
+ end
83
+ end
84
+
85
+ def package_id_for(object)
86
+ pkg = database.find_package(object.package_id)
87
+ return nil unless pkg
88
+
89
+ IdNormalizer.from_guid(pkg.ea_guid)
90
+ end
91
+
92
+ def qualified_name_for(object)
93
+ pkg = database.find_package(object.package_id)
94
+ pkg_prefix = pkg&.xmlpath || pkg&.name
95
+ pkg_prefix ? "#{pkg_prefix}::#{object.name}" : object.name
96
+ end
97
+
98
+ def abstract?(object)
99
+ %w[1 true].include?(object.abstract.to_s)
100
+ end
101
+
102
+ def visibility_from_scope(scope)
103
+ case scope.to_s
104
+ when "Public" then "public"
105
+ when "Protected" then "protected"
106
+ when "Private" then "private"
107
+ else "public"
108
+ end
109
+ end
110
+
111
+ def stereotype_refs_for(object)
112
+ refs = []
113
+ refs << object.stereotype if object.stereotype && !object.stereotype.empty?
114
+ refs
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Sources
5
+ module Qea
6
+ # Maps EA's `t_connector.Connector_Type` discriminator to the
7
+ # concrete Ea::Model::Relationship subclass. Adds new
8
+ # relationship kinds by registering here (OCP).
9
+ module ConnectorRelationshipMap
10
+ module_function
11
+
12
+ LOOKUP = {
13
+ "Association" => "Ea::Model::Association",
14
+ "Aggregation" => "Ea::Model::Association",
15
+ "Composition" => "Ea::Model::Association",
16
+ "Generalization" => "Ea::Model::Generalization",
17
+ "Realization" => "Ea::Model::Realization",
18
+ "Usage" => "Ea::Model::Dependency",
19
+ "Dependency" => "Ea::Model::Dependency",
20
+ "Abstraction" => "Ea::Model::Dependency"
21
+ }.freeze
22
+
23
+ # Some connector types imply specific aggregation values on
24
+ # the source end (EA stores them as separate Connector_Type
25
+ # values rather than as an attribute on the connector).
26
+ AGGREGATION_FOR = {
27
+ "Aggregation" => "shared",
28
+ "Composition" => "composite"
29
+ }.freeze
30
+
31
+ def class_for(connector_type)
32
+ name = LOOKUP[connector_type] || "Ea::Model::Association"
33
+ Object.const_get(name)
34
+ end
35
+
36
+ def source_aggregation_for(connector_type)
37
+ AGGREGATION_FOR[connector_type] || "none"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end