ea 0.1.0 → 0.1.5
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/CLAUDE.md +125 -0
- data/Rakefile +12 -4
- data/TODO.next/00-publish-blocking-bugs.md +74 -0
- data/TODO.next/01-standalone-ea-gem-identity.md +76 -0
- data/TODO.next/02-optional-lutaml-uml-dependency.md +47 -0
- data/TODO.next/03-slim-lutaml-uml.md +79 -0
- data/TODO.next/04-loader-registry-for-uml-repository.md +49 -0
- data/TODO.next/05-extract-shared-transformer-methods.md +14 -0
- data/TODO.next/06-deduplicate-stereotype-loading.md +17 -0
- data/TODO.next/07-transformer-registry-in-factory.md +20 -0
- data/TODO.next/08-connector-type-registry.md +27 -0
- data/TODO.next/09-element-renderer-registry.md +29 -0
- data/TODO.next/10-connector-renderer-lsp.md +18 -0
- data/TODO.next/11-consolidate-style-knowledge.md +33 -0
- data/TODO.next/12-data-driven-from-db-row.md +24 -0
- data/TODO.next/13-extract-duplicated-methods.md +17 -0
- data/TODO.next/14-remove-dead-code.md +10 -0
- data/TODO.next/15-narrow-exception-handling.md +39 -0
- data/TODO.next/16-repository-indexes.md +28 -0
- data/TODO.next/17-fix-spec-quality-and-coverage.md +32 -0
- data/TODO.next/18-xmi-tool-specific-parser-architecture.md +172 -0
- data/TODO.next/19-fix-ea-gemspec-dependency-declarations.md +56 -0
- data/TODO.next/20-ci-requires-unreleased-lutaml-uml.md +63 -0
- data/TODO.next/21-qeatoxmi-via-xmi-gem.md +340 -0
- data/TODO.next/22-strip-respond-to-from-qeatoxmi-specs.md +32 -0
- data/TODO.next/23-cleanup-idallocator.md +41 -0
- data/TODO.next/24-tighten-parity-specs.md +42 -0
- data/TODO.next/25-sparx-eaid-format-for-synthesized-ids.md +62 -0
- data/TODO.next/26-fix-uppervalue-lowervalue-count-gap.md +51 -0
- data/TODO.next/27-extract-cardinality-module.md +68 -0
- data/TODO.next/28-extract-xml-sanitizer.md +51 -0
- data/TODO.next/29-ocp-registry-for-classifier-builders.md +58 -0
- data/TODO.next/30-struct-return-for-association-end.md +37 -0
- data/TODO.next/31-idallocator-specs.md +27 -0
- data/TODO.next/32-phase2-gap-sentinel-specs.md +53 -0
- data/TODO.next/33-normalize-lower-cleanup.md +30 -0
- data/TODO.next/34-document-member-end-order-rt-prefix.md +29 -0
- data/TODO.next/35-walk-runstate-for-instance-slots.md +76 -0
- data/TODO.next/36-wire-interface-realization.md +50 -0
- data/TODO.next/37-visibility-returns-real-booleans.md +36 -0
- data/TODO.next/38-xmi-parser-feature-parity.md +75 -0
- data/TODO.next/39-ea-spa-cli-command.md +53 -0
- data/TODO.next/40-drop-lur-only-check-in-diagrams-extract.md +56 -0
- data/config/diagram_styles.yml +200 -0
- data/config/model_transformations.yml +266 -0
- data/config/qea_schema.yml +1024 -0
- data/docs/ea_to_uml_type_mapping.md +89 -0
- data/docs/xmi_qea_conversion_capabilities.md +99 -0
- data/examples/lur/20251010_current_plateau_v5.1.lur +0 -0
- data/examples/lur/basic.lur +0 -0
- data/examples/lur/test-output.lur +0 -0
- data/examples/lur/test.lur +0 -0
- data/examples/lur_basic_usage.rb +221 -0
- data/examples/lur_cli_workflow.rb +263 -0
- data/examples/lur_statistics.rb +326 -0
- data/examples/qea/20251010_current_plateau_v5.1.qea +0 -0
- data/examples/qea/ArcGISWorkspace_template.qea +0 -0
- data/examples/qea/README_qea_parser.adoc +230 -0
- data/examples/qea/UmlModel_template.qea +0 -0
- data/examples/qea/basic.qea +0 -0
- data/examples/qea/simple.qea +0 -0
- data/examples/qea/simple_example.qea +0 -0
- data/examples/qea/test.qea +0 -0
- data/examples/qea_standalone_query.rb +73 -0
- data/examples/qea_to_repository.rb +51 -0
- data/examples/smoke_test_real_qea.rb +81 -0
- data/exe/ea +7 -0
- data/lib/ea/cli/app.rb +81 -0
- data/lib/ea/cli/command/base.rb +80 -0
- data/lib/ea/cli/command/convert.rb +62 -0
- data/lib/ea/cli/command/diagrams.rb +75 -0
- data/lib/ea/cli/command/list.rb +61 -0
- data/lib/ea/cli/command/parse.rb +29 -0
- data/lib/ea/cli/command/repository_builder.rb +64 -0
- data/lib/ea/cli/command/spa.rb +79 -0
- data/lib/ea/cli/command/stats.rb +20 -0
- data/lib/ea/cli/command/validate.rb +41 -0
- data/lib/ea/cli/command.rb +17 -0
- data/lib/ea/cli/error.rb +34 -0
- data/lib/ea/cli/output/formatter.rb +34 -0
- data/lib/ea/cli/output/json_formatter.rb +20 -0
- data/lib/ea/cli/output/table_formatter.rb +42 -0
- data/lib/ea/cli/output/yaml_formatter.rb +20 -0
- data/lib/ea/cli/output.rb +56 -0
- data/lib/ea/cli.rb +17 -0
- data/lib/ea/diagram/configuration.rb +379 -0
- data/lib/ea/diagram/element_renderers/base_renderer.rb +77 -0
- data/lib/ea/diagram/element_renderers/class_renderer.rb +323 -0
- data/lib/ea/diagram/element_renderers/connector_renderer.rb +41 -0
- data/lib/ea/diagram/element_renderers/package_renderer.rb +61 -0
- data/lib/ea/diagram/element_renderers.rb +43 -0
- data/lib/ea/diagram/extractor.rb +560 -0
- data/lib/ea/diagram/layout_engine.rb +170 -0
- data/lib/ea/diagram/path_builder.rb +202 -0
- data/lib/ea/diagram/style_parser.rb +42 -0
- data/lib/ea/diagram/style_resolver.rb +276 -0
- data/lib/ea/diagram/svg_renderer.rb +274 -0
- data/lib/ea/diagram/util.rb +73 -0
- data/lib/ea/diagram.rb +47 -0
- data/lib/ea/qea/benchmark.rb +210 -0
- data/lib/ea/qea/database.rb +308 -0
- data/lib/ea/qea/factory/association_builder.rb +203 -0
- data/lib/ea/qea/factory/association_transformer.rb +91 -0
- data/lib/ea/qea/factory/attribute_tag_transformer.rb +57 -0
- data/lib/ea/qea/factory/attribute_transformer.rb +93 -0
- data/lib/ea/qea/factory/base_transformer.rb +177 -0
- data/lib/ea/qea/factory/class_transformer.rb +116 -0
- data/lib/ea/qea/factory/constraint_transformer.rb +75 -0
- data/lib/ea/qea/factory/data_type_transformer.rb +77 -0
- data/lib/ea/qea/factory/diagram_transformer.rb +157 -0
- data/lib/ea/qea/factory/document_builder.rb +283 -0
- data/lib/ea/qea/factory/ea_to_uml_factory.rb +229 -0
- data/lib/ea/qea/factory/enum_transformer.rb +74 -0
- data/lib/ea/qea/factory/generalization_builder.rb +227 -0
- data/lib/ea/qea/factory/generalization_transformer.rb +98 -0
- data/lib/ea/qea/factory/instance_transformer.rb +68 -0
- data/lib/ea/qea/factory/object_property_transformer.rb +58 -0
- data/lib/ea/qea/factory/operation_transformer.rb +66 -0
- data/lib/ea/qea/factory/package_transformer.rb +145 -0
- data/lib/ea/qea/factory/reference_resolver.rb +99 -0
- data/lib/ea/qea/factory/stereotype_loader.rb +39 -0
- data/lib/ea/qea/factory/tagged_value_transformer.rb +38 -0
- data/lib/ea/qea/factory/transformer_registry.rb +80 -0
- data/lib/ea/qea/factory.rb +37 -0
- data/lib/ea/qea/file_detector.rb +178 -0
- data/lib/ea/qea/infrastructure/database_connection.rb +100 -0
- data/lib/ea/qea/infrastructure/schema_reader.rb +136 -0
- data/lib/ea/qea/infrastructure/table_reader.rb +224 -0
- data/lib/ea/qea/infrastructure.rb +12 -0
- data/lib/ea/qea/models/base_model.rb +59 -0
- data/lib/ea/qea/models/ea_attribute.rb +109 -0
- data/lib/ea/qea/models/ea_attribute_tag.rb +100 -0
- data/lib/ea/qea/models/ea_complexity_type.rb +79 -0
- data/lib/ea/qea/models/ea_connector.rb +160 -0
- data/lib/ea/qea/models/ea_connector_type.rb +60 -0
- data/lib/ea/qea/models/ea_constraint_type.rb +63 -0
- data/lib/ea/qea/models/ea_datatype.rb +104 -0
- data/lib/ea/qea/models/ea_diagram.rb +115 -0
- data/lib/ea/qea/models/ea_diagram_link.rb +78 -0
- data/lib/ea/qea/models/ea_diagram_object.rb +73 -0
- data/lib/ea/qea/models/ea_diagram_type.rb +56 -0
- data/lib/ea/qea/models/ea_document.rb +63 -0
- data/lib/ea/qea/models/ea_object.rb +223 -0
- data/lib/ea/qea/models/ea_object_constraint.rb +53 -0
- data/lib/ea/qea/models/ea_object_property.rb +87 -0
- data/lib/ea/qea/models/ea_object_type.rb +73 -0
- data/lib/ea/qea/models/ea_operation.rb +127 -0
- data/lib/ea/qea/models/ea_operation_param.rb +76 -0
- data/lib/ea/qea/models/ea_package.rb +78 -0
- data/lib/ea/qea/models/ea_script.rb +62 -0
- data/lib/ea/qea/models/ea_status_type.rb +66 -0
- data/lib/ea/qea/models/ea_stereotype.rb +57 -0
- data/lib/ea/qea/models/ea_tagged_value.rb +99 -0
- data/lib/ea/qea/models/ea_xref.rb +165 -0
- data/lib/ea/qea/models.rb +35 -0
- data/lib/ea/qea/repositories/base_repository.rb +225 -0
- data/lib/ea/qea/repositories/object_repository.rb +219 -0
- data/lib/ea/qea/repositories.rb +10 -0
- data/lib/ea/qea/services/configuration.rb +211 -0
- data/lib/ea/qea/services/database_loader.rb +191 -0
- data/lib/ea/qea/services.rb +10 -0
- data/lib/ea/qea/validation/association_validator.rb +73 -0
- data/lib/ea/qea/validation/attribute_validator.rb +91 -0
- data/lib/ea/qea/validation/base_validator.rb +331 -0
- data/lib/ea/qea/validation/class_validator.rb +121 -0
- data/lib/ea/qea/validation/database/circular_reference_validator.rb +109 -0
- data/lib/ea/qea/validation/database/orphan_validator.rb +153 -0
- data/lib/ea/qea/validation/database/referential_integrity_validator.rb +128 -0
- data/lib/ea/qea/validation/database.rb +16 -0
- data/lib/ea/qea/validation/diagram_validator.rb +112 -0
- data/lib/ea/qea/validation/formatters/json_formatter.rb +137 -0
- data/lib/ea/qea/validation/formatters/text_formatter.rb +235 -0
- data/lib/ea/qea/validation/formatters.rb +12 -0
- data/lib/ea/qea/validation/operation_validator.rb +71 -0
- data/lib/ea/qea/validation/package_validator.rb +111 -0
- data/lib/ea/qea/validation/validation_engine.rb +387 -0
- data/lib/ea/qea/validation/validation_message.rb +144 -0
- data/lib/ea/qea/validation/validation_result.rb +210 -0
- data/lib/ea/qea/validation/validator_registry.rb +134 -0
- data/lib/ea/qea/validation.rb +28 -0
- data/lib/ea/qea/verification/comparison_result.rb +264 -0
- data/lib/ea/qea/verification/document_normalizer.rb +169 -0
- data/lib/ea/qea/verification/document_verifier.rb +322 -0
- data/lib/ea/qea/verification/element_comparator.rb +277 -0
- data/lib/ea/qea/verification/structure_matcher.rb +287 -0
- data/lib/ea/qea/verification.rb +14 -0
- data/lib/ea/qea.rb +185 -0
- data/lib/ea/transformations/configuration.rb +333 -0
- data/lib/ea/transformations/format_registry.rb +366 -0
- data/lib/ea/transformations/parsers/base_parser.rb +482 -0
- data/lib/ea/transformations/parsers/qea_parser.rb +401 -0
- data/lib/ea/transformations/parsers/xmi_parser.rb +243 -0
- data/lib/ea/transformations/transformation_engine.rb +390 -0
- data/lib/ea/transformations.rb +85 -0
- data/lib/ea/transformers/qea_to_xmi/association_end.rb +19 -0
- data/lib/ea/transformers/qea_to_xmi/cardinality.rb +96 -0
- data/lib/ea/transformers/qea_to_xmi/context.rb +106 -0
- data/lib/ea/transformers/qea_to_xmi/guid_format.rb +56 -0
- data/lib/ea/transformers/qea_to_xmi/id_allocator.rb +92 -0
- data/lib/ea/transformers/qea_to_xmi/run_state.rb +107 -0
- data/lib/ea/transformers/qea_to_xmi/transformer.rb +607 -0
- data/lib/ea/transformers/qea_to_xmi/visibility.rb +73 -0
- data/lib/ea/transformers/qea_to_xmi.rb +29 -0
- data/lib/ea/transformers/uml_to_xmi/id_generator.rb +54 -0
- data/lib/ea/transformers/uml_to_xmi/transformer.rb +152 -0
- data/lib/ea/transformers/uml_to_xmi/writer.rb +96 -0
- data/lib/ea/transformers/uml_to_xmi.rb +16 -0
- data/lib/ea/transformers.rb +34 -0
- data/lib/ea/version.rb +1 -1
- data/lib/ea/xmi/liquid_drops/association_drop.rb +56 -0
- data/lib/ea/xmi/liquid_drops/attribute_drop.rb +72 -0
- data/lib/ea/xmi/liquid_drops/cardinality_drop.rb +35 -0
- data/lib/ea/xmi/liquid_drops/connector_drop.rb +54 -0
- data/lib/ea/xmi/liquid_drops/constraint_drop.rb +29 -0
- data/lib/ea/xmi/liquid_drops/data_type_drop.rb +63 -0
- data/lib/ea/xmi/liquid_drops/dependency_drop.rb +36 -0
- data/lib/ea/xmi/liquid_drops/diagram_drop.rb +34 -0
- data/lib/ea/xmi/liquid_drops/enum_drop.rb +49 -0
- data/lib/ea/xmi/liquid_drops/enum_owned_literal_drop.rb +25 -0
- data/lib/ea/xmi/liquid_drops/generalization_attribute_drop.rb +87 -0
- data/lib/ea/xmi/liquid_drops/generalization_drop.rb +127 -0
- data/lib/ea/xmi/liquid_drops/klass_drop.rb +191 -0
- data/lib/ea/xmi/liquid_drops/operation_drop.rb +29 -0
- data/lib/ea/xmi/liquid_drops/package_drop.rb +108 -0
- data/lib/ea/xmi/liquid_drops/root_drop.rb +34 -0
- data/lib/ea/xmi/liquid_drops/source_target_drop.rb +43 -0
- data/lib/ea/xmi/lookup_service.rb +89 -0
- data/lib/ea/xmi/parser.rb +1015 -0
- data/lib/ea/xmi.rb +35 -0
- data/lib/ea.rb +10 -1
- metadata +387 -9
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# End-to-end smoke test: parse a real Sparx EA .qea file end-to-end through
|
|
5
|
+
# both the standalone QEA loader and the UML bridge.
|
|
6
|
+
#
|
|
7
|
+
# Run: bundle exec ruby examples/smoke_test_real_qea.rb
|
|
8
|
+
#
|
|
9
|
+
# Demonstrates:
|
|
10
|
+
# 1. Ea::Qea.database_info — quick table-count stats (no full load)
|
|
11
|
+
# 2. Ea::Qea.load — standalone load → Ea::Qea::Database
|
|
12
|
+
# 3. Ea::Qea.to_uml — bridge → Lutaml::Uml::Document
|
|
13
|
+
#
|
|
14
|
+
# Requires a real .qea file. Defaults to the plateau model fixture; override
|
|
15
|
+
# with the QEA_PATH environment variable.
|
|
16
|
+
|
|
17
|
+
require "bundler/setup"
|
|
18
|
+
require "lutaml/uml"
|
|
19
|
+
require "ea"
|
|
20
|
+
|
|
21
|
+
QEA_PATH = ENV.fetch(
|
|
22
|
+
"QEA_PATH",
|
|
23
|
+
"/Users/mulgogi/src/mn/plateau-model/20251010_current_plateau_v5.1.qea",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
abort "QEA not found: #{QEA_PATH}" unless File.exist?(QEA_PATH)
|
|
27
|
+
|
|
28
|
+
puts "QEA: #{QEA_PATH}"
|
|
29
|
+
puts "Size: #{(File.size(QEA_PATH) / 1_048_576.0).round(2)} MB"
|
|
30
|
+
puts
|
|
31
|
+
|
|
32
|
+
# 1. Quick stats — opens the SQLite DB and counts rows per table, no model load
|
|
33
|
+
puts "== database_info (quick stats, top 12 tables) =="
|
|
34
|
+
Ea::Qea.database_info(QEA_PATH)
|
|
35
|
+
.sort_by { |_, count| -count.to_i }
|
|
36
|
+
.first(12)
|
|
37
|
+
.each { |table, count| puts " #{table}: #{count}" }
|
|
38
|
+
puts
|
|
39
|
+
|
|
40
|
+
# 2. Standalone load — pure QEA parsing, no lutaml-uml needed
|
|
41
|
+
puts "== Ea::Qea.load (standalone) =="
|
|
42
|
+
database = Ea::Qea.load(QEA_PATH)
|
|
43
|
+
puts " loaded: #{database.class}"
|
|
44
|
+
puts " total_records: #{database.total_records}"
|
|
45
|
+
puts
|
|
46
|
+
|
|
47
|
+
puts "== EA objects by type =="
|
|
48
|
+
database.objects.group_by(:object_type)
|
|
49
|
+
.sort_by { |_, objs| -objs.size }
|
|
50
|
+
.each { |type, objs| puts " #{type}: #{objs.size}" }
|
|
51
|
+
puts
|
|
52
|
+
|
|
53
|
+
# 3. UML bridge — compose with lutaml-uml to get a Lutaml::Uml::Document
|
|
54
|
+
puts "== Ea::Qea.to_uml (bridge) =="
|
|
55
|
+
document = Ea::Qea.to_uml(database)
|
|
56
|
+
puts " document: #{document.class}"
|
|
57
|
+
puts
|
|
58
|
+
|
|
59
|
+
# 4. Walk the package tree — classes live inside packages, not at document root
|
|
60
|
+
def walk(pkgs, counters)
|
|
61
|
+
Array(pkgs).each do |pkg|
|
|
62
|
+
counters[:packages] += 1
|
|
63
|
+
counters[:classes] += Array(pkg.classes).size
|
|
64
|
+
counters[:enums] += Array(pkg.enums).size
|
|
65
|
+
counters[:data_types] += Array(pkg.data_types).size
|
|
66
|
+
counters[:instances] += Array(pkg.instances).size
|
|
67
|
+
walk(pkg.packages, counters)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
puts "== UML Document (incl. nested in packages) =="
|
|
72
|
+
counters = { packages: 0, classes: 0, enums: 0, data_types: 0, instances: 0 }
|
|
73
|
+
walk(document.packages, counters)
|
|
74
|
+
counters.each { |k, v| puts " #{k}: #{v}" }
|
|
75
|
+
puts " associations (document root): #{document.associations.size}"
|
|
76
|
+
puts " diagrams (document root): #{document.diagrams.size}"
|
|
77
|
+
puts " orphan classes (document root): #{document.classes.size}"
|
|
78
|
+
puts " ^ classes whose EA package_id has no t_package row"
|
|
79
|
+
puts
|
|
80
|
+
|
|
81
|
+
puts "SMOKE TEST PASSED"
|
data/exe/ea
ADDED
data/lib/ea/cli/app.rb
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
|
|
5
|
+
module Ea
|
|
6
|
+
module Cli
|
|
7
|
+
class App < Thor
|
|
8
|
+
class << self
|
|
9
|
+
def exit_on_failure?
|
|
10
|
+
true
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
desc "version", "Show ea gem version"
|
|
15
|
+
def version
|
|
16
|
+
puts Ea::VERSION
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
desc "list FILE", "List model elements (auto-detects QEA or XMI)"
|
|
20
|
+
option :type, type: :string,
|
|
21
|
+
desc: "Filter: class | interface | package | diagram | connector | enum"
|
|
22
|
+
option :format, type: :string, default: "table",
|
|
23
|
+
desc: "Output format: table | json | yaml"
|
|
24
|
+
def list(file)
|
|
25
|
+
Command::List.new(file: file, **symbolize(options)).call
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "diagrams ACTION FILE [NAME]",
|
|
29
|
+
"Diagram operations: list FILE | extract NAME FILE"
|
|
30
|
+
option :format, type: :string, default: "table"
|
|
31
|
+
option :output, type: :string, desc: "Output path (extract only)"
|
|
32
|
+
def diagrams(action, file = nil, name = nil)
|
|
33
|
+
Command::Diagrams
|
|
34
|
+
.new(action: action, file: file, name: name, **symbolize(options))
|
|
35
|
+
.call
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
desc "validate FILE", "Validate EA model"
|
|
39
|
+
option :format, type: :string, default: "table"
|
|
40
|
+
def validate(file)
|
|
41
|
+
Command::Validate.new(file: file, **symbolize(options)).call
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
desc "stats FILE", "Show collection counts (standalone — no lutaml-uml)"
|
|
45
|
+
option :format, type: :string, default: "table"
|
|
46
|
+
def stats(file)
|
|
47
|
+
Command::Stats.new(file: file, **symbolize(options)).call
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
desc "parse FILE", "Parse to Lutaml::Uml::Document (requires lutaml-uml)"
|
|
51
|
+
option :format, type: :string, default: "yaml",
|
|
52
|
+
desc: "Output: json | yaml"
|
|
53
|
+
def parse(file)
|
|
54
|
+
Command::Parse.new(file: file, **symbolize(options)).call
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
desc "convert FILE", "Convert between EA formats (e.g. QEA → XMI)"
|
|
58
|
+
option :to, type: :string, required: true, desc: "Target format: xmi"
|
|
59
|
+
option :output, type: :string, desc: "Output path"
|
|
60
|
+
option :format, type: :string, default: "table"
|
|
61
|
+
def convert(file)
|
|
62
|
+
Command::Convert.new(file: file, **symbolize(options)).call
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
desc "spa FILE", "Generate single-page app (SPA) from QEA/XMI/LUR"
|
|
66
|
+
option :output, type: :string,
|
|
67
|
+
desc: "Output path (default: <basename>.html)"
|
|
68
|
+
option :mode, type: :string, default: "single_file",
|
|
69
|
+
desc: "Output mode: single_file | multi_file"
|
|
70
|
+
def spa(file)
|
|
71
|
+
Command::Spa.new(file: file, **symbolize(options)).call
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def symbolize(opts)
|
|
77
|
+
opts.transform_keys(&:to_sym)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Cli
|
|
5
|
+
module Command
|
|
6
|
+
# Shared base for all CLI commands.
|
|
7
|
+
#
|
|
8
|
+
# Provides:
|
|
9
|
+
# - Options Hash access (read-only)
|
|
10
|
+
# - Output formatter resolution from `:format` option
|
|
11
|
+
# - Standalone QEA database loading (no lutaml-uml required)
|
|
12
|
+
# - UML document parsing (lazy-loads lutaml-uml)
|
|
13
|
+
# - File existence validation
|
|
14
|
+
#
|
|
15
|
+
# Subclasses implement {#call} and may use the protected helpers.
|
|
16
|
+
class Base
|
|
17
|
+
def initialize(options = {})
|
|
18
|
+
@options = options
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Template method — subclasses implement.
|
|
22
|
+
# @raise [NotImplementedError] if not overridden
|
|
23
|
+
def call
|
|
24
|
+
raise NotImplementedError, "#{self.class}#call not implemented"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
|
|
29
|
+
attr_reader :options
|
|
30
|
+
|
|
31
|
+
def formatter
|
|
32
|
+
Ea::Cli::Output.instance_for(options[:format] || :table)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def file_path
|
|
36
|
+
options[:file] or raise Ea::Cli::Error, "missing required --file"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Load a QEA database. Works standalone — no lutaml-uml dependency.
|
|
40
|
+
# @return [Ea::Qea::Database]
|
|
41
|
+
def load_database(path = file_path)
|
|
42
|
+
validate_file!(path)
|
|
43
|
+
Ea::Qea.load(path)
|
|
44
|
+
rescue Ea::Cli::Error
|
|
45
|
+
raise
|
|
46
|
+
rescue Ea::Error => e
|
|
47
|
+
raise Ea::Cli::Error, "Failed to load #{path}: #{e.message}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Parse any EA file to a Lutaml::Uml::Document.
|
|
51
|
+
# Lazy-loads lutaml-uml via Ea::Qea's require_uml! guard.
|
|
52
|
+
# @return [Lutaml::Uml::Document]
|
|
53
|
+
def parse_to_uml(path = file_path, **parse_options)
|
|
54
|
+
validate_file!(path)
|
|
55
|
+
Ea::Qea.parse(path, parse_options)
|
|
56
|
+
rescue Ea::Cli::Error
|
|
57
|
+
raise
|
|
58
|
+
rescue Ea::Error => e
|
|
59
|
+
if e.message.include?("lutaml-uml")
|
|
60
|
+
raise Ea::Cli::MissingUmlDependency
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
raise Ea::Cli::Error, "Failed to parse #{path}: #{e.message}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def validate_file!(path)
|
|
67
|
+
raise Ea::Cli::FileNotFound, path unless File.exist?(path)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Write content to a path; honors the `:output` option.
|
|
71
|
+
# @return [String] the path written to
|
|
72
|
+
def write_output(content, default_name:)
|
|
73
|
+
path = options[:output] || default_name
|
|
74
|
+
File.write(path, content)
|
|
75
|
+
path
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Cli
|
|
5
|
+
module Command
|
|
6
|
+
# `ea convert FILE --to xmi [--output PATH]`
|
|
7
|
+
#
|
|
8
|
+
# Converts an EA file to another format. Currently supports:
|
|
9
|
+
# --to xmi .qea → Sparx XMI (direct, full Sparx fidelity)
|
|
10
|
+
#
|
|
11
|
+
# Routing is by input file extension:
|
|
12
|
+
# .qea → Ea::Transformers.qea_to_xmi (direct path, no intermediate
|
|
13
|
+
# UML model — preserves all Sparx-specific concepts)
|
|
14
|
+
# any other extension → UnsupportedFormat
|
|
15
|
+
#
|
|
16
|
+
# Future formats (e.g. parsing a Lutaml::LML .lur file) are added by
|
|
17
|
+
# extending the case statement in {#convert_to_xmi}.
|
|
18
|
+
class Convert < Base
|
|
19
|
+
SUPPORTED_TARGETS = %w[xmi].freeze
|
|
20
|
+
SUPPORTED_INPUT_FORMATS = %i[qea].freeze
|
|
21
|
+
|
|
22
|
+
def call
|
|
23
|
+
target = options[:to] or raise Ea::Cli::Error, "missing required --to"
|
|
24
|
+
case target.to_s
|
|
25
|
+
when "xmi" then convert_to_xmi
|
|
26
|
+
else
|
|
27
|
+
raise Ea::Cli::Error,
|
|
28
|
+
"Unsupported target '#{target}'. " \
|
|
29
|
+
"Valid: #{SUPPORTED_TARGETS.join(', ')}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def convert_to_xmi
|
|
36
|
+
xml = case input_format
|
|
37
|
+
when :qea then qea_to_xmi_xml
|
|
38
|
+
else
|
|
39
|
+
raise Ea::Cli::UnsupportedFormat.new(
|
|
40
|
+
file_path,
|
|
41
|
+
"supported inputs: " \
|
|
42
|
+
"#{SUPPORTED_INPUT_FORMATS.join(', ')}",
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
path = write_output(xml, default_name: "#{file_path}.xmi")
|
|
46
|
+
formatter.render([[path]], columns: [:written_to])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def qea_to_xmi_xml
|
|
50
|
+
database = load_database(file_path)
|
|
51
|
+
Ea::Transformers.qea_to_xmi(database)
|
|
52
|
+
ensure
|
|
53
|
+
database&.close_connection
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def input_format
|
|
57
|
+
File.extname(file_path).downcase[1..].to_sym
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Cli
|
|
5
|
+
module Command
|
|
6
|
+
# `ea diagrams ACTION FILE [NAME]`
|
|
7
|
+
#
|
|
8
|
+
# Actions:
|
|
9
|
+
# list FILE — list diagrams in a QEA/XMI/LUR file (standalone)
|
|
10
|
+
# extract FILE NAME — render the named diagram to SVG
|
|
11
|
+
#
|
|
12
|
+
# `list` reads the EA database directly (no lutaml-uml required
|
|
13
|
+
# for QEA; XMI parsing via xmi gem only).
|
|
14
|
+
#
|
|
15
|
+
# `extract` accepts QEA, XMI, or LUR. The Repository is built
|
|
16
|
+
# from the input file via `RepositoryBuilder` (single source of
|
|
17
|
+
# truth — QEA/XMI are parsed via `Ea::Transformations`, LUR is
|
|
18
|
+
# loaded natively via `Repository.from_file`).
|
|
19
|
+
class Diagrams < Base
|
|
20
|
+
include RepositoryBuilder
|
|
21
|
+
|
|
22
|
+
ACTIONS = %w[list extract].freeze
|
|
23
|
+
|
|
24
|
+
def call
|
|
25
|
+
case action
|
|
26
|
+
when "list" then list
|
|
27
|
+
when "extract" then extract
|
|
28
|
+
else
|
|
29
|
+
raise Ea::Cli::UnknownAction.new(action, valid: ACTIONS)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def action
|
|
36
|
+
options[:action] or raise Ea::Cli::Error, "missing required ACTION"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def name
|
|
40
|
+
options[:name] or raise Ea::Cli::Error, "missing required NAME"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def list
|
|
44
|
+
db = load_database
|
|
45
|
+
rows = db.diagrams.map { |d| [d.name, diagram_type_label(d), d.ea_guid] }
|
|
46
|
+
formatter.render(rows, columns: %i[name type guid])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def extract
|
|
50
|
+
repository = RepositoryBuilder.build_repository(file_path)
|
|
51
|
+
result = extractor.extract_one(repository, name, extract_options)
|
|
52
|
+
raise Ea::Cli::Error, result[:message] unless result[:success]
|
|
53
|
+
|
|
54
|
+
path = result[:path] || write_output(result.fetch(:svg_content),
|
|
55
|
+
default_name: "#{name}.svg")
|
|
56
|
+
formatter.render([[path]], columns: [:written_to])
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def extractor
|
|
60
|
+
Ea::Diagram::Extractor.new
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def extract_options
|
|
64
|
+
opts = {}
|
|
65
|
+
opts[:output] = options[:output] if options[:output]
|
|
66
|
+
opts
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def diagram_type_label(diagram)
|
|
70
|
+
diagram.diagram_type || "Logical"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Cli
|
|
5
|
+
module Command
|
|
6
|
+
# `ea list FILE [--type TYPE]`
|
|
7
|
+
#
|
|
8
|
+
# Lists model elements from a QEA file. With `--type`, returns elements
|
|
9
|
+
# of one kind (class/interface/package/diagram/connector/enum). Without
|
|
10
|
+
# `--type`, returns a summary of counts per kind.
|
|
11
|
+
#
|
|
12
|
+
# Operates standalone — does not require lutaml-uml.
|
|
13
|
+
class List < Base
|
|
14
|
+
def call
|
|
15
|
+
rows =
|
|
16
|
+
if options[:type]
|
|
17
|
+
list_by_type(options[:type])
|
|
18
|
+
else
|
|
19
|
+
list_summary
|
|
20
|
+
end
|
|
21
|
+
formatter.render(rows, columns: columns_for_current_mode)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def list_summary
|
|
27
|
+
db = load_database
|
|
28
|
+
[
|
|
29
|
+
[:classes, db.objects.find_by_type("Class").size],
|
|
30
|
+
[:interfaces, db.objects.find_by_type("Interface").size],
|
|
31
|
+
[:packages, db.packages.size],
|
|
32
|
+
[:enums, db.objects.find_by_type("Enumeration").size],
|
|
33
|
+
[:datatypes, db.objects.find_by_type("DataType").size],
|
|
34
|
+
[:diagrams, db.diagrams.size],
|
|
35
|
+
[:connectors, db.connectors.size],
|
|
36
|
+
]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def list_by_type(type)
|
|
40
|
+
db = load_database
|
|
41
|
+
case type.to_s
|
|
42
|
+
when "class" then db.objects.find_by_type("Class").map { |o| [o.name, o.ea_guid] }
|
|
43
|
+
when "interface" then db.objects.find_by_type("Interface").map { |o| [o.name, o.ea_guid] }
|
|
44
|
+
when "enum" then db.objects.find_by_type("Enumeration").map { |o| [o.name, o.ea_guid] }
|
|
45
|
+
when "package" then db.packages.map { |p| [p.name, p.ea_guid] }
|
|
46
|
+
when "diagram" then db.diagrams.map { |d| [d.name, d.ea_guid] }
|
|
47
|
+
when "connector" then db.connectors.map { |c| [c.name, c.ea_guid] }
|
|
48
|
+
else
|
|
49
|
+
raise Ea::Cli::Error,
|
|
50
|
+
"Unknown type '#{type}'. " \
|
|
51
|
+
"Valid: class, interface, package, diagram, connector, enum"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def columns_for_current_mode
|
|
56
|
+
options[:type] ? %i[name guid] : %i[kind count]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Cli
|
|
5
|
+
module Command
|
|
6
|
+
# `ea parse FILE [--format json|yaml]`
|
|
7
|
+
#
|
|
8
|
+
# Parses the EA file (QEA or Sparx XMI) to a Lutaml::Uml::Document and
|
|
9
|
+
# serializes it. Requires lutaml-uml.
|
|
10
|
+
class Parse < Base
|
|
11
|
+
def call
|
|
12
|
+
document = parse_to_uml(file_path)
|
|
13
|
+
formatter_output(document)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def formatter_output(document)
|
|
19
|
+
case (options[:format] || :yaml).to_sym
|
|
20
|
+
when :json then puts document.to_json
|
|
21
|
+
when :yaml then puts document.to_yaml
|
|
22
|
+
else
|
|
23
|
+
raise Ea::Cli::Error, "Unknown format: #{options[:format]}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Cli
|
|
5
|
+
module Command
|
|
6
|
+
# Shared helper for building a `Lutaml::UmlRepository::Repository`
|
|
7
|
+
# from any file format supported by `Ea::Transformations`.
|
|
8
|
+
#
|
|
9
|
+
# Single source of truth for "QEA/XMI/LUR → Repository" wiring.
|
|
10
|
+
# Used by `Ea::Cli::Command::Diagrams` (extract action) and
|
|
11
|
+
# `Ea::Cli::Command::Spa` (SPA generation).
|
|
12
|
+
#
|
|
13
|
+
# `.lur` files take the fast path via `Repository.from_file` (the
|
|
14
|
+
# native LUR loader). QEA/XMI files are parsed via
|
|
15
|
+
# `Ea::Transformations.parse` and wrapped via
|
|
16
|
+
# `Repository.from_document`.
|
|
17
|
+
module RepositoryBuilder
|
|
18
|
+
LUR_EXT = ".lur".freeze
|
|
19
|
+
|
|
20
|
+
module_function
|
|
21
|
+
|
|
22
|
+
# @param path [String] file path (QEA, XMI, or LUR)
|
|
23
|
+
# @return [Lutaml::UmlRepository::Repository]
|
|
24
|
+
def build_repository(path)
|
|
25
|
+
require_lutaml_uml!
|
|
26
|
+
|
|
27
|
+
if lur?(path)
|
|
28
|
+
require_lutaml_uml_repository!
|
|
29
|
+
return Lutaml::UmlRepository::Repository.from_file(path)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
document = Ea::Transformations.parse(path)
|
|
33
|
+
require_lutaml_uml_repository!
|
|
34
|
+
Lutaml::UmlRepository::Repository.from_document(document)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def lur?(path)
|
|
38
|
+
File.extname(path).downcase == LUR_EXT
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Loads the bridge gem. The lutaml-uml gem is an optional
|
|
42
|
+
# dependency; if missing, raise the CLI's MissingUmlDependency
|
|
43
|
+
# so the user gets a clear message about installing it.
|
|
44
|
+
def require_lutaml_uml!
|
|
45
|
+
require "lutaml/uml"
|
|
46
|
+
rescue LoadError => e
|
|
47
|
+
raise Ea::Cli::MissingUmlDependency,
|
|
48
|
+
"spa and diagrams-extract commands require the " \
|
|
49
|
+
"`lutaml-uml` gem. Install it via `gem install lutaml-uml` " \
|
|
50
|
+
"or add it to your Gemfile. (#{e.message})"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def require_lutaml_uml_repository!
|
|
54
|
+
require "lutaml/uml_repository"
|
|
55
|
+
rescue LoadError => e
|
|
56
|
+
raise Ea::Cli::MissingUmlDependency,
|
|
57
|
+
"spa and diagrams-extract commands require the " \
|
|
58
|
+
"`lutaml-uml` gem. Install it via `gem install lutaml-uml` " \
|
|
59
|
+
"or add it to your Gemfile. (#{e.message})"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Cli
|
|
5
|
+
module Command
|
|
6
|
+
# `ea spa FILE [--output=PATH] [--mode=MODE]`
|
|
7
|
+
#
|
|
8
|
+
# Generates a static single-page application (SPA) from a QEA,
|
|
9
|
+
# XMI, or LUR file. The SPA shows the package hierarchy, class
|
|
10
|
+
# details, diagram list, and member navigation in a browser.
|
|
11
|
+
#
|
|
12
|
+
# Pipeline:
|
|
13
|
+
# 1. Ea::Transformations.parse(file) → Lutaml::Uml::Document
|
|
14
|
+
# 2. Lutaml::UmlRepository::Repository.from_document(document)
|
|
15
|
+
# 3. Lutaml::UmlRepository::StaticSite::Generator.new(repo, ...).generate
|
|
16
|
+
#
|
|
17
|
+
# Output format defaults to single-file Vue IIFE HTML.
|
|
18
|
+
class Spa < Base
|
|
19
|
+
DEFAULT_MODE = :single_file
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
repository = RepositoryBuilder.build_repository(file_path)
|
|
23
|
+
|
|
24
|
+
require_lutaml_uml_static_site!
|
|
25
|
+
strategy = resolve_output_strategy
|
|
26
|
+
|
|
27
|
+
output_path = resolve_output_path
|
|
28
|
+
options = {
|
|
29
|
+
output: output_path,
|
|
30
|
+
mode: mode,
|
|
31
|
+
output_strategy: strategy,
|
|
32
|
+
}.compact
|
|
33
|
+
|
|
34
|
+
Lutaml::UmlRepository::StaticSite::Generator
|
|
35
|
+
.new(repository, options)
|
|
36
|
+
.generate
|
|
37
|
+
|
|
38
|
+
formatter.render([[output_path]], columns: [:written_to])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def mode
|
|
44
|
+
(options[:mode] || DEFAULT_MODE).to_sym
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def resolve_output_path
|
|
48
|
+
options[:output] || default_output_path
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def default_output_path
|
|
52
|
+
base = File.basename(file_path, ".*")
|
|
53
|
+
dir = File.dirname(file_path)
|
|
54
|
+
File.join(dir, "#{base}.html")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def resolve_output_strategy
|
|
58
|
+
case mode
|
|
59
|
+
when :single_file, "single_file"
|
|
60
|
+
Lutaml::UmlRepository::StaticSite::Output::VueInlinedStrategy
|
|
61
|
+
when :multi_file, "multi_file"
|
|
62
|
+
Lutaml::UmlRepository::StaticSite::Output::MultiFileStrategy
|
|
63
|
+
else
|
|
64
|
+
raise Ea::Cli::UnsupportedFormat,
|
|
65
|
+
"Unknown spa mode '#{mode}': use single_file or multi_file"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def require_lutaml_uml_static_site!
|
|
70
|
+
require "lutaml/uml_repository/static_site"
|
|
71
|
+
rescue LoadError => e
|
|
72
|
+
raise Ea::Cli::MissingUmlDependency,
|
|
73
|
+
"spa command requires the `lutaml-uml` gem. " \
|
|
74
|
+
"Install it via `gem install lutaml-uml`. (#{e.message})"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Cli
|
|
5
|
+
module Command
|
|
6
|
+
# `ea stats FILE`
|
|
7
|
+
#
|
|
8
|
+
# Standalone — does not require lutaml-uml. Reads the QEA database
|
|
9
|
+
# directly and prints per-collection counts.
|
|
10
|
+
class Stats < Base
|
|
11
|
+
def call
|
|
12
|
+
db = load_database
|
|
13
|
+
stats = db.stats
|
|
14
|
+
rows = stats.map { |k, v| [k, v] }
|
|
15
|
+
formatter.render(rows, columns: %i[collection count])
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Cli
|
|
5
|
+
module Command
|
|
6
|
+
# `ea validate FILE`
|
|
7
|
+
#
|
|
8
|
+
# Runs Ea::Qea::Validation::ValidationEngine and reports messages.
|
|
9
|
+
# Exits non-zero if any errors were found.
|
|
10
|
+
#
|
|
11
|
+
# Requires lutaml-uml (validation runs against Lutaml::Uml::Document).
|
|
12
|
+
class Validate < Base
|
|
13
|
+
COLUMNS = %i[severity entity_type entity_name message].freeze
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
result = parse_with_validation(file_path)
|
|
17
|
+
rows = result.messages.map do |m|
|
|
18
|
+
[m.severity, m.entity_type, m.entity_name, m.message]
|
|
19
|
+
end
|
|
20
|
+
formatter.render(rows, columns: COLUMNS)
|
|
21
|
+
exit(1) if result.errors.any?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def parse_with_validation(path)
|
|
27
|
+
validate_file!(path)
|
|
28
|
+
Ea::Qea.parse(path, validate: true)[:validation_result]
|
|
29
|
+
rescue Ea::Cli::Error
|
|
30
|
+
raise
|
|
31
|
+
rescue Ea::Error => e
|
|
32
|
+
if e.message.include?("lutaml-uml")
|
|
33
|
+
raise Ea::Cli::MissingUmlDependency
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
raise Ea::Cli::Error, "Failed to validate #{path}: #{e.message}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|