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.
- checksums.yaml +4 -4
- data/TODO.refactor/01-ea-model-domain-layer.md +64 -0
- data/TODO.refactor/02-ea-model-qea-source-adapter.md +58 -0
- data/TODO.refactor/03-ea-model-xmi-source-adapter.md +33 -0
- data/TODO.refactor/04-ea-model-annotations-tagged-values.md +34 -0
- data/TODO.refactor/05-ea-model-diagrams-coordinates.md +37 -0
- data/TODO.refactor/06-spa-projection-layer.md +45 -0
- data/TODO.refactor/07-spa-sharded-output-strategy.md +28 -0
- data/TODO.refactor/08-spa-build-time-search-index.md +24 -0
- data/TODO.refactor/09-spa-frontend-data-contract.md +25 -0
- data/TODO.refactor/10-spa-cli-command-rework.md +34 -0
- data/TODO.refactor/11-spa-perf-benchmark.md +23 -0
- data/TODO.refactor/12-lutaml-uml-static-site-deprecation.md +28 -0
- data/lib/ea/cli/app.rb +13 -7
- data/lib/ea/cli/command/spa.rb +33 -48
- data/lib/ea/cli/command/svg.rb +68 -0
- data/lib/ea/cli/command.rb +1 -0
- data/lib/ea/model/annotation.rb +29 -0
- data/lib/ea/model/association.rb +40 -0
- data/lib/ea/model/base.rb +27 -0
- data/lib/ea/model/bounds.rb +22 -0
- data/lib/ea/model/classifier.rb +41 -0
- data/lib/ea/model/data_type.rb +9 -0
- data/lib/ea/model/dependency.rb +19 -0
- data/lib/ea/model/diagram.rb +28 -0
- data/lib/ea/model/diagram_connector.rb +30 -0
- data/lib/ea/model/diagram_element.rb +23 -0
- data/lib/ea/model/document.rb +107 -0
- data/lib/ea/model/enumeration.rb +15 -0
- data/lib/ea/model/enumeration_literal.rb +16 -0
- data/lib/ea/model/generalization.rb +19 -0
- data/lib/ea/model/interface.rb +9 -0
- data/lib/ea/model/klass.rb +16 -0
- data/lib/ea/model/metadata.rb +31 -0
- data/lib/ea/model/operation.rb +33 -0
- data/lib/ea/model/package.rb +36 -0
- data/lib/ea/model/parameter.rb +24 -0
- data/lib/ea/model/point.rb +16 -0
- data/lib/ea/model/polymorphic_maps.rb +30 -0
- data/lib/ea/model/primitive_type.rb +9 -0
- data/lib/ea/model/property.rb +49 -0
- data/lib/ea/model/realization.rb +19 -0
- data/lib/ea/model/relationship.rb +21 -0
- data/lib/ea/model/stereotype.rb +24 -0
- data/lib/ea/model/tagged_value.rb +20 -0
- data/lib/ea/model/waypoint.rb +19 -0
- data/lib/ea/model.rb +48 -0
- data/lib/ea/sources/qea/adapter.rb +63 -0
- data/lib/ea/sources/qea/annotation_builder.rb +36 -0
- data/lib/ea/sources/qea/classifier_builder.rb +119 -0
- data/lib/ea/sources/qea/connector_relationship_map.rb +42 -0
- data/lib/ea/sources/qea/diagram_builder.rb +214 -0
- data/lib/ea/sources/qea/diagram_style_parser.rb +43 -0
- data/lib/ea/sources/qea/id_normalizer.rb +24 -0
- data/lib/ea/sources/qea/metadata_builder.rb +42 -0
- data/lib/ea/sources/qea/object_classifier_map.rb +36 -0
- data/lib/ea/sources/qea/operation_builder.rb +79 -0
- data/lib/ea/sources/qea/package_builder.rb +44 -0
- data/lib/ea/sources/qea/property_builder.rb +86 -0
- data/lib/ea/sources/qea/relationship_builder.rb +113 -0
- data/lib/ea/sources/qea/stereotype_builder.rb +44 -0
- data/lib/ea/sources/qea/tagged_value_builder.rb +39 -0
- data/lib/ea/sources/qea.rb +31 -0
- data/lib/ea/sources/xmi/adapter.rb +62 -0
- data/lib/ea/sources/xmi/annotation_builder.rb +49 -0
- data/lib/ea/sources/xmi/classifier_builder.rb +89 -0
- data/lib/ea/sources/xmi/diagram_builder.rb +82 -0
- data/lib/ea/sources/xmi/id_normalizer.rb +27 -0
- data/lib/ea/sources/xmi/metadata_builder.rb +32 -0
- data/lib/ea/sources/xmi/operation_builder.rb +59 -0
- data/lib/ea/sources/xmi/package_builder.rb +73 -0
- data/lib/ea/sources/xmi/property_builder.rb +77 -0
- data/lib/ea/sources/xmi/relationship_builder.rb +142 -0
- data/lib/ea/sources/xmi/relationship_type_map.rb +34 -0
- data/lib/ea/sources/xmi/type_classifier_map.rb +33 -0
- data/lib/ea/sources/xmi.rb +29 -0
- data/lib/ea/sources.rb +15 -0
- data/lib/ea/spa/configuration.rb +110 -0
- data/lib/ea/spa/generator.rb +45 -0
- data/lib/ea/spa/lazy_ref.rb +18 -0
- data/lib/ea/spa/output/sharded_multi_file_strategy.rb +71 -0
- data/lib/ea/spa/output/single_file_strategy.rb +56 -0
- data/lib/ea/spa/output/strategy.rb +36 -0
- data/lib/ea/spa/output.rb +11 -0
- data/lib/ea/spa/package_tree.rb +15 -0
- data/lib/ea/spa/package_tree_node.rb +26 -0
- data/lib/ea/spa/projector.rb +199 -0
- data/lib/ea/spa/search_entry.rb +28 -0
- data/lib/ea/spa/search_index.rb +18 -0
- data/lib/ea/spa/shard.rb +19 -0
- data/lib/ea/spa/skeleton.rb +22 -0
- data/lib/ea/spa/skeleton_entry.rb +27 -0
- data/lib/ea/spa.rb +31 -0
- data/lib/ea/svg/bounds_calculator.rb +74 -0
- data/lib/ea/svg/connector_path.rb +138 -0
- data/lib/ea/svg/element_box.rb +256 -0
- data/lib/ea/svg/renderer.rb +77 -0
- data/lib/ea/svg/style_resolver.rb +72 -0
- data/lib/ea/svg.rb +21 -0
- data/lib/ea/version.rb +1 -1
- data/lib/ea.rb +4 -0
- 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,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
|