ea 0.4.0 → 0.5.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/CHANGELOG.md +128 -1
- data/Rakefile +3 -0
- data/TODO.complete/01-t-xref-parsing.md +91 -0
- data/TODO.complete/02-per-label-geometry.md +50 -0
- data/TODO.complete/03-object-properties-constraints.md +57 -0
- data/TODO.complete/04-operation-params.md +37 -0
- data/TODO.complete/05-pdata-flag-matrix.md +52 -0
- data/TODO.complete/06-emf-image-rendering.md +34 -0
- data/TODO.complete/07-auxiliary-tables.md +47 -0
- data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
- data/TODO.complete/11-gml-xsd-generation.md +42 -0
- data/TODO.complete/13-shapescript-interpreter.md +28 -0
- data/TODO.complete/20-cli-diff.md +46 -0
- data/TODO.complete/21-cli-render.md +26 -0
- data/TODO.complete/22-cli-export.md +29 -0
- data/TODO.complete/23-cli-mdg.md +23 -0
- data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
- data/TODO.complete/31-public-send-cleanup.md +61 -0
- data/TODO.complete/32-spec-coverage.md +33 -0
- data/TODO.complete/33-blocked-parity-gaps.md +41 -0
- data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
- data/TODO.complete/37-cli-lint.md +32 -0
- data/TODO.complete/38-cli-query.md +26 -0
- data/TODO.complete/39-cli-info.md +23 -0
- data/TODO.complete/40-ocl-evaluator.md +31 -0
- data/TODO.complete/41-round-trip-spec.md +18 -0
- data/TODO.complete/42-html-diff-report.md +18 -0
- data/TODO.complete/43-json-schema-export.md +20 -0
- data/TODO.complete/44-remaining-qea-tables.md +40 -0
- data/TODO.complete/45-shapescript-extensions.md +25 -0
- data/TODO.complete/46-xsd-consolidation.md +20 -0
- data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
- data/TODO.complete/51-remove-respond-to.md +25 -0
- data/TODO.complete/52-xsd-decouple-fixtures.md +23 -0
- data/TODO.complete/53-xmi-public-send-cleanup.md +26 -0
- data/TODO.complete/54-wire-stereotype-icon-renderer.md +20 -0
- data/TODO.complete/55-wire-shapescript.md +20 -0
- data/TODO.complete/56-wire-emf-renderer.md +27 -0
- data/TODO.complete/57-wire-ocl-evaluator.md +23 -0
- data/TODO.complete/58-diff-modifications.md +21 -0
- data/TODO.complete/59-delete-toy-xmi-export.md +30 -0
- data/TODO.complete/60-curate-json-export.md +20 -0
- data/TODO.complete/61-plantuml-relationships.md +22 -0
- data/TODO.complete/62-changelog.md +17 -0
- data/TODO.complete/63-dependabot-fix.md +18 -0
- data/TODO.complete/STATUS.md +85 -0
- data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
- data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
- data/TODO.diagrams/03-text-over-rendering.md +30 -0
- data/TODO.diagrams/04-rect-under-rendering.md +33 -0
- data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
- data/TODO.diagrams/06-group-layering-parity.md +39 -0
- data/TODO.diagrams/07-text-content-fidelity.md +65 -0
- data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
- data/TODO.diagrams/12-connector-labels.md +40 -0
- data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
- data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
- data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
- data/TODO.diagrams/14-center-header-text.md +33 -0
- data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
- data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
- data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
- data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
- data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
- data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
- data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
- data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
- data/TODO.diagrams/18-property-lookup-index.md +42 -0
- data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
- data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
- data/TODO.diagrams/20-split-labels-emitter.md +56 -0
- data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
- data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
- data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
- data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
- data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
- data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
- data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
- data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
- data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
- data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
- data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
- data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
- data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
- data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
- data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
- data/TODO.diagrams/36-instance-specification-model.md +42 -0
- data/TODO.diagrams/37-element-icon-decorations.md +41 -0
- data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
- data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
- data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
- data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
- data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
- data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
- data/TODO.diagrams/44-element-icon-decorations.md +34 -0
- data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
- data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
- data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
- data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
- data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
- data/TODO.diagrams/50-examples-parity.md +85 -0
- data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
- data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
- data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
- data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
- data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
- data/TODO.diagrams/56-package-contents-rendering.md +148 -0
- data/TODO.diagrams/57-instance-specification-model.md +100 -0
- data/TODO.diagrams/58-domain-model-note-text.md +67 -0
- data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
- data/TODO.diagrams/60-testschema-association-marker.md +40 -0
- data/TODO.diagrams/61-legend-block-rendering.md +55 -0
- data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
- data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
- data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
- data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
- data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
- data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
- data/TODO.diagrams/68-inherited-attributes.md +65 -0
- data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
- data/TODO.diagrams/70-mdg-technology-support.md +68 -0
- data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
- data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
- data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
- data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
- data/TODO.diagrams/75-plateau-text-gap.md +39 -0
- data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
- data/TODO.diagrams/77-basic-text-overrender.md +40 -0
- data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
- data/TODO.diagrams/79-architecture-backlog.md +65 -0
- data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
- data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
- data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
- data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
- data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
- data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
- data/TODO.diagrams/86-foreign-package-rule.md +62 -0
- data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
- data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
- data/TODO.diagrams/README.md +69 -0
- data/TODO.diagrams/STATUS.md +73 -0
- data/TODO.fixup/01-default-grouped-paths.md +34 -0
- data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
- data/TODO.fixup/03-diagram-font-inference.md +28 -0
- data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
- data/TODO.fixup/05-marker-registry-ocp.md +32 -0
- data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
- data/TODO.fixup/07-edge-attachment-formula.md +22 -0
- data/TODO.fixup/08-visual-diff-harness.md +28 -0
- data/TODO.fixup/09-extract-element-compartments.md +34 -0
- data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
- data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
- data/TODO.fixup/12-path-style-harmony.md +23 -0
- data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
- data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
- data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
- data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
- data/TODO.fixup/17-path-count-calibration.md +27 -0
- data/TODO.fixup/18-polygon-count-calibration.md +32 -0
- data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
- data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
- data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
- data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
- data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
- data/TODO.fixup/24-note-element-rendering.md +23 -0
- data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
- data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
- data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
- data/TODO.fixup/28-geometry-parser-identification.md +57 -0
- data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
- data/TODO.fixup/30-theme-system-stub.md +46 -0
- data/TODO.fixup/31-theme-value-object-registry.md +17 -0
- data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
- data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
- data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
- data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
- data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
- data/TODO.fixup/37-package-shape-rendering.md +22 -0
- data/TODO.fixup/38-note-element-rendering.md +15 -0
- data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
- data/TODO.fixup/40-connector-dashed-lines.md +15 -0
- data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
- data/TODO.fixup/42-textrenderer-centralization.md +10 -0
- data/TODO.fixup/43-colorresolver-unification.md +24 -0
- data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
- data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
- data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
- data/TODO.fixup/47-canvas-frame-inset.md +19 -0
- data/TODO.fixup/48-background-rect-format.md +15 -0
- data/TODO.fixup/49-all-samples-integration-test.md +17 -0
- data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
- data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
- data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
- data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
- data/TODO.fixup/54-diagram-theme-api.md +50 -0
- data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
- data/TODO.fixup/56-theme-yaml-files.md +65 -0
- data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
- data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
- data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
- data/TODO.fixup/60-cli-theme-option.md +22 -0
- data/TODO.fixup/61-theme-validation.md +28 -0
- data/TODO.fixup/62-theme-builder-dsl.md +31 -0
- data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
- data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
- data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
- data/TODO.fixup/66-styleex-writeback.md +22 -0
- data/TODO.fixup/67-delete-dead-code.md +28 -0
- data/TODO.fixup/68-figure-parity-status.md +82 -0
- data/TODO.fixup/69-package-polygon-rendering.md +54 -0
- data/TODO.fixup/70-tagged-values-compartment.md +39 -0
- data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
- data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
- data/TODO.fixup/73-delete-dead-transformations.md +14 -0
- data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
- data/TODO.fixup/75-visibility-legend-icons.md +23 -0
- data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
- data/TODO.fixup/77-canvas-frame-margins.md +28 -0
- data/TODO.fixup/78-note-element-rendering.md +30 -0
- data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
- data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
- data/TODO.fixup/81-figure-parity-current-state.md +63 -0
- data/config/qea_schema.yml +356 -0
- data/config/themes/119.yml +98 -0
- data/config/themes/default.yml +14 -0
- data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
- data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
- data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
- data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
- data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
- data/examples/exports/UmlModel_template/model.xml +23 -0
- data/examples/exports/basic/UML_EA.DTD +6856 -0
- data/examples/exports/basic/model.xml +8039 -0
- data/examples/exports/simple/UML_EA.DTD +6856 -0
- data/examples/exports/simple/model.xml +585 -0
- data/examples/exports/simple_example/UML_EA.DTD +6856 -0
- data/examples/exports/simple_example/model.xml +23 -0
- data/examples/exports/test/UML_EA.DTD +6856 -0
- data/examples/exports/test/model.xml +1638 -0
- data/examples/qea/basic.qea +0 -0
- data/frontend/README.md +51 -0
- data/frontend/dist/app.iife.js +24 -0
- data/frontend/dist/style.css +1 -0
- data/frontend/index.html +26 -0
- data/frontend/package-lock.json +2359 -0
- data/frontend/package.json +26 -0
- data/frontend/src/App.vue +59 -0
- data/frontend/src/app.ts +8 -0
- data/frontend/src/components/AppHeader.vue +127 -0
- data/frontend/src/components/AppSidebar.vue +119 -0
- data/frontend/src/components/ClassDetails.vue +247 -0
- data/frontend/src/components/DiagramView.vue +79 -0
- data/frontend/src/components/PackageDetails.vue +92 -0
- data/frontend/src/components/PackageTreeNode.vue +64 -0
- data/frontend/src/components/SearchResults.vue +48 -0
- data/frontend/src/components/WelcomeView.vue +87 -0
- data/frontend/src/shims-vue.d.ts +5 -0
- data/frontend/src/stores/dataStore.ts +79 -0
- data/frontend/src/stores/uiStore.ts +167 -0
- data/frontend/src/styles/main.css +1566 -0
- data/frontend/src/types.ts +221 -0
- data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
- data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
- data/frontend/tests/e2e/render.spec.ts +188 -0
- data/frontend/tsconfig.json +20 -0
- data/frontend/vite.config.ts +26 -0
- data/frontend/vitest.config.ts +8 -0
- data/lib/ea/cli/app.rb +57 -2
- data/lib/ea/cli/command/diagrams.rb +37 -28
- data/lib/ea/cli/command/diff.rb +46 -0
- data/lib/ea/cli/command/export.rb +101 -0
- data/lib/ea/cli/command/info.rb +75 -0
- data/lib/ea/cli/command/lint.rb +48 -0
- data/lib/ea/cli/command/mdg.rb +98 -0
- data/lib/ea/cli/command/query.rb +34 -0
- data/lib/ea/cli/command/render.rb +135 -0
- data/lib/ea/cli/command/svg.rb +8 -15
- data/lib/ea/cli/command.rb +7 -0
- data/lib/ea/diagram/display_config.rb +184 -0
- data/lib/ea/diagram.rb +4 -41
- data/lib/ea/diff/change.rb +30 -0
- data/lib/ea/diff/comparator.rb +109 -0
- data/lib/ea/diff/html_reporter.rb +91 -0
- data/lib/ea/diff.rb +15 -0
- data/lib/ea/export/json/generator.rb +118 -0
- data/lib/ea/export/json.rb +12 -0
- data/lib/ea/export/json_schema/generator.rb +77 -0
- data/lib/ea/export/json_schema.rb +12 -0
- data/lib/ea/export/plantuml/generator.rb +140 -0
- data/lib/ea/export/plantuml.rb +12 -0
- data/lib/ea/export/xsd/class_mapping.rb +65 -0
- data/lib/ea/export/xsd/generator.rb +137 -0
- data/lib/ea/export/xsd/namespace_registry.rb +82 -0
- data/lib/ea/export/xsd.rb +19 -0
- data/lib/ea/export.rb +13 -0
- data/lib/ea/image/emf_renderer.rb +42 -0
- data/lib/ea/image.rb +12 -0
- data/lib/ea/lint/engine.rb +30 -0
- data/lib/ea/lint/lint_rule.rb +38 -0
- data/lib/ea/lint/offense.rb +18 -0
- data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
- data/lib/ea/lint/rules/duplicate_name.rb +28 -0
- data/lib/ea/lint/rules/missing_stereotype.rb +40 -0
- data/lib/ea/lint/rules/naming_convention.rb +44 -0
- data/lib/ea/lint/rules/orphan_element.rb +28 -0
- data/lib/ea/lint/rules.rb +14 -0
- data/lib/ea/lint.rb +13 -0
- data/lib/ea/mdg/document.rb +120 -0
- data/lib/ea/mdg/loader.rb +118 -0
- data/lib/ea/mdg/registry.rb +98 -0
- data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
- data/lib/ea/mdg/xml/applies_to.rb +18 -0
- data/lib/ea/mdg/xml/apply.rb +18 -0
- data/lib/ea/mdg/xml/content.rb +18 -0
- data/lib/ea/mdg/xml/documentation.rb +24 -0
- data/lib/ea/mdg/xml/stereotype.rb +28 -0
- data/lib/ea/mdg/xml/stereotypes.rb +18 -0
- data/lib/ea/mdg/xml/tag.rb +28 -0
- data/lib/ea/mdg/xml/tagged_values.rb +18 -0
- data/lib/ea/mdg/xml/technology.rb +28 -0
- data/lib/ea/mdg/xml/uml_profile.rb +22 -0
- data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
- data/lib/ea/mdg/xml.rb +24 -0
- data/lib/ea/mdg.rb +25 -0
- data/lib/ea/model/classifier.rb +4 -0
- data/lib/ea/model/constraint.rb +24 -0
- data/lib/ea/model/diagram.rb +85 -0
- data/lib/ea/model/diagram_connector.rb +34 -0
- data/lib/ea/model/diagram_element.rb +16 -0
- data/lib/ea/model/document.rb +47 -2
- data/lib/ea/model/ghost_label.rb +32 -0
- data/lib/ea/model/instance_specification.rb +34 -0
- data/lib/ea/model/legend.rb +43 -0
- data/lib/ea/model/legend_item.rb +26 -0
- data/lib/ea/model/note.rb +28 -0
- data/lib/ea/model/operation.rb +2 -0
- data/lib/ea/model/property.rb +2 -0
- data/lib/ea/model/relationship.rb +2 -0
- data/lib/ea/model/signal.rb +14 -0
- data/lib/ea/model/slot.rb +19 -0
- data/lib/ea/model.rb +8 -0
- data/lib/ea/ocl/evaluator.rb +94 -0
- data/lib/ea/ocl/nodes.rb +30 -0
- data/lib/ea/ocl/parser.rb +133 -0
- data/lib/ea/ocl/unsupported_error.rb +7 -0
- data/lib/ea/ocl.rb +13 -0
- data/lib/ea/qea/factory/document_builder.rb +24 -10
- data/lib/ea/qea/factory/generalization_builder.rb +9 -11
- data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
- data/lib/ea/qea/models/ea_author.rb +35 -0
- data/lib/ea/qea/models/ea_glossary.rb +34 -0
- data/lib/ea/qea/models/ea_image.rb +53 -0
- data/lib/ea/qea/models/ea_implement.rb +32 -0
- data/lib/ea/qea/models/ea_list.rb +42 -0
- data/lib/ea/qea/models/ea_object_effort.rb +34 -0
- data/lib/ea/qea/models/ea_object_problem.rb +37 -0
- data/lib/ea/qea/models/ea_object_require.rb +34 -0
- data/lib/ea/qea/models/ea_object_resource.rb +34 -0
- data/lib/ea/qea/models/ea_object_risk.rb +34 -0
- data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
- data/lib/ea/qea/models/ea_object_test.rb +35 -0
- data/lib/ea/qea/models/ea_object_trx.rb +34 -0
- data/lib/ea/qea/models/ea_operation.rb +7 -0
- data/lib/ea/qea/models/ea_palette.rb +30 -0
- data/lib/ea/qea/models/ea_palette_item.rb +36 -0
- data/lib/ea/qea/models/ea_phase.rb +40 -0
- data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
- data/lib/ea/qea/models/ea_secrypt.rb +34 -0
- data/lib/ea/qea/models/ea_version.rb +39 -0
- data/lib/ea/qea/models/ea_xref.rb +11 -1
- data/lib/ea/qea/models.rb +19 -0
- data/lib/ea/qea/services/database_loader.rb +22 -1
- data/lib/ea/qea/validation/database/ocl_constraint_validator.rb +65 -0
- data/lib/ea/qea/validation/database.rb +2 -0
- data/lib/ea/qea/validation/validation_engine.rb +2 -0
- data/lib/ea/qea/validation.rb +2 -0
- data/lib/ea/query/builder.rb +127 -0
- data/lib/ea/query.rb +9 -0
- data/lib/ea/render/image_converter.rb +101 -0
- data/lib/ea/render.rb +10 -0
- data/lib/ea/shapescript/parser.rb +208 -0
- data/lib/ea/shapescript/renderer.rb +74 -0
- data/lib/ea/shapescript/shape.rb +34 -0
- data/lib/ea/shapescript.rb +24 -0
- data/lib/ea/sources/qea/adapter.rb +22 -5
- data/lib/ea/sources/qea/classifier_builder.rb +184 -7
- data/lib/ea/sources/qea/diagram_builder.rb +352 -24
- data/lib/ea/sources/qea/id_normalizer.rb +16 -1
- data/lib/ea/sources/qea/instance_builder.rb +113 -0
- data/lib/ea/sources/qea/legend_builder.rb +137 -0
- data/lib/ea/sources/qea/note_builder.rb +58 -0
- data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
- data/lib/ea/sources/qea/operation_builder.rb +1 -0
- data/lib/ea/sources/qea/package_builder.rb +16 -0
- data/lib/ea/sources/qea/relationship_builder.rb +21 -5
- data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
- data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
- data/lib/ea/sources/qea/xref/parser.rb +150 -0
- data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
- data/lib/ea/sources/qea/xref/record.rb +41 -0
- data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
- data/lib/ea/sources/qea/xref.rb +27 -0
- data/lib/ea/sources/qea.rb +4 -0
- data/lib/ea/sources/xmi/adapter.rb +80 -2
- data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
- data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
- data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
- data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
- data/lib/ea/sources/xmi/note_builder.rb +46 -0
- data/lib/ea/sources/xmi/property_builder.rb +9 -0
- data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
- data/lib/ea/sources/xmi/tag_builder.rb +45 -0
- data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
- data/lib/ea/sources/xmi.rb +4 -0
- data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
- data/lib/ea/spa/output/single_file_strategy.rb +8 -2
- data/lib/ea/spa/output/strategy.rb +34 -0
- data/lib/ea/svg/connector_router.rb +134 -0
- data/lib/ea/svg/ea_emitter/background.rb +2 -3
- data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
- data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
- data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
- data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
- data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
- data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
- data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
- data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
- data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
- data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
- data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
- data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
- data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
- data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
- data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
- data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
- data/lib/ea/svg/ea_emitter/compartment/stereotype_icon.rb +32 -0
- data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
- data/lib/ea/svg/ea_emitter/compartment.rb +60 -0
- data/lib/ea/svg/ea_emitter/connectors.rb +46 -12
- data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
- data/lib/ea/svg/ea_emitter/document.rb +49 -25
- data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
- data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
- data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
- data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
- data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
- data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
- data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
- data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
- data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
- data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
- data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
- data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
- data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
- data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
- data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
- data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
- data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
- data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
- data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
- data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
- data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +119 -0
- data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
- data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
- data/lib/ea/svg/ea_emitter/element.rb +37 -0
- data/lib/ea/svg/ea_emitter/elements.rb +283 -95
- data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
- data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
- data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
- data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
- data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
- data/lib/ea/svg/ea_emitter/label.rb +23 -0
- data/lib/ea/svg/ea_emitter/labels.rb +93 -27
- data/lib/ea/svg/ea_emitter/layer.rb +20 -0
- data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
- data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
- data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
- data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
- data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
- data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
- data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
- data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
- data/lib/ea/svg/ea_emitter/marker.rb +37 -0
- data/lib/ea/svg/ea_emitter/markers.rb +351 -64
- data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
- data/lib/ea/svg/ea_emitter/style.rb +15 -0
- data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
- data/lib/ea/svg/ea_emitter.rb +33 -11
- data/lib/ea/svg/parity/checker.rb +105 -0
- data/lib/ea/svg/parity/source.rb +59 -0
- data/lib/ea/svg/parity/suite.rb +113 -0
- data/lib/ea/svg/parity.rb +14 -0
- data/lib/ea/svg.rb +2 -15
- data/lib/ea/theme/definition.rb +176 -0
- data/lib/ea/theme/loader.rb +125 -0
- data/lib/ea/theme/registry.rb +96 -0
- data/lib/ea/theme.rb +37 -0
- data/lib/ea/transformations.rb +5 -88
- data/lib/ea/validation/xmi_parity.rb +80 -0
- data/lib/ea/validation.rb +12 -0
- data/lib/ea/version.rb +4 -1
- data/lib/ea.rb +15 -4
- data/lib/tasks/svg_bench.rake +87 -0
- data/lutaml-uml-0.5.1.gem +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
- data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
- metadata +464 -26
- data/lib/ea/diagram/configuration.rb +0 -379
- data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
- data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
- data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
- data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
- data/lib/ea/diagram/element_renderers.rb +0 -43
- data/lib/ea/diagram/extractor.rb +0 -566
- data/lib/ea/diagram/layout_engine.rb +0 -170
- data/lib/ea/diagram/path_builder.rb +0 -202
- data/lib/ea/diagram/style_parser.rb +0 -42
- data/lib/ea/diagram/style_resolver.rb +0 -291
- data/lib/ea/diagram/svg_renderer.rb +0 -274
- data/lib/ea/diagram/util.rb +0 -73
- data/lib/ea/svg/bounds_calculator.rb +0 -74
- data/lib/ea/svg/connector_path.rb +0 -138
- data/lib/ea/svg/element_box.rb +0 -256
- data/lib/ea/svg/renderer.rb +0 -77
- data/lib/ea/svg/style_resolver.rb +0 -72
- data/lib/ea/transformations/configuration.rb +0 -333
- data/lib/ea/transformations/format_registry.rb +0 -366
- data/lib/ea/transformations/parsers/base_parser.rb +0 -482
- data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
- data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
- data/lib/ea/transformations/transformation_engine.rb +0 -390
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Qea
|
|
5
|
+
module Validation
|
|
6
|
+
# Evaluates OCL invariants stored in t_objectconstraint.
|
|
7
|
+
# For each constraint, parse its OCL expression via
|
|
8
|
+
# Ea::Ocl::Parser and evaluate it against the owning
|
|
9
|
+
# object's attribute values. Failures surface as :warning
|
|
10
|
+
# messages — OCL failures aren't structural errors.
|
|
11
|
+
class OclConstraintValidator < BaseValidator
|
|
12
|
+
def validate
|
|
13
|
+
constraints = database.collections[:object_constraints] || []
|
|
14
|
+
constraints.each { |c| check_constraint(c) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def check_constraint(constraint)
|
|
20
|
+
owner = owner_for(constraint)
|
|
21
|
+
return unless owner
|
|
22
|
+
|
|
23
|
+
ast = parse(constraint.constraint)
|
|
24
|
+
return unless ast
|
|
25
|
+
|
|
26
|
+
if evaluate(ast, owner)
|
|
27
|
+
result.add(:info, "OCL constraint '#{constraint.constraint_type}' " \
|
|
28
|
+
"passed for #{owner.name}",
|
|
29
|
+
entity_name: owner.name)
|
|
30
|
+
else
|
|
31
|
+
result.add(:warning, "OCL constraint '#{constraint.constraint_type}' " \
|
|
32
|
+
"failed for #{owner.name}: #{constraint.constraint}",
|
|
33
|
+
entity_name: owner.name)
|
|
34
|
+
end
|
|
35
|
+
rescue Ea::Ocl::UnsupportedError => e
|
|
36
|
+
result.add(:info, "Skipping OCL constraint on #{owner&.name} " \
|
|
37
|
+
"(unsupported syntax): #{e.message}",
|
|
38
|
+
entity_name: owner&.name)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Parse the OCL body. Returns nil for empty / non-invariant
|
|
42
|
+
# constraints (we don't try to evaluate non-invariant forms).
|
|
43
|
+
def parse(body)
|
|
44
|
+
return nil if body.nil? || body.empty?
|
|
45
|
+
|
|
46
|
+
Ea::Ocl::Parser.parse(body)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def evaluate(ast, owner)
|
|
50
|
+
Ea::Ocl::Evaluator.evaluate(ast, owner)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def owner_for(constraint)
|
|
54
|
+
object_id = constraint.respond_to?(:ea_object_id) ?
|
|
55
|
+
constraint.ea_object_id :
|
|
56
|
+
constraint.object_id
|
|
57
|
+
return nil unless object_id
|
|
58
|
+
|
|
59
|
+
(database.collections[:objects] || [])
|
|
60
|
+
.find { |o| o.object_id == object_id }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -10,6 +10,8 @@ module Ea
|
|
|
10
10
|
"ea/qea/validation/database/orphan_validator"
|
|
11
11
|
autoload :ReferentialIntegrityValidator,
|
|
12
12
|
"ea/qea/validation/database/referential_integrity_validator"
|
|
13
|
+
autoload :OclConstraintValidator,
|
|
14
|
+
"ea/qea/validation/database/ocl_constraint_validator"
|
|
13
15
|
end
|
|
14
16
|
end
|
|
15
17
|
end
|
|
@@ -83,6 +83,7 @@ module Ea
|
|
|
83
83
|
orphan
|
|
84
84
|
circular_reference
|
|
85
85
|
package
|
|
86
|
+
ocl_constraint
|
|
86
87
|
]
|
|
87
88
|
|
|
88
89
|
validator_names = if validators
|
|
@@ -213,6 +214,7 @@ module Ea
|
|
|
213
214
|
@registry.register(:circular_reference,
|
|
214
215
|
CircularReferenceValidator)
|
|
215
216
|
@registry.register(:package, PackageValidator)
|
|
217
|
+
@registry.register(:ocl_constraint, OclConstraintValidator)
|
|
216
218
|
|
|
217
219
|
# Phase 2: UML Tree Structure Validators
|
|
218
220
|
# DocumentStructureValidator lives in lutaml-uml and may not be
|
data/lib/ea/qea/validation.rb
CHANGED
|
@@ -20,6 +20,8 @@ module Ea
|
|
|
20
20
|
"ea/qea/validation/database/orphan_validator"
|
|
21
21
|
autoload :CircularReferenceValidator,
|
|
22
22
|
"ea/qea/validation/database/circular_reference_validator"
|
|
23
|
+
autoload :OclConstraintValidator,
|
|
24
|
+
"ea/qea/validation/database/ocl_constraint_validator"
|
|
23
25
|
autoload :Database, "ea/qea/validation/database"
|
|
24
26
|
autoload :Formatters, "ea/qea/validation/formatters"
|
|
25
27
|
autoload :ValidationEngine, "ea/qea/validation/validation_engine"
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Query
|
|
5
|
+
# Chainable query builder. Each method returns a new Builder
|
|
6
|
+
# with the predicate applied — no mutation. Final #call returns
|
|
7
|
+
# the filtered collection.
|
|
8
|
+
#
|
|
9
|
+
# Example:
|
|
10
|
+
# Ea::Query.new(model).classes.in_package("core")
|
|
11
|
+
# .with_stereotype("FeatureType").call
|
|
12
|
+
class Builder
|
|
13
|
+
attr_reader :model, :filters
|
|
14
|
+
|
|
15
|
+
def initialize(model, filters: [], kind: :objects)
|
|
16
|
+
@model = model
|
|
17
|
+
@filters = filters
|
|
18
|
+
@kind = kind
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Filter to UML classes only.
|
|
22
|
+
# @return [Builder]
|
|
23
|
+
def classes
|
|
24
|
+
with_type("Class")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Filter to interfaces only.
|
|
28
|
+
# @return [Builder]
|
|
29
|
+
def interfaces
|
|
30
|
+
with_type("Interface")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Filter to packages.
|
|
34
|
+
# @return [Builder]
|
|
35
|
+
def packages
|
|
36
|
+
Builder.new(model, filters: filters, kind: :packages)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Filter to diagrams.
|
|
40
|
+
# @return [Builder]
|
|
41
|
+
def diagrams
|
|
42
|
+
Builder.new(model, filters: filters, kind: :diagrams)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Filter by EA object_type (Class, Interface, Package, ...).
|
|
46
|
+
# @return [Builder]
|
|
47
|
+
def with_type(type)
|
|
48
|
+
Builder.new(model, filters: filters + [->(o) { o.object_type == type }], kind: @kind)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Restrict to elements in the named package.
|
|
52
|
+
# @param name [String]
|
|
53
|
+
# @return [Builder]
|
|
54
|
+
def in_package(name)
|
|
55
|
+
pkg = (model.collections[:packages] || []).find { |p| p.name == name }
|
|
56
|
+
return self unless pkg
|
|
57
|
+
|
|
58
|
+
filter_by { |o| o.package_id == pkg.package_id }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Filter by applied stereotype name. Stereotype lookup walks
|
|
62
|
+
# t_xref for @STEREO blocks referencing the object's ea_guid.
|
|
63
|
+
# @param name [String]
|
|
64
|
+
# @return [Builder]
|
|
65
|
+
def with_stereotype(name)
|
|
66
|
+
xrefs = model.collections[:xrefs] || []
|
|
67
|
+
filter_by do |o|
|
|
68
|
+
applied = stereotype_names_for(o, xrefs)
|
|
69
|
+
applied.any? { |r| r == name }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Filter by name exact match.
|
|
74
|
+
# @param name [String]
|
|
75
|
+
# @return [Builder]
|
|
76
|
+
def named(name)
|
|
77
|
+
filter_by { |o| o.name == name }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Filter by name substring (case-insensitive).
|
|
81
|
+
# @param substring [String]
|
|
82
|
+
# @return [Builder]
|
|
83
|
+
def name_contains(substring)
|
|
84
|
+
re = Regexp.new(Regexp.escape(substring), Regexp::IGNORECASE)
|
|
85
|
+
filter_by { |o| o.name&.match?(re) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Add a custom filter proc.
|
|
89
|
+
# @param block [Proc(Object)->Boolean]
|
|
90
|
+
# @return [Builder]
|
|
91
|
+
def filter_by(&block)
|
|
92
|
+
Builder.new(model, filters: filters + [block], kind: @kind)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Extract stereotype names applied to an object via t_xref.
|
|
96
|
+
# Returns [] when no @STEREO block references the object's GUID.
|
|
97
|
+
# @param object [Ea::Qea::Models::EaObject]
|
|
98
|
+
# @param xrefs [Array<Ea::Qea::Models::EaXref>]
|
|
99
|
+
# @return [Array<String>]
|
|
100
|
+
def stereotype_names_for(object, xrefs)
|
|
101
|
+
return [] unless object.is_a?(Ea::Qea::Models::EaObject)
|
|
102
|
+
return [] unless object.ea_guid
|
|
103
|
+
|
|
104
|
+
xrefs.select do |xr|
|
|
105
|
+
xr.client == object.ea_guid && xr.description&.include?("@STEREO")
|
|
106
|
+
end.map do |xr|
|
|
107
|
+
xr.description.match(/Name=([^;]+)/)[1]
|
|
108
|
+
end.compact
|
|
109
|
+
end
|
|
110
|
+
private :stereotype_names_for
|
|
111
|
+
|
|
112
|
+
# Materialize the filtered collection.
|
|
113
|
+
# @return [Array]
|
|
114
|
+
def call
|
|
115
|
+
records = model.collections[@kind] || []
|
|
116
|
+
records.select { |o| filters.all? { |f| f.call(o) } }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Enumerable support.
|
|
120
|
+
include Enumerable
|
|
121
|
+
|
|
122
|
+
def each(&block)
|
|
123
|
+
call.each(&block)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
data/lib/ea/query.rb
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Render
|
|
5
|
+
# Converts SVG strings to PNG and PDF using available system
|
|
6
|
+
# tools. Detects `rsvg-convert`, ImageMagick `convert`, and
|
|
7
|
+
# headless Chrome (`chromium --headless`). Raises a clear error
|
|
8
|
+
# when no converter is available.
|
|
9
|
+
class ImageConverter
|
|
10
|
+
attr_reader :svg
|
|
11
|
+
|
|
12
|
+
# @param svg [String] SVG markup
|
|
13
|
+
def initialize(svg)
|
|
14
|
+
@svg = svg
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @return [String] PNG bytes
|
|
18
|
+
def to_png
|
|
19
|
+
with_tempfile(".svg") do |svg_path|
|
|
20
|
+
File.write(svg_path, svg)
|
|
21
|
+
convert(svg_path, :png)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @return [String] PDF bytes
|
|
26
|
+
def to_pdf
|
|
27
|
+
with_tempfile(".svg") do |svg_path|
|
|
28
|
+
File.write(svg_path, svg)
|
|
29
|
+
convert(svg_path, :pdf)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns the first available converter executable, or nil.
|
|
34
|
+
# @return [Symbol, nil] :rsvg_convert | :magick | :chrome
|
|
35
|
+
def self.available_converter
|
|
36
|
+
return :rsvg_convert if system("which rsvg-convert > /dev/null 2>&1")
|
|
37
|
+
return :magick if system("which convert > /dev/null 2>&1")
|
|
38
|
+
return :chrome if chromium_executable
|
|
39
|
+
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.chromium_executable
|
|
44
|
+
%w[chromium chromium-browser google-chrome
|
|
45
|
+
/Applications/Chromium.app/Contents/MacOS/Chromium
|
|
46
|
+
/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome]
|
|
47
|
+
.find { |exe| File.executable?(exe) || system("which #{exe} > /dev/null 2>&1") }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def convert(svg_path, target)
|
|
53
|
+
converter = self.class.available_converter
|
|
54
|
+
raise Ea::Cli::Error,
|
|
55
|
+
"No SVG→#{target.upcase} converter available. " \
|
|
56
|
+
"Install rsvg-convert (librsvg) or ImageMagick." unless converter
|
|
57
|
+
|
|
58
|
+
with_tempfile(".#{target}") do |out_path|
|
|
59
|
+
run_converter(converter, svg_path, out_path, target)
|
|
60
|
+
File.binread(out_path)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def run_converter(name, svg_path, out_path, target)
|
|
65
|
+
case name
|
|
66
|
+
when :rsvg_convert
|
|
67
|
+
format_flag = target == :pdf ? "-f pdf" : "-f png"
|
|
68
|
+
system("rsvg-convert #{format_flag} -o #{shell_quote(out_path)} " \
|
|
69
|
+
"#{shell_quote(svg_path)}",
|
|
70
|
+
exception: true)
|
|
71
|
+
when :magick
|
|
72
|
+
system("convert #{shell_quote(svg_path)} #{shell_quote(out_path)}",
|
|
73
|
+
exception: true)
|
|
74
|
+
when :chrome
|
|
75
|
+
chrome = self.class.chromium_executable
|
|
76
|
+
system("#{shell_quote(chrome)} --headless --disable-gpu " \
|
|
77
|
+
"--print-to-pdf=#{shell_quote(out_path)} " \
|
|
78
|
+
"file://#{svg_path}",
|
|
79
|
+
exception: true)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Quote a path for safe inclusion in a shell command. Only
|
|
84
|
+
# handles paths without single quotes (sufficient for tempfile
|
|
85
|
+
# paths which are numeric IDs under /tmp).
|
|
86
|
+
def shell_quote(path)
|
|
87
|
+
"'#{path}'"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def with_tempfile(suffix)
|
|
91
|
+
require "tmpdir"
|
|
92
|
+
Dir.mktmpdir("ea-render-") do |dir|
|
|
93
|
+
path = File.join(dir, "input#{suffix}")
|
|
94
|
+
yield path
|
|
95
|
+
ensure
|
|
96
|
+
File.delete(path) if File.exist?(path)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
data/lib/ea/render.rb
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
# Conversion of SVG strings to raster/vector formats.
|
|
5
|
+
# Delegates to external tools (rsvg-convert, headless Chrome) when
|
|
6
|
+
# available; raises a clear error otherwise.
|
|
7
|
+
module Render
|
|
8
|
+
autoload :ImageConverter, "ea/render/image_converter"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Shapescript
|
|
5
|
+
# Tokenizer + parser for ShapeScript. Returns a list of Shape
|
|
6
|
+
# primitives extracted from a single shape body.
|
|
7
|
+
#
|
|
8
|
+
# Supported grammar (extended):
|
|
9
|
+
#
|
|
10
|
+
# script := statement*
|
|
11
|
+
# statement := shape | var_decl | primitive | if_stmt | label
|
|
12
|
+
# shape := "shape" identifier "{" statement* "}"
|
|
13
|
+
# var_decl := "var" identifier "=" expr ";"
|
|
14
|
+
# primitive := call "(" args ")" ";"
|
|
15
|
+
# call := "rectangle" | "ellipse" | "polygon" |
|
|
16
|
+
# "line" | "path" | "label"
|
|
17
|
+
# args := expr ("," expr)*
|
|
18
|
+
# if_stmt := "if" "(" expr ")" "{" statement* "}"
|
|
19
|
+
# ("else" "{" statement* "}")?
|
|
20
|
+
# expr := identifier | number | string | binop
|
|
21
|
+
#
|
|
22
|
+
# Variables are resolved at parse time when possible; otherwise
|
|
23
|
+
# the identifier is left as a string for late binding.
|
|
24
|
+
module Parser
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
# @param source [String] ShapeScript source
|
|
28
|
+
# @return [Array<Shape>] flat list of all primitives across
|
|
29
|
+
# all shapes in the source. Shape nesting is flattened.
|
|
30
|
+
# Variables, conditionals, and labels are resolved/inlined
|
|
31
|
+
# where possible; unresolvable expressions are skipped.
|
|
32
|
+
def parse(source)
|
|
33
|
+
return [] if source.nil? || source.empty?
|
|
34
|
+
|
|
35
|
+
tokens = tokenize(source)
|
|
36
|
+
env = {}
|
|
37
|
+
shapes = []
|
|
38
|
+
consume_statements(tokens, env, shapes)
|
|
39
|
+
shapes
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# -- Tokenizer ---------------------------------------------------
|
|
43
|
+
|
|
44
|
+
def tokenize(source)
|
|
45
|
+
clean = source.gsub(%r{//[^\n]*}, "")
|
|
46
|
+
.gsub(/\/\*.*?\*\//m, "")
|
|
47
|
+
clean.scan(/[A-Za-z_][A-Za-z0-9_]*|-?\d+\.?\d*|"[^"]*"|'[^']*'|[{}(),;=+\-*\/]/)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Consume statements until matching close brace or end.
|
|
51
|
+
def consume_statements(tokens, env, shapes, top_level: true)
|
|
52
|
+
until tokens.empty?
|
|
53
|
+
tok = tokens.first
|
|
54
|
+
if tok == "}"
|
|
55
|
+
tokens.shift if top_level == false
|
|
56
|
+
break
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
tokens.shift # consume tok
|
|
60
|
+
case tok
|
|
61
|
+
when "shape"
|
|
62
|
+
_name = tokens.shift
|
|
63
|
+
_open = tokens.shift # "{"
|
|
64
|
+
consume_statements(tokens, env.dup, shapes, top_level: false)
|
|
65
|
+
when "var"
|
|
66
|
+
consume_var_decl(tokens, env)
|
|
67
|
+
when "if"
|
|
68
|
+
consume_if(tokens, env, shapes)
|
|
69
|
+
when "label"
|
|
70
|
+
shapes << consume_label(tokens, env)
|
|
71
|
+
else
|
|
72
|
+
shapes << consume_primitive(tok, tokens, env) if PRIMITIVES.key?(tok)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
PRIMITIVES = {
|
|
78
|
+
"rectangle" => :rectangle,
|
|
79
|
+
"ellipse" => :ellipse,
|
|
80
|
+
"polygon" => :polygon,
|
|
81
|
+
"line" => :line,
|
|
82
|
+
"path" => :path
|
|
83
|
+
}.freeze
|
|
84
|
+
|
|
85
|
+
def consume_primitive(name, tokens, env)
|
|
86
|
+
kind = PRIMITIVES[name]
|
|
87
|
+
open_paren = tokens.shift # "("
|
|
88
|
+
params = []
|
|
89
|
+
if open_paren == "("
|
|
90
|
+
until tokens.empty?
|
|
91
|
+
tok = tokens.shift
|
|
92
|
+
break if tok == ")"
|
|
93
|
+
|
|
94
|
+
value = resolve_expr(tok, tokens, env)
|
|
95
|
+
params << value if value.is_a?(Numeric)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
consume_semicolon(tokens)
|
|
99
|
+
Shape.new(kind: kind, params: params)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def consume_label(tokens, env)
|
|
103
|
+
tokens.shift # "("
|
|
104
|
+
text = tokens.shift
|
|
105
|
+
text = text.to_s.gsub(/\A["']|["']\z/, "")
|
|
106
|
+
tokens.shift until tokens.empty? || tokens.first == ")"
|
|
107
|
+
tokens.shift # ")"
|
|
108
|
+
consume_semicolon(tokens)
|
|
109
|
+
Shape.new(kind: :label, params: [text])
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def consume_var_decl(tokens, env)
|
|
113
|
+
name = tokens.shift
|
|
114
|
+
eq = tokens.shift # "="
|
|
115
|
+
value = resolve_expr(tokens.shift, tokens, env)
|
|
116
|
+
consume_semicolon(tokens)
|
|
117
|
+
env[name] = value if value.is_a?(Numeric) || value.is_a?(String)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def consume_if(tokens, env, then_shapes)
|
|
121
|
+
tokens.shift # "("
|
|
122
|
+
condition = resolve_expr(tokens.shift, tokens, env)
|
|
123
|
+
tokens.shift until tokens.empty? || tokens.first == ")"
|
|
124
|
+
tokens.shift # ")"
|
|
125
|
+
tokens.shift # "{"
|
|
126
|
+
|
|
127
|
+
# Evaluate condition; if true, consume then-block into shapes.
|
|
128
|
+
# If false, skip then-block and consume optional else.
|
|
129
|
+
truthy = condition == true || (condition.is_a?(Numeric) && condition.nonzero?)
|
|
130
|
+
if truthy
|
|
131
|
+
consume_statements(tokens, env, then_shapes, top_level: false)
|
|
132
|
+
else
|
|
133
|
+
skip_block(tokens)
|
|
134
|
+
if tokens.first == "else"
|
|
135
|
+
tokens.shift
|
|
136
|
+
tokens.shift # "{"
|
|
137
|
+
consume_statements(tokens, env, then_shapes, top_level: false)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def skip_block(tokens)
|
|
143
|
+
tokens.shift # "{"
|
|
144
|
+
depth = 1
|
|
145
|
+
until tokens.empty? || depth.zero?
|
|
146
|
+
t = tokens.shift
|
|
147
|
+
depth += 1 if t == "{"
|
|
148
|
+
depth -= 1 if t == "}"
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def consume_semicolon(tokens)
|
|
153
|
+
tokens.shift if tokens.first == ";"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Resolve an expression to a value. Returns Numeric/String/Boolean
|
|
157
|
+
# or nil when unresolvable. Handles simple binary ops (+, -, *, /).
|
|
158
|
+
def resolve_expr(tok, tokens, env)
|
|
159
|
+
return nil unless tok
|
|
160
|
+
|
|
161
|
+
# Parenthesized sub-expression
|
|
162
|
+
if tok == "("
|
|
163
|
+
value = resolve_expr(tokens.shift, tokens, env)
|
|
164
|
+
tokens.shift if tokens.first == ")"
|
|
165
|
+
return value
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Numeric literal
|
|
169
|
+
return tok.to_i if tok.match?(/\A-?\d+\z/)
|
|
170
|
+
return tok.to_f if tok.match?(/\A-?\d+\.\d+\z/)
|
|
171
|
+
|
|
172
|
+
# String literal
|
|
173
|
+
return tok[1..-2] if tok.match?(/\A["'].*["']\z/)
|
|
174
|
+
|
|
175
|
+
# Boolean literals
|
|
176
|
+
return true if tok == "true"
|
|
177
|
+
return false if tok == "false"
|
|
178
|
+
|
|
179
|
+
# Variable reference
|
|
180
|
+
if env.key?(tok)
|
|
181
|
+
value = env[tok]
|
|
182
|
+
# Look ahead for binary op
|
|
183
|
+
if %w[+ - * /].include?(tokens.first)
|
|
184
|
+
op = tokens.shift
|
|
185
|
+
rhs = resolve_expr(tokens.shift, tokens, env)
|
|
186
|
+
return apply_binop(op, value, rhs)
|
|
187
|
+
end
|
|
188
|
+
return value
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Bare identifier (function call? hasproperty? — leave as nil
|
|
192
|
+
# for now; treat as false in conditionals).
|
|
193
|
+
nil
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def apply_binop(op, left, right)
|
|
197
|
+
return nil unless left.is_a?(Numeric) && right.is_a?(Numeric)
|
|
198
|
+
|
|
199
|
+
case op
|
|
200
|
+
when "+" then left + right
|
|
201
|
+
when "-" then left - right
|
|
202
|
+
when "*" then left * right
|
|
203
|
+
when "/" then left / right
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Shapescript
|
|
5
|
+
# Renders a list of parsed Shape primitives to SVG elements as
|
|
6
|
+
# a single string. Coordinates are emitted as-is — callers
|
|
7
|
+
# should wrap in a `<g>` with the appropriate transform if they
|
|
8
|
+
# want to position the icon.
|
|
9
|
+
module Renderer
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
# @param shapes [Array<Shape>]
|
|
13
|
+
# @param fill [String] SVG fill color
|
|
14
|
+
# @param stroke [String] SVG stroke color
|
|
15
|
+
# @return [String] SVG fragment with one element per shape
|
|
16
|
+
def render(shapes, fill: "#FFFFFF", stroke: "#000000")
|
|
17
|
+
return "" if shapes.nil? || shapes.empty?
|
|
18
|
+
|
|
19
|
+
shapes.map { |s| render_one(s, fill: fill, stroke: stroke) }.join("\n")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render_one(shape, fill:, stroke:)
|
|
23
|
+
case shape.kind
|
|
24
|
+
when :rectangle then render_rect(shape.params, fill, stroke)
|
|
25
|
+
when :ellipse then render_ellipse(shape.params, fill, stroke)
|
|
26
|
+
when :polygon then render_polygon(shape.params, fill, stroke)
|
|
27
|
+
when :line then render_line(shape.params, stroke)
|
|
28
|
+
when :path then render_path(shape.params, stroke)
|
|
29
|
+
when :label then render_label(shape.params, fill)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def render_label(p, fill)
|
|
34
|
+
text = p.first.to_s
|
|
35
|
+
%(<text x="0" y="0" fill="#{fill}">#{escape(text)}</text>)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def escape(text)
|
|
39
|
+
text.to_s.gsub("&", "&")
|
|
40
|
+
.gsub("<", "<")
|
|
41
|
+
.gsub(">", ">")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def render_rect(p, fill, stroke)
|
|
45
|
+
x, y, w, h = p
|
|
46
|
+
%(<rect x="#{x}" y="#{y}" width="#{w}" height="#{h}" fill="#{fill}" stroke="#{stroke}"/>)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def render_ellipse(p, fill, stroke)
|
|
50
|
+
cx, cy, rx, ry = p
|
|
51
|
+
%(<ellipse cx="#{cx}" cy="#{cy}" rx="#{rx}" ry="#{ry}" fill="#{fill}" stroke="#{stroke}"/>)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def render_polygon(p, fill, stroke)
|
|
55
|
+
points = p.each_slice(2).map { |x, y| "#{x},#{y}" }.join(" ")
|
|
56
|
+
%(<polygon points="#{points}" fill="#{fill}" stroke="#{stroke}"/>)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def render_line(p, stroke)
|
|
60
|
+
x1, y1, x2, y2 = p
|
|
61
|
+
%(<line x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}" stroke="#{stroke}"/>)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def render_path(p, stroke)
|
|
65
|
+
return "" if p.empty?
|
|
66
|
+
|
|
67
|
+
x, y = p.shift(2)
|
|
68
|
+
d = "M #{x} #{y}"
|
|
69
|
+
p.each_slice(2) { |px, py| d += " L #{px} #{py}" }
|
|
70
|
+
%(<path d="#{d}" fill="none" stroke="#{stroke}"/>)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Shapescript
|
|
5
|
+
# One rendered shape primitive. Carries type + params
|
|
6
|
+
# ready for SVG emission.
|
|
7
|
+
Shape = Struct.new(:kind, :params, keyword_init: true) do
|
|
8
|
+
# @return [Boolean] true when this shape is a rectangle
|
|
9
|
+
def rectangle?
|
|
10
|
+
kind == :rectangle
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ellipse?
|
|
14
|
+
kind == :ellipse
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def polygon?
|
|
18
|
+
kind == :polygon
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def line?
|
|
22
|
+
kind == :line
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def path?
|
|
26
|
+
kind == :path
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def label?
|
|
30
|
+
kind == :label
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
# ShapeScript interpreter namespace.
|
|
5
|
+
#
|
|
6
|
+
# EA's MDG technologies define stereotype icon decorations via a
|
|
7
|
+
# domain-specific language called ShapeScript. This is a basic
|
|
8
|
+
# interpreter supporting the core shape primitives:
|
|
9
|
+
#
|
|
10
|
+
# shape <Name> { ... }
|
|
11
|
+
# rectangle(left, top, right, bottom);
|
|
12
|
+
# ellipse(left, top, right, bottom);
|
|
13
|
+
# polygon(x1, y1, x2, y2, ..., xN, yN);
|
|
14
|
+
# line(x1, y1, x2, y2);
|
|
15
|
+
# path(x1, y1, x2, y2, ...);
|
|
16
|
+
#
|
|
17
|
+
# Compound features (conditionals, variables, color references)
|
|
18
|
+
# are out of scope for this basic interpreter.
|
|
19
|
+
module Shapescript
|
|
20
|
+
autoload :Parser, "ea/shapescript/parser"
|
|
21
|
+
autoload :Renderer, "ea/shapescript/renderer"
|
|
22
|
+
autoload :Shape, "ea/shapescript/shape"
|
|
23
|
+
end
|
|
24
|
+
end
|