diff --git a/README.md b/README.md index cababc6..580198c 100644 --- a/README.md +++ b/README.md @@ -78,9 +78,10 @@ immutable Moose 13 release. smalltalkCI selects it with `Moose64-13`. | `Tests` | Core, importer tests and PgMetadata scenario-test support | | `Generator` | Core and the metamodel generator | | `GeneratorTests` | Generator plus its global-regeneration selection regression test | -| `LegacyUI` | Historical GT Inspector, analysis, Telescope and connection-manager packages | +| `MooseIDE` | Optional standard Moose tool adapters and Roassal 3 SQL views (requires Moose 13) | +| `MooseIDETests` | Optional tool and visualization regression tests | -`LegacyUI` has not been ported or validated on the current image. Core usage does +The unsupported `LegacyUI` load group is removed for v3. Historical sources remain archived in `src`; use the [Roassal 3 SQL views](docs/mooseide-integration.md) instead. Core usage does not require those UI packages or a Genie MCP server. Loading modern Genie in a historical Pharo 7 image is not part of the installation procedure. diff --git a/docs/mooseide-integration.md b/docs/mooseide-integration.md index 84917fe..53d25fb 100644 --- a/docs/mooseide-integration.md +++ b/docs/mooseide-integration.md @@ -22,3 +22,17 @@ fork-first v3 development work, not a published v3 release. GitHub CI loads these optional tests in the ready-made Moose image. The plain Pharo job continues to test Core without GUI dependencies. + +## Roassal 3 SQL views + +The builders return unopened `RSCanvas` objects. Use any imported SQL model; there is no global `mi` dependency or implicit schema filter. For large databases pass a deliberately selected model, since all matching entities are drawn. + +```smalltalk +(FmxSQLRoassalViews schemaTablesIn: model) openWithTitle: 'Schemas and tables'. +(FmxSQLRoassalViews foreignKeysIn: model) openWithTitle: 'Foreign keys'. +(FmxSQLRoassalViews routineCallsIn: model) openWithTitle: 'Routine call candidates'. +``` + +Schema boxes contain table boxes, like class/method nesting examples. Foreign-key arrows point from the referencing table to the referenced table; composite references collapse to one edge. Call arrows point from the enclosing routine to every candidate routine, including trigger routines. Candidate edges are static-analysis evidence, not proof of runtime calls. Disconnected entities remain visible; endpoints outside the supplied model are omitted. Edges are placed above nodes, and no entities or relationships are changed. Empty models yield empty canvases. + +The historical Roassal 2/GT/Telescope sources remain in the repository for migration reference, but the unsupported `LegacyUI` load group is retired in v3. These Roassal 3 builders use current association APIs and replace the advertised visualization entry points. diff --git a/src/BaselineOfFAMIXNGSQL/BaselineOfFAMIXNGSQL.class.st b/src/BaselineOfFAMIXNGSQL/BaselineOfFAMIXNGSQL.class.st index cf29d08..2202d43 100644 --- a/src/BaselineOfFAMIXNGSQL/BaselineOfFAMIXNGSQL.class.st +++ b/src/BaselineOfFAMIXNGSQL/BaselineOfFAMIXNGSQL.class.st @@ -71,6 +71,5 @@ BaselineOfFAMIXNGSQL >> baseline: spec [ group: 'GeneratorTests' with: #('Generator' 'FAMIXNGSQLMetamodelGenerator-Tests'); group: 'Tests' with: #('Core' 'FamixNGSQL-Importer-Tests'); group: 'MooseIDE' with: #('Core' 'FamixNGSQL-MooseIDE'); - group: 'MooseIDETests' with: #('MooseIDE' 'FamixNGSQL-MooseIDE-Tests'); - group: 'LegacyUI' with: #('Core' 'FamixNGSQL-GTInspectorExtension' 'FAMIXNGSQLAnalysis' 'FAMIXNGSQLAnalysis-ConnectionsManager') ] + group: 'MooseIDETests' with: #('MooseIDE' 'FamixNGSQL-MooseIDE-Tests') ] ] diff --git a/src/FamixNGSQL-MooseIDE-Tests/FmxSQLRoassalViewsTest.class.st b/src/FamixNGSQL-MooseIDE-Tests/FmxSQLRoassalViewsTest.class.st new file mode 100644 index 0000000..63b0f35 --- /dev/null +++ b/src/FamixNGSQL-MooseIDE-Tests/FmxSQLRoassalViewsTest.class.st @@ -0,0 +1,60 @@ +Class { + #name : #FmxSQLRoassalViewsTest, + #superclass : #TestCase, + #category : #'FamixNGSQL-MooseIDE-Tests' +} +{ #category : #tests } +FmxSQLRoassalViewsTest >> testEmptyModel [ + | model | + model := FmxSQLModel new. + #(schemaTablesIn: foreignKeysIn: routineCallsIn:) do: [ :selector | + | canvas | + canvas := FmxSQLRoassalViews perform: selector with: model. + self assert: canvas nodes isEmpty. + self assert: canvas edges isEmpty ] +] +{ #category : #tests } +FmxSQLRoassalViewsTest >> testCompositeForeignKeysAndDisconnectedTable [ + | model source target lonely foreignKey canvas before | + model := FmxSQLModel new. + source := FmxSQLTable new name: 'orders'; mooseModel: model; yourself. + target := FmxSQLTable new name: 'customers'; mooseModel: model; yourself. + lonely := FmxSQLTable new name: 'disconnected'; mooseModel: model; yourself. + foreignKey := FmxSQLForeignKeyConstraint new table: source; mooseModel: model; yourself. + 2 timesRepeat: [ | column | + column := FmxSQLColumn new columnsContainer: target; mooseModel: model; yourself. + FmxSQLColumnReference new foreignKeyConstraintSource: foreignKey; target: column; mooseModel: model ]. + before := model entities size. + self assert: (FmxSQLRoassalViews foreignKeyPairsIn: model) asArray equals: { { source. target } }. + canvas := FmxSQLRoassalViews foreignKeysIn: model. + self assert: canvas nodes size equals: 3. + self assert: canvas edges size equals: 1. + self assert: canvas edges first from model identicalTo: source. + self assert: canvas edges first to model identicalTo: target. + self assert: model entities size equals: before +] +{ #category : #tests } +FmxSQLRoassalViewsTest >> testCallCandidatesIncludingTriggerAndExternalTarget [ + | model caller target trigger external canvas | + model := FmxSQLModel new. + caller := FmxSQLStoredProcedure new name: 'caller'; mooseModel: model; yourself. + target := FmxSQLStoredProcedure new name: 'callee'; mooseModel: model; yourself. + trigger := FmxSQLTriggerStoredProcedure new name: 'trigger'; mooseModel: model; yourself. + external := FmxSQLStoredProcedure new name: 'outside'; yourself. + FmxSQLStoredProcedureCall new sender: caller; candidates: { target. trigger. external }; mooseModel: model. + self assert: (FmxSQLRoassalViews routineCallPairsIn: model) size equals: 2. + canvas := FmxSQLRoassalViews routineCallsIn: model. + self assert: canvas nodes size equals: 3. + self assert: canvas edges size equals: 2 +] +{ #category : #tests } +FmxSQLRoassalViewsTest >> testSchemaContainsTableShapes [ + | model schema table canvas | + model := FmxSQLModel new. + schema := FmxSQLNamespace new name: 'app'; mooseModel: model; yourself. + table := FmxSQLTable new name: 'items'; parentNamespace: schema; mooseModel: model; yourself. + canvas := FmxSQLRoassalViews schemaTablesIn: model. + self assert: canvas nodes size equals: 1. + self assert: canvas nodes first model identicalTo: schema. + self assert: (canvas nodes first allChildren anySatisfy: [ :shape | shape model == table ]) +] diff --git a/src/FamixNGSQL-MooseIDE/FmxSQLRoassalViews.class.st b/src/FamixNGSQL-MooseIDE/FmxSQLRoassalViews.class.st new file mode 100644 index 0000000..f13d7bc --- /dev/null +++ b/src/FamixNGSQL-MooseIDE/FmxSQLRoassalViews.class.st @@ -0,0 +1,78 @@ +"Roassal 3 views using SQL containment and association APIs. Builders return unopened canvases." +Class { + #name : #FmxSQLRoassalViews, + #superclass : #Object, + #category : #'FamixNGSQL-MooseIDE' +} +{ #category : #building } +FmxSQLRoassalViews class >> schemaTablesIn: model [ + | graph | + graph := RSMondrian new. + graph shape box color: Color veryLightGray; labeled: #name. + graph nodes: (model allWithType: FmxSQLNamespace) forEach: [ :schema | + | tables | + tables := schema namespaceEntities select: [ :e | e isKindOf: FmxSQLTable ]. + graph shape box color: Color blue lighter; labeled: #name. + graph nodes: tables. + graph layout grid ]. + graph layout grid. + graph build. + ^ graph canvas +] +{ #category : #building } +FmxSQLRoassalViews class >> foreignKeysIn: model [ + ^ self graphOf: (model allWithType: FmxSQLTable) pairs: (self foreignKeyPairsIn: model) +] +{ #category : #building } +FmxSQLRoassalViews class >> routineCallsIn: model [ + ^ self graphOf: (self routinesIn: model) pairs: (self routineCallPairsIn: model) +] +{ #category : #private } +FmxSQLRoassalViews class >> graphOf: entities pairs: pairs [ + | graph | + graph := RSMondrian new. + graph shape box color: Color green muchLighter; labeled: [ :entity | self labelFor: entity ]. + graph nodes: entities. + graph arrowedLine color: Color gray; connectToAll: [ :entity | + (pairs select: [ :pair | pair first == entity ]) collect: #second ]. + graph layout circle. + graph build. + graph canvas edges do: #pushFront. + ^ graph canvas +] +{ #category : #private } +FmxSQLRoassalViews class >> labelFor: entity [ + ^ (entity parentNamespace ifNil: [ '' ] ifNotNil: [ :ns | ns name, '.' ]), entity name +] +{ #category : #extracting } +FmxSQLRoassalViews class >> foreignKeyPairsIn: model [ + | tables pairs | + tables := IdentitySet withAll: (model allWithType: FmxSQLTable). + pairs := Set new. + (model allWithType: FmxSQLForeignKeyConstraint) do: [ :foreignKey | + foreignKey outgoingForeignKeyColumnReferences do: [ :reference | + | target | + target := reference target ifNotNil: [ :column | column columnsContainer ]. + ((tables includes: foreignKey table) and: [ tables includes: target ]) ifTrue: [ + pairs add: { foreignKey table. target } ] ] ]. + ^ pairs +] +{ #category : #extracting } +FmxSQLRoassalViews class >> routinesIn: model [ + ^ ((model allWithType: FmxSQLStoredProcedure) asOrderedCollection + addAll: (model allWithType: FmxSQLTriggerStoredProcedure); yourself) asIdentitySet +] +{ #category : #extracting } +FmxSQLRoassalViews class >> routineCallPairsIn: model [ + | routines pairs | + routines := self routinesIn: model. + pairs := Set new. + (model allWithType: FmxSQLStoredProcedureCall) do: [ :call | + | caller | + caller := call source. + (caller notNil and: [ (routines includes: caller) not ]) ifTrue: [ + caller := caller allParents detect: [ :parent | routines includes: parent ] ifNone: [ nil ] ]. + (routines includes: caller) ifTrue: [ + call allTargets do: [ :target | (routines includes: target) ifTrue: [ pairs add: { caller. target } ] ] ] ]. + ^ pairs +]