Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitaeverywhere committed Oct 18, 2015
2 parents 966bf88 + 9c656e3 commit bf1a4ea
Show file tree
Hide file tree
Showing 19 changed files with 518 additions and 180 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,31 @@ An UML Class explorer for InterSystems Caché.
+ Build class diagrams;
+ Build diagrams for any package or subpackage;
+ Edit diagrams after build;
+ Switch between strict UML notation and designed view;
+ Export diagrams as an image;
+ See Class methods, properties, parameters, SQL queries and more;
+ See any keywords and related information by hovering over everything with pointer;
+ View class methods code with syntax highlighting;
+ Zoom in and out;
+ Search on diagram or in class tree;
+ Explore!

## Screenshots

![Demo](https://cloud.githubusercontent.com/assets/4989256/10561433/30415858-7531-11e5-97c6-6623d2b6ab30.png)
![Demo](https://cloud.githubusercontent.com/assets/4989256/10566777/112646cc-75f9-11e5-95cc-3db82abf1706.png)

## Installation

To install latest Caché UML Explorer, you just need to import UMLExplorer package. Download the
To install latest Caché Class Explorer, you just need to import ClassExplorer package. Download the
archive from [latest releases](https://github.com/intersystems-ru/UMLExplorer/releases), and then import
<code>Cache/CacheUMLExplorer-vX.X.X.xml</code> file.
<code>Cache/CacheClassExplorer-vX.X.X.xml</code> file.

###### Web application
Note that importing UMLExplorer.WebAppInstaller class will also create a /UMLExplorer application.
Note that importing ClassExplorer.WebAppInstaller class will also create a /ClassExplorer application.
If you want to create WEB application manually, please, do not import this class. Anyway, <b>
importing this class requires %SYS permission.</b>
## Usage
Visit <code>[server domain and port]/UMLExplorer/</code> (slash at end required) to enter
Visit <code>[server domain and port]/ClassExplorer/</code> (slash at end required) to enter
application.

## Build
Expand Down
114 changes: 70 additions & 44 deletions cache/projectTemplate.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Export generator="Cache" version="25" zv="Cache for Windows (x86-64) 2015.2 (Build 540)" ts="2015-04-28 19:50:48">
<Class name="UMLExplorer.ClassView">
<Class name="ClassExplorer.ClassView">
<Description>
Cache UML Explorer vX.X.X/*build.replace:pkg.version*/
Cache Class Explorer vX.X.X/*build.replace:pkg.version*/
Class contains methods that return structured classes/packages data.</Description>
<TimeChanged>63830,81286.756889</TimeChanged>
<TimeChanged>63844,1327.122337</TimeChanged>
<TimeCreated>63653,67019.989197</TimeCreated>

<Method name="getAllNamespacesList">
Expand Down Expand Up @@ -45,33 +45,34 @@ Returns structured class tree with all classes available in current namespace</D
set level = 1
do objects.SetAt(resp, level)
do classes.Execute()
while (classes.Next()) {
set name = classes.Data("Name")
if ($EXTRACT(name, 1, 1) = "%") && ($NAMESPACE '= "%SYS") { continue }
set parts = $LISTFROMSTRING(name, ".")
set i = 0
while (i < $LISTLENGTH(parts)) && ($LISTGET(lastParts, i + 1) = $LISTGET(parts, i + 1)) {
set i = i + 1
set level = 1
while ((level < $LISTLENGTH(parts)) && ($LISTGET(lastParts, level) = ("/"_$LISTGET(parts, level)))) {
set level = level + 1
}
set level = i + 1
set resp = objects.GetAt(level)
if (resp="") {
set resp = ##class(%ZEN.proxyObject).%New()
do objects.GetAt(level - 1).%DispatchSetProperty($LISTGET(parts, level - 1), resp)
do objects.GetAt(level - 1).%DispatchSetProperty("/" _ $LISTGET(parts, level - 1), resp)
do objects.SetAt(resp, level)
}
while ($LISTLENGTH(parts) > level) {
set level = level + 1
set resp = ##class(%ZEN.proxyObject).%New()
do objects.GetAt(level - 1).%DispatchSetProperty($LISTGET(parts, level - 1), resp)
do objects.GetAt(level - 1).%DispatchSetProperty("/" _ $LISTGET(parts, level - 1), resp)
do objects.SetAt(resp, level)
}
if ($LISTLENGTH(parts) = level) {
do resp.%DispatchSetProperty($LISTGET(parts, level), classes.Data("Hidden"))
}
set lastParts = parts
for i=1:1:$LISTLENGTH(lastParts)-1 {
set $LIST(lastParts, i) = "/"_$LISTGET(lastParts, i)
}
}
quit objects.GetAt(1)
Expand All @@ -87,26 +88,38 @@ Return structured data about class.</Description>
<ReturnType>%ZEN.proxyObject</ReturnType>
<Implementation><![CDATA[
set classDefinition = ##class(%Dictionary.ClassDefinition).%OpenId(className)
set compiledClassDefinition = ##class(%Dictionary.CompiledClass).%OpenId(className)
if (classDefinition = "") || (oData.classes.%DispatchGetProperty(classDefinition.Name) '= "") quit ""
set oClass = ##class(%ZEN.proxyObject).%New()
do oData.classes.%DispatchSetProperty(classDefinition.Name, oClass) // prevent from recursive setup
set package = $LISTTOSTRING($LIST($LISTFROMSTRING(classDefinition.Name, "."), 1, *-1),".")
set oProperties = ##class(%ZEN.proxyObject).%New()
set oQueries = ##class(%ZEN.proxyObject).%New()
set oIndices = ##class(%ZEN.proxyObject).%New()
set oClass.NAMESPACE = $NAMESPACE
set oClass.SYSTEM = classDefinition.System
set oClass.PROCEDUREBLOCK = classDefinition.ProcedureBlock
set oClass.ABSTRACT = classDefinition.Abstract
set oClass.FINAL = classDefinition.Final
set oClass.HIDDEN = classDefinition.Hidden
set oClass.classType = classDefinition.ClassType
set oClass.serverOnly = classDefinition.ServerOnly // -
set oClass.isDataType = classDefinition.ClientDataTypeIsDefined()
set oClass.isOdbcType = classDefinition.OdbcTypeIsDefined()
set oClass.isSoapBindingStyle = classDefinition.SoapBindingStyleIsDefined()
set oClass.isSoapBodyUse = classDefinition.SoapBodyUseIsDefined()
set oClass.isSqlCategory = classDefinition.SqlCategoryIsDefined()
set props = ##class(%Dictionary.ClassDefinition).%OpenId("%Dictionary.ClassDefinition")
for j=1:1:props.Properties.Count() {
set pname = props.Properties.GetAt(j).Name
set:((pname '= "parent")
&& ('props.Properties.GetAt(j).Private)
&& ('$IsObject($PROPERTY(classDefinition, pname)))) $PROPERTY(oClass, pname) = $PROPERTY(classDefinition, pname)
}
if (oClass.TimeChanged) { set oClass.TimeChanged = $zdatetime(oClass.TimeChanged) }
if (oClass.TimeCreated) { set oClass.TimeCreated = $zdatetime(oClass.TimeCreated) }
if ((compiledClassDefinition '= "") && (compiledClassDefinition.ClassType '= "")) {
set oClass.ClassType = compiledClassDefinition.ClassType // set class type from all inherited classes
}
set oClass.Super = "" // do not quit with super at this moment
if (oData.restrictPackage) && ('..inPackage(oData.basePackageName, package)) quit oClass
set oClass.super = ..correctInheritance(oData, classDefinition, package)
set oClass.Super = ..correctInheritance(oData, classDefinition, package) // now expand super names
set oClass.properties = oProperties
set count = classDefinition.Properties.Count()
Expand Down Expand Up @@ -171,7 +184,20 @@ Return structured data about class.</Description>
do oQueries.%DispatchSetProperty(q.Name, oProp)
}
do ..collectInheritance(oData, oClass.super)
#dim ind as %Dictionary.IndexDefinition
set oClass.indices = oIndices
set props = ##class(%Dictionary.ClassDefinition).%OpenId("%Dictionary.IndexDefinition")
for i=1:1:classDefinition.Indices.Count() {
set oProp = ##class(%ZEN.proxyObject).%New()
set ind = classDefinition.Indices.GetAt(i)
for j=1:1:props.Properties.Count() {
set pname = props.Properties.GetAt(j).Name
set:(pname '= "parent") $PROPERTY(oProp, pname) = $PROPERTY(ind, pname)
}
do oIndices.%DispatchSetProperty(ind.Name, oProp)
}
do ..collectInheritance(oData, oClass.Super)
quit oClass
]]></Implementation>
Expand Down Expand Up @@ -343,19 +369,19 @@ Returns structured package data</Description>
</Class>


<Project name="UMLExplorer" LastModified="2015-05-24 18:14:48.579613">
<Project name="ClassExplorer" LastModified="2015-05-24 18:14:48.579613">
<Items>
<ProjectItem name="UMLExplorer.ClassView" type="CLS"></ProjectItem>
<ProjectItem name="UMLExplorer.Router" type="CLS"></ProjectItem>
<ProjectItem name="UMLExplorer.StaticContent" type="CLS"></ProjectItem>
<ProjectItem name="UMLExplorer.WebAppInstaller" type="CLS"></ProjectItem>
<ProjectItem name="ClassExplorer.ClassView" type="CLS"></ProjectItem>
<ProjectItem name="ClassExplorer.Router" type="CLS"></ProjectItem>
<ProjectItem name="ClassExplorer.StaticContent" type="CLS"></ProjectItem>
<ProjectItem name="ClassExplorer.WebAppInstaller" type="CLS"></ProjectItem>
</Items>
</Project>


<Class name="UMLExplorer.Router">
<Class name="ClassExplorer.Router">
<Description>
REST interface for UMLExplorer</Description>
REST interface for ClassExplorer</Description>
<Super>%CSP.REST</Super>
<TimeChanged>63697,73073.878177</TimeChanged>
<TimeCreated>63648,30450.187229</TimeCreated>
Expand All @@ -365,8 +391,8 @@ REST interface for UMLExplorer</Description>
<Routes>
<Route Url="/" Method="GET" Call="Index"/>
<Route Url="/index" Method="GET" Call="Index"/>
<Route Url="/css/CacheUMLExplorer.css" Method="GET" Call="GetCss"/>
<Route Url="/js/CacheUMLExplorer.js" Method="GET" Call="GetJs"/>
<Route Url="/css/CacheClassExplorer.css" Method="GET" Call="GetCss"/>
<Route Url="/js/CacheClassExplorer.js" Method="GET" Call="GetJs"/>
<Route Url="/Test" Method="GET" Call="Test"/>
<Route Url="/GetClassTree" Method="GET" Call="GetClassTree"/>
<Route Url="/GetClassView" Method="GET" Call="GetClassView"/>
Expand All @@ -383,7 +409,7 @@ Method returns whole class tree visible in the current namespace.</Description>
<ClassMethod>1</ClassMethod>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[
do ##class(UMLExplorer.ClassView).getClassTree(%request.Get("namespace")).%ToJSON(, "o")
do ##class(ClassExplorer.ClassView).getClassTree(%request.Get("namespace")).%ToJSON(, "o")
return $$$OK
]]></Implementation>
</Method>
Expand Down Expand Up @@ -420,7 +446,7 @@ Return the list of all namespaces</Description>
<ClassMethod>1</ClassMethod>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[
do ##class(UMLExplorer.ClassView).getAllNamespacesList().%ToJSON(, "o")
do ##class(ClassExplorer.ClassView).getAllNamespacesList().%ToJSON(, "o")
return $$$OK
]]></Implementation>
</Method>
Expand Down Expand Up @@ -513,7 +539,7 @@ Issue an "304 Not Modified" status</Description>
</Class>


<Class name="UMLExplorer.StaticContent">
<Class name="ClassExplorer.StaticContent">
<Description>
Cache UML Explorer vX.X.X/*build.replace:pkg.version*/ static content generator.
Class contains methods that return JS/CSS/HTML data for single page application.</Description>
Expand All @@ -527,7 +553,7 @@ Write the contents of xData tag</Description>
<FormalSpec>Const:%String</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[
Set xdata = ##class(%Dictionary.CompiledXData).%OpenId("UMLExplorer.StaticContent||"_Const).Data
Set xdata = ##class(%Dictionary.CompiledXData).%OpenId("ClassExplorer.StaticContent||"_Const).Data
set status=##class(%XML.TextReader).ParseStream(xdata, .textreader)
while textreader.Read() { if (textreader.NodeType="chars") { w textreader.Value } }
return $$$OK
Expand Down Expand Up @@ -560,7 +586,7 @@ Write the contents of xData tag</Description>
</Class>


<Class name="UMLExplorer.WebAppInstaller">
<Class name="ClassExplorer.WebAppInstaller">
<Super>%Projection.AbstractProjection</Super>
<TimeChanged>63696,65168.289869</TimeChanged>
<TimeCreated>63696,64041.85537</TimeCreated>
Expand All @@ -587,14 +613,14 @@ This method is invoked when a class is compiled.</Description>
set cspProperties("NameSpace") = ns
set cspProperties("Description") = "A WEB application for Cache UML Explorer."
set cspProperties("IsNameSpaceDefault") = 1
set cspProperties("DispatchClass") = "UMLExplorer.Router"
if ('##class(Security.Applications).Exists("/UMLExplorer")) {
w !, "Creating WEB application ""/UMLExplorer""..."
set tSC = ##class(Security.Applications).Create("/UMLExplorer", .cspProperties)
set cspProperties("DispatchClass") = "ClassExplorer.Router"
if ('##class(Security.Applications).Exists("/ClassExplorer")) {
w !, "Creating WEB application ""/ClassExplorer""..."
set tSC = ##class(Security.Applications).Create("/ClassExplorer", .cspProperties)
if $$$ISERR(tSC) throw ##class(%Installer.Exception).CreateFromStatus(tSC)
w !, "WEB application ""/UMLExplorer"" created."
w !, "WEB application ""/ClassExplorer"" created."
} else {
w !, "WEB application ""/UMLExplorer"" already exists, so it is ready to use."
w !, "WEB application ""/ClassExplorer"" already exists, so it is ready to use."
}
zn:ns'="%SYS" ns
quit $$$OK
Expand All @@ -610,10 +636,10 @@ This method is invoked when a class is 'uncompiled'.</Description>
<Implementation><![CDATA[
set ns = $NAMESPACE
zn:ns'="%SYS" "%SYS"
if (##class(Security.Applications).Exists("/UMLExplorer")) {
w !, "Deleting WEB application ""/UMLExplorer""..."
do ##class(Security.Applications).Delete("/UMLExplorer")
w !, "WEB application ""/UMLExplorer"" was successfully removed."
if (##class(Security.Applications).Exists("/ClassExplorer")) {
w !, "Deleting WEB application ""/ClassExplorer""..."
do ##class(Security.Applications).Delete("/ClassExplorer")
w !, "WEB application ""/ClassExplorer"" was successfully removed."
}
zn:ns'="%SYS" ns
QUIT $$$OK
Expand Down
26 changes: 13 additions & 13 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var banner = [
" ** @author <%= pkg.author %>",
" ** @version <%= pkg.version %>",
" ** @license <%= pkg.license %>",
" ** @see https://github.com/ZitRos/CacheUMLExplorer",
" ** @see https://github.com/ZitRos/CacheClassExplorer",
" **/",
""
].join("\n");
Expand Down Expand Up @@ -58,17 +58,17 @@ gulp.task("gatherLibs", ["clean"], function () {
"web/jsLib/joint.layout.DirectedGraph.min.js"
]))
.pipe(stripComments({ safe: true }))
.pipe(concat("CacheUMLExplorer.js"))
.pipe(concat("CacheClassExplorer.js"))
.pipe(replace(/ /g, "\\x0B"))
.pipe(replace(/\x1b/g, "\\x1B"))
.pipe(gulp.dest("build/web/js/"));
});

gulp.task("gatherScripts", ["clean", "gatherLibs"], function () {
return gulp.src("web/js/*.js")
.pipe(concat("CacheUMLExplorer.js"))
.pipe(concat("CacheClassExplorer.js"))
.pipe(specialReplace())
.pipe(wrap("CacheUMLExplorer = (function(){<%= contents %> return CacheUMLExplorer;}());"))
.pipe(wrap("CacheClassExplorer = (function(){<%= contents %> return CacheClassExplorer;}());"))
.pipe(uglify({
output: {
ascii_only: true,
Expand All @@ -78,15 +78,15 @@ gulp.task("gatherScripts", ["clean", "gatherLibs"], function () {
preserveComments: "some"
}))
.pipe(header(banner, { pkg: pkg }))
.pipe(addsrc.prepend("build/web/js/CacheUMLExplorer.js"))
.pipe(concat("CacheUMLExplorer.js"))
.pipe(addsrc.prepend("build/web/js/CacheClassExplorer.js"))
.pipe(concat("CacheClassExplorer.js"))
.pipe(replace(/\x1b/g, "\\x1B"))
.pipe(gulp.dest("build/web/js/"));
});

gulp.task("gatherCSS", ["clean"], function () {
return gulp.src("web/css/*.css")
.pipe(concat("CacheUMLExplorer.css"))
.pipe(concat("CacheClassExplorer.css"))
.pipe(postcss([ autoprefixer({ browsers: ["last 3 version"] }) ]))
.pipe(minifyCSS({ keepSpecialComments: 0 }))
.pipe(gulp.dest("build/web/css/"));
Expand All @@ -95,8 +95,8 @@ gulp.task("gatherCSS", ["clean"], function () {
gulp.task("addHTMLFile", ["clean"], function () {
return gulp.src("web/index.html")
.pipe(htmlReplace({
"css": "css/CacheUMLExplorer.css",
"js": "js/CacheUMLExplorer.js"
"css": "css/CacheClassExplorer.css",
"js": "js/CacheClassExplorer.js"
}))
.pipe(gulp.dest("build/web/"));
});
Expand All @@ -118,23 +118,23 @@ gulp.task("exportCacheXML", [
.pipe(specialReplace())
.pipe(replace(
/\{\{replace:css}}/,
function () { return fs.readFileSync("build/web/css/CacheUMLExplorer.css", "utf-8"); }
function () { return fs.readFileSync("build/web/css/CacheClassExplorer.css", "utf-8"); }
))
.pipe(replace(
/\{\{replace:js}}/,
function () { return fs.readFileSync("build/web/js/CacheUMLExplorer.js", "utf-8"); }
function () { return fs.readFileSync("build/web/js/CacheClassExplorer.js", "utf-8"); }
))
.pipe(replace(
/\{\{replace:html}}/,
function () { return fs.readFileSync("build/web/index.html", "utf-8"); }
))
.pipe(rename(function (path) { path.basename = "CacheUMLExplorer-v" + pkg["version"]; }))
.pipe(rename(function (path) { path.basename = "CacheClassExplorer-v" + pkg["version"]; }))
.pipe(gulp.dest("build/Cache"));
});

gulp.task("zipRelease", ["exportCacheXML"], function () {
return gulp.src(["build/**/*", "!build/web/**/*"])
.pipe(zip("CacheUMLExplorer-v" + pkg["version"] + ".zip", {
.pipe(zip("CacheClassExplorer-v" + pkg["version"] + ".zip", {
comment: "Cache UML explorer v" + pkg["version"] + " by Nikita Savchenko\n\n" +
"+ Cache folder holds XML file to import to InterSystems Cache.\n\n" +
"For further information about installation and information, check README.md file."
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "CacheUMLExplorer",
"version": "1.5.1",
"name": "CacheClassExplorer",
"version": "1.8.2",
"description": "An UML Class explorer for InterSystems Caché",
"directories": {
"test": "test"
Expand All @@ -9,7 +9,7 @@
"devDependencies": {
"autoprefixer-core": "^5.1.11",
"express": "^5.0.0-alpha.1",
"gulp": "^3.8.11",
"gulp": "^3.9.0",
"gulp-add-src": "^0.2.0",
"gulp-clean": "^0.3.1",
"gulp-concat": "^2.4.1",
Expand Down
Loading

0 comments on commit bf1a4ea

Please sign in to comment.