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,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module Ea
|
|
6
|
+
module Cli
|
|
7
|
+
module Command
|
|
8
|
+
# `ea svg NAME FILE [--output=PATH]`
|
|
9
|
+
#
|
|
10
|
+
# Renders a single diagram from a QEA or XMI file into a
|
|
11
|
+
# standalone SVG using the umldi content (placed element
|
|
12
|
+
# bounds + connector waypoints) captured in Ea::Model::Diagram.
|
|
13
|
+
class Svg < Base
|
|
14
|
+
def call
|
|
15
|
+
diagram = find_diagram
|
|
16
|
+
svg = Ea::Svg::Renderer.new(diagram,
|
|
17
|
+
model_index: document.index_by_id).render
|
|
18
|
+
|
|
19
|
+
output_path = resolve_output_path(diagram)
|
|
20
|
+
FileUtils.mkdir_p(File.dirname(output_path))
|
|
21
|
+
File.write(output_path, svg)
|
|
22
|
+
|
|
23
|
+
formatter.render([[output_path]], columns: [:written_to])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def document
|
|
29
|
+
@document ||= build_model_document
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def build_model_document
|
|
33
|
+
case File.extname(file_path).downcase
|
|
34
|
+
when ".qea"
|
|
35
|
+
Ea::Sources::Qea::Adapter.from_path(file_path)
|
|
36
|
+
when ".xmi"
|
|
37
|
+
Ea::Sources::Xmi::Adapter.from_path(file_path)
|
|
38
|
+
else
|
|
39
|
+
raise Ea::Cli::UnsupportedFormat,
|
|
40
|
+
"Unknown file format: #{File.extname(file_path)}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def diagram_name
|
|
45
|
+
options[:name] or raise Ea::Cli::Error, "missing required diagram name"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def find_diagram
|
|
49
|
+
match = document.diagrams.find { |d| d.name == diagram_name }
|
|
50
|
+
return match if match
|
|
51
|
+
|
|
52
|
+
raise Ea::Cli::Error,
|
|
53
|
+
"Diagram #{diagram_name.inspect} not found in #{file_path}. " \
|
|
54
|
+
"Use `ea diagrams list #{file_path}` to see names."
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def resolve_output_path(diagram)
|
|
58
|
+
options[:output] || begin
|
|
59
|
+
base = File.basename(file_path, ".*")
|
|
60
|
+
dir = File.dirname(file_path)
|
|
61
|
+
safe_name = (diagram.name || "diagram").gsub(/[^\w.\-]+/, "_")
|
|
62
|
+
File.join(dir, "#{base}.#{safe_name}.svg")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/ea/cli/command.rb
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# First-class annotation: comments, documentation, change notes,
|
|
6
|
+
# requirements, file references — the textual meta-content EA
|
|
7
|
+
# authors attach to model elements.
|
|
8
|
+
#
|
|
9
|
+
# Modeled as a typed concept rather than a packed string, so the
|
|
10
|
+
# SPA can show different annotation kinds with appropriate
|
|
11
|
+
# presentation (e.g. documentation gets rendered as markdown,
|
|
12
|
+
# change history goes in a sidebar).
|
|
13
|
+
class Annotation < Base
|
|
14
|
+
attribute :kind, :string # comment|documentation|change|file|requirement
|
|
15
|
+
attribute :body, :string
|
|
16
|
+
attribute :author, :string
|
|
17
|
+
attribute :modified_date, :string
|
|
18
|
+
attribute :language, :string
|
|
19
|
+
|
|
20
|
+
json do
|
|
21
|
+
map "kind", to: :kind
|
|
22
|
+
map "body", to: :body
|
|
23
|
+
map "author", to: :author
|
|
24
|
+
map "modifiedDate", to: :modified_date
|
|
25
|
+
map "language", to: :language
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# An Association between two classifiers. Each end carries its
|
|
6
|
+
# own role name, multiplicity, aggregation kind, and
|
|
7
|
+
# navigability. Source and target are referenced by id.
|
|
8
|
+
class Association < Relationship
|
|
9
|
+
attribute :source_id, :string
|
|
10
|
+
attribute :target_id, :string
|
|
11
|
+
attribute :source_role_name, :string
|
|
12
|
+
attribute :target_role_name, :string
|
|
13
|
+
attribute :source_multiplicity_lower, :integer
|
|
14
|
+
attribute :source_multiplicity_upper, :integer
|
|
15
|
+
attribute :target_multiplicity_lower, :integer
|
|
16
|
+
attribute :target_multiplicity_upper, :integer
|
|
17
|
+
attribute :source_aggregation, :string, default: -> { "none" }
|
|
18
|
+
attribute :target_aggregation, :string, default: -> { "none" }
|
|
19
|
+
attribute :source_navigable, :boolean, default: true
|
|
20
|
+
attribute :target_navigable, :boolean, default: true
|
|
21
|
+
|
|
22
|
+
attribute :relationship_kind, :string, default: -> { "association" }
|
|
23
|
+
|
|
24
|
+
json do
|
|
25
|
+
map "sourceId", to: :source_id
|
|
26
|
+
map "targetId", to: :target_id
|
|
27
|
+
map "sourceRoleName", to: :source_role_name
|
|
28
|
+
map "targetRoleName", to: :target_role_name
|
|
29
|
+
map "sourceMultiplicityLower", to: :source_multiplicity_lower
|
|
30
|
+
map "sourceMultiplicityUpper", to: :source_multiplicity_upper
|
|
31
|
+
map "targetMultiplicityLower", to: :target_multiplicity_lower
|
|
32
|
+
map "targetMultiplicityUpper", to: :target_multiplicity_upper
|
|
33
|
+
map "sourceAggregation", to: :source_aggregation, render_default: true
|
|
34
|
+
map "targetAggregation", to: :target_aggregation, render_default: true
|
|
35
|
+
map "sourceNavigable", to: :source_navigable, render_default: true
|
|
36
|
+
map "targetNavigable", to: :target_navigable, render_default: true
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# Common base for every Ea::Model type.
|
|
6
|
+
#
|
|
7
|
+
# Every model element has a stable identity (`id`) used to
|
|
8
|
+
# reference it from other elements (e.g. a Property refers to its
|
|
9
|
+
# type by id, a Relationship refers to its ends by id). Source
|
|
10
|
+
# adapters normalize the source-specific identifier (xmi:id,
|
|
11
|
+
# ea_guid, etc.) into this `id` field.
|
|
12
|
+
class Base < Lutaml::Model::Serializable
|
|
13
|
+
attribute :id, :string
|
|
14
|
+
attribute :name, :string
|
|
15
|
+
|
|
16
|
+
json do
|
|
17
|
+
map "id", to: :id
|
|
18
|
+
map "name", to: :name
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
yaml do
|
|
22
|
+
map "id", to: :id
|
|
23
|
+
map "name", to: :name
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# Rectangular bounds in diagram pixel coordinates. Used by
|
|
6
|
+
# DiagramElement to record where a classifier is placed on a
|
|
7
|
+
# diagram.
|
|
8
|
+
class Bounds < Base
|
|
9
|
+
attribute :x, :integer
|
|
10
|
+
attribute :y, :integer
|
|
11
|
+
attribute :width, :integer
|
|
12
|
+
attribute :height, :integer
|
|
13
|
+
|
|
14
|
+
json do
|
|
15
|
+
map "x", to: :x
|
|
16
|
+
map "y", to: :y
|
|
17
|
+
map "width", to: :width
|
|
18
|
+
map "height", to: :height
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# Abstract base for Class, DataType, Enumeration, Interface,
|
|
6
|
+
# PrimitiveType — anything that can be a type of a Property, an
|
|
7
|
+
# end of an Association, etc.
|
|
8
|
+
#
|
|
9
|
+
# Concrete subclasses live in their own files and add their own
|
|
10
|
+
# specific attributes (e.g. Enumeration#literals). Source
|
|
11
|
+
# adapters dispatch on the source's type discriminator
|
|
12
|
+
# (t_object.Object_Type, xmi:type) to pick the right subclass.
|
|
13
|
+
class Classifier < Base
|
|
14
|
+
attribute :qualified_name, :string
|
|
15
|
+
attribute :package_id, :string # containing package
|
|
16
|
+
attribute :is_abstract, :boolean, default: false
|
|
17
|
+
attribute :visibility, :string
|
|
18
|
+
attribute :model_kind, :string, default: -> { "classifier" }
|
|
19
|
+
attribute :properties, Property, collection: true, initialize_empty: true
|
|
20
|
+
attribute :operations, Operation, collection: true, initialize_empty: true
|
|
21
|
+
attribute :stereotype_refs, :string, collection: true, initialize_empty: true
|
|
22
|
+
attribute :tagged_values, TaggedValue, collection: true, initialize_empty: true
|
|
23
|
+
attribute :annotations, Annotation, collection: true, initialize_empty: true
|
|
24
|
+
|
|
25
|
+
json do
|
|
26
|
+
map "id", to: :id
|
|
27
|
+
map "name", to: :name
|
|
28
|
+
map "qualifiedName", to: :qualified_name
|
|
29
|
+
map "packageId", to: :package_id
|
|
30
|
+
map "isAbstract", to: :is_abstract, render_default: true
|
|
31
|
+
map "visibility", to: :visibility
|
|
32
|
+
map "modelKind", to: :model_kind, render_default: true
|
|
33
|
+
map "properties", to: :properties, render_empty: true
|
|
34
|
+
map "operations", to: :operations, render_empty: true
|
|
35
|
+
map "stereotypeRefs", to: :stereotype_refs, render_empty: true
|
|
36
|
+
map "taggedValues", to: :tagged_values, render_empty: true
|
|
37
|
+
map "annotations", to: :annotations, render_empty: true
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# Dependency: client depends on supplier. No semantic
|
|
6
|
+
# implication beyond "client references supplier".
|
|
7
|
+
class Dependency < Relationship
|
|
8
|
+
attribute :client_id, :string
|
|
9
|
+
attribute :supplier_id, :string
|
|
10
|
+
|
|
11
|
+
attribute :relationship_kind, :string, default: -> { "dependency" }
|
|
12
|
+
|
|
13
|
+
json do
|
|
14
|
+
map "clientId", to: :client_id
|
|
15
|
+
map "supplierId", to: :supplier_id
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# A diagram: an ordered set of placed elements and connectors.
|
|
6
|
+
# The diagram references the package it belongs to (by id) and
|
|
7
|
+
# owns its elements/connectors compositionally.
|
|
8
|
+
class Diagram < Base
|
|
9
|
+
attribute :package_id, :string
|
|
10
|
+
attribute :diagram_type, :string # logical|class|sequence|use_case|...
|
|
11
|
+
attribute :bounds, Bounds # canvas size
|
|
12
|
+
attribute :elements, DiagramElement, collection: true, initialize_empty: true
|
|
13
|
+
attribute :connectors, DiagramConnector, collection: true, initialize_empty: true
|
|
14
|
+
attribute :annotations, Annotation, collection: true, initialize_empty: true
|
|
15
|
+
|
|
16
|
+
json do
|
|
17
|
+
map "id", to: :id
|
|
18
|
+
map "name", to: :name
|
|
19
|
+
map "packageId", to: :package_id
|
|
20
|
+
map "diagramType", to: :diagram_type
|
|
21
|
+
map "bounds", to: :bounds
|
|
22
|
+
map "elements", to: :elements, render_empty: true
|
|
23
|
+
map "connectors", to: :connectors, render_empty: true
|
|
24
|
+
map "annotations", to: :annotations, render_empty: true
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# A connector drawn on a diagram between two DiagramElements.
|
|
6
|
+
# References the underlying Relationship (by id) and the source
|
|
7
|
+
# and target DiagramElements (by id). Carries ordered waypoints
|
|
8
|
+
# for routing.
|
|
9
|
+
class DiagramConnector < Base
|
|
10
|
+
attribute :diagram_id, :string
|
|
11
|
+
attribute :relationship_ref, :string
|
|
12
|
+
attribute :source_element_ref, :string
|
|
13
|
+
attribute :target_element_ref, :string
|
|
14
|
+
attribute :waypoints, Waypoint, collection: true, initialize_empty: true
|
|
15
|
+
attribute :label, :string
|
|
16
|
+
attribute :style, :hash, default: -> { {} }
|
|
17
|
+
|
|
18
|
+
json do
|
|
19
|
+
map "id", to: :id
|
|
20
|
+
map "diagramId", to: :diagram_id
|
|
21
|
+
map "relationshipRef", to: :relationship_ref
|
|
22
|
+
map "sourceElementRef", to: :source_element_ref
|
|
23
|
+
map "targetElementRef", to: :target_element_ref
|
|
24
|
+
map "waypoints", to: :waypoints, render_empty: true
|
|
25
|
+
map "label", to: :label
|
|
26
|
+
map "style", to: :style
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# A single placed element on a diagram. References the model
|
|
6
|
+
# element it visualizes (by id), carries pixel bounds and a
|
|
7
|
+
# parsed style map (fill color, line color, font, etc.).
|
|
8
|
+
class DiagramElement < Base
|
|
9
|
+
attribute :diagram_id, :string
|
|
10
|
+
attribute :model_element_ref, :string # id of Classifier/Package/etc.
|
|
11
|
+
attribute :bounds, Bounds
|
|
12
|
+
attribute :style, :hash, default: -> { {} } # parsed style fields
|
|
13
|
+
|
|
14
|
+
json do
|
|
15
|
+
map "id", to: :id
|
|
16
|
+
map "diagramId", to: :diagram_id
|
|
17
|
+
map "modelElementRef", to: :model_element_ref
|
|
18
|
+
map "bounds", to: :bounds
|
|
19
|
+
map "style", to: :style
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# Discriminator → subclass maps for polymorphic Classifier and
|
|
6
|
+
# Relationship collections. Defined here (rather than autoloaded)
|
|
7
|
+
# because lutaml-model needs them at class-evaluation time, and
|
|
8
|
+
# autoload only triggers on first constant reference.
|
|
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
|
+
|
|
30
|
+
# Root container for a harmonized model. Source adapters build
|
|
31
|
+
# one of these; consumer adapters read from it.
|
|
32
|
+
#
|
|
33
|
+
# Element identity is global within the document: every package,
|
|
34
|
+
# classifier, relationship, diagram, and diagram element has a
|
|
35
|
+
# unique `id`. References between elements are by id, so the
|
|
36
|
+
# document can be sharded cheaply (no need to walk a tree to
|
|
37
|
+
# resolve a reference).
|
|
38
|
+
class Document < Base
|
|
39
|
+
attribute :metadata, Metadata
|
|
40
|
+
attribute :packages, Package, collection: true, initialize_empty: true
|
|
41
|
+
attribute :classifiers, Classifier, collection: true, initialize_empty: true,
|
|
42
|
+
polymorphic: CLASSIFIER_POLYMORPHIC_MAP
|
|
43
|
+
attribute :relationships, Relationship, collection: true, initialize_empty: true,
|
|
44
|
+
polymorphic: RELATIONSHIP_POLYMORPHIC_MAP
|
|
45
|
+
attribute :stereotypes, Stereotype, collection: true, initialize_empty: true
|
|
46
|
+
attribute :diagrams, Diagram, collection: true, initialize_empty: true
|
|
47
|
+
|
|
48
|
+
# Flat lookup indexes, built lazily by consumers. Not
|
|
49
|
+
# serialized — they're derived from the element collections.
|
|
50
|
+
def index_by_id
|
|
51
|
+
@index_by_id ||= begin
|
|
52
|
+
idx = {}
|
|
53
|
+
packages.each { |p| idx[p.id] = p }
|
|
54
|
+
classifiers.each { |c| idx[c.id] = c }
|
|
55
|
+
relationships.each { |r| idx[r.id] = r }
|
|
56
|
+
stereotypes.each { |s| idx[s.id] = s }
|
|
57
|
+
diagrams.each { |d| idx[d.id] = d }
|
|
58
|
+
diagrams.each do |d|
|
|
59
|
+
d.elements.each { |e| idx[e.id] = e }
|
|
60
|
+
d.connectors.each { |c| idx[c.id] = c }
|
|
61
|
+
end
|
|
62
|
+
idx
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def root_packages
|
|
67
|
+
packages.select { |p| p.parent_id.nil? }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def classifiers_in_package(package_id)
|
|
71
|
+
classifiers.select { |c| c.package_id == package_id }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def relationships_for(classifier_id)
|
|
75
|
+
relationships.select do |r|
|
|
76
|
+
case r
|
|
77
|
+
when Association
|
|
78
|
+
r.source_id == classifier_id || r.target_id == classifier_id
|
|
79
|
+
when Generalization
|
|
80
|
+
r.specific_id == classifier_id || r.general_id == classifier_id
|
|
81
|
+
when Realization
|
|
82
|
+
r.realizing_id == classifier_id || r.contract_id == classifier_id
|
|
83
|
+
when Dependency
|
|
84
|
+
r.client_id == classifier_id || r.supplier_id == classifier_id
|
|
85
|
+
else
|
|
86
|
+
false
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def reset_indexes
|
|
92
|
+
@index_by_id = nil
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
json do
|
|
96
|
+
map "metadata", to: :metadata
|
|
97
|
+
map "packages", to: :packages, render_empty: true
|
|
98
|
+
map "classifiers", to: :classifiers, render_empty: true,
|
|
99
|
+
polymorphic: CLASSIFIER_POLYMORPHIC_MAP
|
|
100
|
+
map "relationships", to: :relationships, render_empty: true,
|
|
101
|
+
polymorphic: RELATIONSHIP_POLYMORPHIC_MAP
|
|
102
|
+
map "stereotypes", to: :stereotypes, render_empty: true
|
|
103
|
+
map "diagrams", to: :diagrams, render_empty: true
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
class Enumeration < Classifier
|
|
6
|
+
attribute :model_kind, :string, default: -> { "enumeration" }
|
|
7
|
+
attribute :literals, EnumerationLiteral, collection: true,
|
|
8
|
+
initialize_empty: true
|
|
9
|
+
|
|
10
|
+
json do
|
|
11
|
+
map "literals", to: :literals, render_empty: true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# A single literal of an Enumeration.
|
|
6
|
+
class EnumerationLiteral < Base
|
|
7
|
+
attribute :value, :string
|
|
8
|
+
attribute :ordinal, :integer
|
|
9
|
+
|
|
10
|
+
json do
|
|
11
|
+
map "value", to: :value
|
|
12
|
+
map "ordinal", to: :ordinal
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# Generalization (inheritance): specific (child) extends
|
|
6
|
+
# general (parent).
|
|
7
|
+
class Generalization < Relationship
|
|
8
|
+
attribute :specific_id, :string # child
|
|
9
|
+
attribute :general_id, :string # parent
|
|
10
|
+
|
|
11
|
+
attribute :relationship_kind, :string, default: -> { "generalization" }
|
|
12
|
+
|
|
13
|
+
json do
|
|
14
|
+
map "specificId", to: :specific_id
|
|
15
|
+
map "generalId", to: :general_id
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# Named `Klass` (not `Class`) to avoid collision with Ruby's
|
|
6
|
+
# built-in Class. Represents a UML Class.
|
|
7
|
+
class Klass < Classifier
|
|
8
|
+
attribute :model_kind, :string, default: -> { "class" }
|
|
9
|
+
attribute :is_active, :boolean, default: false
|
|
10
|
+
|
|
11
|
+
json do
|
|
12
|
+
map "isActive", to: :is_active, render_default: true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# Document-level metadata. Captured during source adaptation;
|
|
6
|
+
# surfaced by the SPA as project info.
|
|
7
|
+
class Metadata < Base
|
|
8
|
+
attribute :title, :string
|
|
9
|
+
attribute :version, :string
|
|
10
|
+
attribute :author, :string
|
|
11
|
+
attribute :company, :string
|
|
12
|
+
attribute :created_date, :string
|
|
13
|
+
attribute :modified_date, :string
|
|
14
|
+
attribute :source_format, :string # "qea" | "xmi"
|
|
15
|
+
attribute :source_tool, :string # e.g. "Sparx EA 16.0"
|
|
16
|
+
attribute :source_path, :string
|
|
17
|
+
|
|
18
|
+
json do
|
|
19
|
+
map "title", to: :title
|
|
20
|
+
map "version", to: :version
|
|
21
|
+
map "author", to: :author
|
|
22
|
+
map "company", to: :company
|
|
23
|
+
map "createdDate", to: :created_date
|
|
24
|
+
map "modifiedDate", to: :modified_date
|
|
25
|
+
map "sourceFormat", to: :source_format
|
|
26
|
+
map "sourceTool", to: :source_tool
|
|
27
|
+
map "sourcePath", to: :source_path
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# An Operation on a Classifier (method). Parameters are owned
|
|
6
|
+
# composition-style.
|
|
7
|
+
class Operation < Base
|
|
8
|
+
attribute :owner_id, :string
|
|
9
|
+
attribute :qualified_name, :string
|
|
10
|
+
attribute :return_type_name, :string
|
|
11
|
+
attribute :is_static, :boolean, default: false
|
|
12
|
+
attribute :is_abstract, :boolean, default: false
|
|
13
|
+
attribute :visibility, :string
|
|
14
|
+
attribute :parameters, Parameter, collection: true, initialize_empty: true
|
|
15
|
+
attribute :stereotype_refs, :string, collection: true, initialize_empty: true
|
|
16
|
+
attribute :annotations, Annotation, collection: true, initialize_empty: true
|
|
17
|
+
|
|
18
|
+
json do
|
|
19
|
+
map "id", to: :id
|
|
20
|
+
map "name", to: :name
|
|
21
|
+
map "ownerId", to: :owner_id
|
|
22
|
+
map "qualifiedName", to: :qualified_name
|
|
23
|
+
map "returnTypeName", to: :return_type_name
|
|
24
|
+
map "isStatic", to: :is_static, render_default: true
|
|
25
|
+
map "isAbstract", to: :is_abstract, render_default: true
|
|
26
|
+
map "visibility", to: :visibility
|
|
27
|
+
map "parameters", to: :parameters, render_empty: true
|
|
28
|
+
map "stereotypeRefs", to: :stereotype_refs, render_empty: true
|
|
29
|
+
map "annotations", to: :annotations, render_empty: true
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# A Package: hierarchical container for sub-packages and
|
|
6
|
+
# classifiers. References children by id (flat index in the
|
|
7
|
+
# Document) rather than nesting, so the same model is cheap to
|
|
8
|
+
# serialize, shard, and walk from any entry point.
|
|
9
|
+
class Package < Base
|
|
10
|
+
attribute :parent_id, :string # nil for root
|
|
11
|
+
attribute :qualified_name, :string
|
|
12
|
+
attribute :sub_package_ids, :string, collection: true,
|
|
13
|
+
initialize_empty: true
|
|
14
|
+
attribute :classifier_ids, :string, collection: true,
|
|
15
|
+
initialize_empty: true
|
|
16
|
+
attribute :diagram_ids, :string, collection: true,
|
|
17
|
+
initialize_empty: true
|
|
18
|
+
attribute :stereotype_refs, :string, collection: true, initialize_empty: true
|
|
19
|
+
attribute :tagged_values, TaggedValue, collection: true, initialize_empty: true
|
|
20
|
+
attribute :annotations, Annotation, collection: true, initialize_empty: true
|
|
21
|
+
|
|
22
|
+
json do
|
|
23
|
+
map "id", to: :id
|
|
24
|
+
map "name", to: :name
|
|
25
|
+
map "parentId", to: :parent_id
|
|
26
|
+
map "qualifiedName", to: :qualified_name
|
|
27
|
+
map "subPackageIds", to: :sub_package_ids, render_empty: true
|
|
28
|
+
map "classifierIds", to: :classifier_ids, render_empty: true
|
|
29
|
+
map "diagramIds", to: :diagram_ids, render_empty: true
|
|
30
|
+
map "stereotypeRefs", to: :stereotype_refs, render_empty: true
|
|
31
|
+
map "taggedValues", to: :tagged_values, render_empty: true
|
|
32
|
+
map "annotations", to: :annotations, render_empty: true
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Model
|
|
5
|
+
# A single parameter of an Operation.
|
|
6
|
+
class Parameter < Base
|
|
7
|
+
attribute :ordinal, :integer
|
|
8
|
+
attribute :direction, :string, default: -> { "in" } # in|out|inout|return
|
|
9
|
+
attribute :type_name, :string
|
|
10
|
+
attribute :multiplicity_lower, :integer
|
|
11
|
+
attribute :multiplicity_upper, :integer
|
|
12
|
+
attribute :default_value, :string
|
|
13
|
+
|
|
14
|
+
json do
|
|
15
|
+
map "ordinal", to: :ordinal
|
|
16
|
+
map "direction", to: :direction
|
|
17
|
+
map "typeName", to: :type_name
|
|
18
|
+
map "multiplicityLower", to: :multiplicity_lower
|
|
19
|
+
map "multiplicityUpper", to: :multiplicity_upper
|
|
20
|
+
map "defaultValue", to: :default_value
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|