├── LICENSE ├── README.md └── COSQuickRef.md /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 InterSystems Corp. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Caché ObjectScript Code Guidelines 2 | 3 | This is the first approximation of Caché ObjectScript guidelines we use for github.com/intersystems-ru projects. There are mandatory and recommended parts. 4 | 5 | We do not plan to be very restrictive, but want be rather flexible enough and allow any *reasonable* style. 6 | Here is the general advice: use common sense and try to *be consistent* wherever you write code. 7 | If there is some style already established in the class or utility being modified then we recommend 8 | to continue use that same style, than introducing yet another one, which may be more recommended 9 | but which will be introducing some unnecessary inconsistency. 10 | 11 | ## Quick Reference Guide 12 | 13 | For Caché ObjectScript quick reference guide see [here](COSQuickRef.md). 14 | 15 | ## Mandatory Part 16 | 17 | * Only "modern" syntax permitted, dotted syntax is not generally allowed (with few exceptions); 18 | * No short name for statements keywords or built-in functions allowed - use fully expanded names; 19 | * Comment your code appropriately, use English in comments to make your code easy to understand by your international colleagues. Same English-based rule applies to global, local, and type names. 20 | 21 | ## Recommended Part 22 | * We recommend to use fully expanded keyword or function name in lower case or capitalized. Whatever you'll select should be consistent across class or utility, i.e. 23 | ``` 24 | If (expression) { 25 | Do ##class(Sample).%New(initexpresion) 26 | For i=1:1:10 { 27 | Write something, $ZVersion, ! 28 | } 29 | } 30 | ``` 31 | or 32 | 33 | ``` 34 | if (expression) { 35 | do ##class(Sample).%New(initexpresion) 36 | for i=1:1:10 { 37 | write something, $zversion, ! 38 | } 39 | } 40 | ``` 41 | * We recommend to use fully qualified package name of a class (except `%Library` package) 42 | ``` 43 | Set obj = ##class(Sample.Person).%New(initexpresion) 44 | ``` 45 | instead of 46 | ``` 47 | Set obj = ##class(Person).%New(initexpresion) 48 | ``` 49 | * In general, use the reasonable name convention consistently across whole hierarchy. We recommend to use CamelCase for classes, methods and properties (e.g. `Sample.Person`, `OpenFile`, `LastIndex`, etc.), but smallCamelCase for local variables (e.g. `startDate`, `endDate`), but please be consistent and use local schema if it's different than current recommendation. 50 | * We recommend to use `#dim` statement for declaration of local variables types, e.g. 51 | ``` 52 | #dim array as %ArrayOfDataTypes = ##class(%ArrayOfDataTypes).%New() 53 | do array.SetAt(id, "id") 54 | #dim status As %Status = $$$OK 55 | ``` 56 | This will help editor to provide better auto-complete, and eventually will be used by static checker for type information extraction. 57 | * Please indent blocks reasonably. We recommend to use 8, 4 or 2 spaces indentations. Pick your level, and apply consistently. 58 | ``` 59 | if (expression) { 60 | do ..Method(args...) 61 | set i = j + 1 62 | } else { 63 | while (condition) { 64 | write arg1, arg2, ! 65 | } 66 | } 67 | ``` 68 | * For better readability please insert spaces after comma in functions/methods argument list, i.e. 69 | ``` 70 | Write 1, 2, 3 71 | Do ..Method(arg1, arg2, argN) 72 | ``` 73 | For obvious reasons this recommendation not applies to arguments in `$$$macro`-call, where such extra spaces will break final result. 74 | * For better readability please insert spaces around assignment. You may align several neighbour assignments according to your tastes, i.e. 75 | ``` 76 | #dim index As %Integer = 0 77 | #dim countOfItems As %Integer = ..Count() 78 | set j = k + 1 79 | set iN = jN \ l 80 | ``` 81 | 82 | ## Related discussion 83 | [Standards document for Objectscript developers](https://community.intersystems.com/post/standards-document-objectscript-developers) 84 | -------------------------------------------------------------------------------- /COSQuickRef.md: -------------------------------------------------------------------------------- 1 | # ObjectScript Quick Reference 2 | A list of some common ObjectScript expressions. **Note:** In order to distinguish actual text (things like "do", "set", "##class") from placeholder text (package names, class names, method names, variable names, object references, etc.) this document uses the convention of appending the text "Name" (packageName, className, methodName, varName, orefName, etc.) where necessary. 3 | 4 | ## Object/SQL Basics 5 | 6 | | Action | Code | 7 | |----------------------------------------------|-----------------------------------------------------------------------------------| 8 | | Call a class method | `Do ##class(packageName.className).methodName(arguments)`
`Set varName = ##class(packageName.className).methodName(arguments)`
Note: place a . before each pass-by-reference argument | 9 | | Call an instance method on an object | `Do orefName.methodName(arguments)`
`Set varName = orefName.methodName(arguments)`
Note: place a . before each pass-by-reference argument | 10 | | Create a new object | `Set orefName = ##class(packageName.className).%New()` | 11 | | Open an existing object | `Set orefName = ##class(packageName.className).%OpenId(id, concurrency, .status)` | 12 | | Open an existing object by unique index value| `Set orefName = ##class(packageName.className).IndexNameOpen(value, concurrency, .status)` | 13 | | Save an object | `Set status = orefName.%Save()` | 14 | | Retrieve the ID of a saved object | `Set id = orefName.%Id()` | 15 | | Retrieve the OID of a saved object | `Set oid = orefName.%Oid()` | 16 | | Retrieve property of a saved object | `Set varName = ##class(packageName.className).propertyNameGetStored(id)` | 17 | | Determine if an object was modified | `Set varName = orefName.%IsModified()` | 18 | | Validate an object without saving | `Set status = orefName.%ValidateObject()` | 19 | | Validate a property without saving | `Set status = ##class(packageName.className).propertyNameIsValid(orefName.propertyName)`| 20 | | Print status after error | `Do $system.Status.DisplayError(status)`
`Write $system.Status.GetErrorText(status)` | 21 | | Obtain status details after error | `Do $system.Status.DecomposeStatus(status, .err)` | 22 | | Convert status into exception object | `Set exception = ##class(%Exception.StatusException).CreateFromStatus(status)` | 23 | | Remove an object from process memory | `Set orefName = ""` | 24 | | Delete an existing object of a class | `Set status = ##class(packageName.className).%DeleteId(id)` | 25 | | Delete an existing object by unique index value| `Set status = ##class(packageName.className).IndexNameDelete(value)` | 26 | | Delete all saved objects of a class | `Do ##class(packageName.className).%DeleteExtent()`
`Do ##class(packageName.className).%KillExtent()` | 27 | | Reload properties of a saved object | `Do orefName.%Reload()` | 28 | | Clone an object | `Set orefClonedName = orefName.%ConstructClone()` | 29 | | Write a property | `Write orefName.propertyName` | 30 | | Set a property | `Set orefName.propertyName = value` | 31 | | Write a class parameter | `Write ..#PARAMETER`
`Write ##class(packageName.className).#PARAMETER` | 32 | | Set a serial (embedded) property | `Set orefName.serialPropertyName.embeddedPropertyName = value` | 33 | | Link two objects | `Set oref1Name.propertyName = oref2Name` | 34 | | Populate a class | `Do ##class(packageName.className).Populate(count, verbose)` | 35 | | Remove all objects in process memory | `Kill` | 36 | | List all objects in process memory | `Do $system.OBJ.ShowObjects()` | 37 | | Display all properties of an object | `Do $system.OBJ.Dump(orefName)`
`Zwrite orefName` (v2012.2+) | 38 | | Determine If variable is an object reference | `If $isobject(varName) ; 1=yes, 0=no` | 39 | | Find classname of an object | `Write $classname(orefName)` | 40 | | Start the SQL shell | `Do $system.SQL.Shell()` | 41 | | Check SQL privileges | `Do $system.SQL.CheckPriv()` | 42 | | Test a class query | `Do ##class(%ResultSet).RunQuery(className, queryName)` | 43 | | Declare a variable's type for IDE code completion | `#dim orefName as packageName.className` | 44 | 45 | ## ObjectScript Commands 46 | 47 | | Command | Description | 48 | |-------------------------------|-------------------------------------------------------------------------------------| 49 | | `Write` | Display text strings, value of variable or expression | 50 | | `Zwrite` | Display array, list string, bit string | 51 | | `Set` | Set value of variable | 52 | | `Do` | Execute method, procedure, or routine | 53 | | `Quit` or `Return` (v2013.1) | Terminate method, procedure, or routine. Optionally return value to calling method | 54 | | `Continue` | Stop current loop iteration, and continue looping | 55 | | `Halt` | Stop process and close Terminal | 56 | | `Kill` | Destroy variable(s) | 57 | | `If {} ElseIf {} Else {}` | Evaluate conditions and branch | 58 | | `For {}`, `While {}`, `Do {} While` | Execute block of code repeatedly | 59 | | `Try {} Catch {}`, `Throw` | Handle errors | 60 | 61 | ## ObjectScript Date/Time Functions and Special Variables 62 | 63 | | Action | Code | 64 | |--------------------------------------------------|-------------------------------------------------------------| 65 | | Date conversion (external → internal) | `Set varName = $zdh("mm/dd/yyyy")` | 66 | | Date conversion (internal → external) | `Set varName = $zd(internalDate, format)` | 67 | | Time conversion (external → internal) | `Set varName = $zth("hh:mm:ss")` | 68 | | Time conversion (internal → external) | `Set varName = $zt(internalTime, format)` | 69 | | Display current internal date/time string | `Write $horolog` | 70 | | Display UTC date/time string | `Write $ztimestamp` | 71 | | Get the date relative to other date (any type) | `Write $system.SQL.DATEADD("hh", -4, "yyyy-mm-dd hh:mm:ss")` | 72 | | Get the difference between two dates (any type) | `Write $system.SQL.DATEDIFF("hh", horolog-or-ztimestamp, horolog-or-ztimestamp)`| 73 | 74 | ## ObjectScript Branching Functions 75 | 76 | | Action | Code | 77 | |--------------------------------------------------|------------------------------------------------------------------------| 78 | | Display result for value of expression | `Write $case(expression, value1:result1, value2:result2, …, :resultN)` | 79 | | Display result for first true condition | `Write $select(condition1:result1, condition2:result2, …, 1:resultN)` | 80 | 81 | ## ObjectScript String Functions 82 | 83 | | Action | Code | 84 | |----------------------------------------------------------|---------------------------------------------------------------| 85 | | Display substring extracted from string | `Write $extract(string, start, end)` | 86 | | Display right-justified string within width characters | `Write $justify(string, width)` | 87 | | Display length of string | `Write $length(string)` | 88 | | Display number of delimited pieces in string | `Write $length(string, delimiter)` | 89 | | Display piece from delimited string | `Write $piece(string, delimiter, pieceNumber)` | 90 | | Set piece into delimited string | `Set $piece(string, delimiter, pieceNumber) = piece` | 91 | | Display string after replacing substring | `Write $replace(string, subString, replaceString)` | 92 | | Display reversed string | `Write $reverse(string)` | 93 | | Display string after replacing characters | `Write $translate(string, searchChars, replaceChars)` | 94 | | Build a list | `Set listString = $listbuild(list items, separated by comma)` | 95 | | Retrieve an item from a list | `Set listItem = $list(listString, position)` | 96 | | Retrieves elements sequentially from a list | `Set pointerToNextElement = 0`
`While = $ListNext(listString, pointerToNextElement, value) {}` | 97 | | Put item into list string | `Set $list(listString, position) = substring` | 98 | | Display the length of a list | `Write $listlength(listString)` | 99 | | Search a value in a list | `Write $listfind(listString, value)` | 100 | | Build a list from a string | `Set listString = $listFromString(string, delimiter)` | 101 | 102 | ## ObjectScript Existence Functions 103 | 104 | | Action | Code | 105 | |--------------------------------------------------|-------------------------------------------------------------| 106 | | Check if variable exists | `Write $data(varName)` | 107 | | Return value of variable, or default If undefined | `Write $get(varName, defaultVarName)` | 108 | | Return next valid subscript in array | `Write $order(array(subscript))` | 109 | 110 | ## Additional ObjectScript Functions 111 | 112 | | Action | Code | 113 | |--------------------------------------------------|---------------------------------------------------------------------| 114 | | Increment ^global by 1 (or by increment) | `$increment(^globalName, increment)`
`$sequence(^globalName)` | 115 | | Match a regular expression | `Set matches = $match(string, regularexpression)` | 116 | | Display random integer from start to start+(count-1) | `Write $random(count) + start` | 117 | 118 | ## ObjectScript Special Variables 119 | 120 | | Action | Code | 121 | |-----------------------------------|---------------------------------------| 122 | | Display process ID | `Write $job` | 123 | | Display current namespace | `Write $namespace` | 124 | | Change current namespace | `Set $namespace = newnamespace` | 125 | | Display username | `Write $username` | 126 | | Display roles | `Write $roles` | 127 | 128 | ## Utilities 129 | 130 | | Action | Code | 131 | |-----------------------------------|---------------------------------------| 132 | | Change current namespace | `Do ^%CD`
`zn "newnamespace"` | 133 | | Display a ^global | `Do ^%G`
`zwrite ^globalName | 134 | 135 | ## Collections 136 | 137 | | Action | Code | 138 | |--------------------------------------------------------------|------------------------------------------------------------------------------------------------------------| 139 | | Create a new standalone list
Work with a list property | `Set orefListName=##class(%ListOfDataTypes).%New()`
Use methods below on a list collection property | 140 | | Insert an element at the end of a list | `Do orefListName.Insert(value)`
`Do orefName.listPropertyName.Insert(value)` | 141 | | Insert an element into a list | `Do orefListName.SetAt(value, position)`
`Do orefName.listPropertyName.SetAt(value, position)` | 142 | | Remove an element from a list | `Do orefListName.RemoveAt(position)`
`Do orefName.listPropertyName.RemoveAt(position)` | 143 | | Display an element of a list | `Write orefListName.GetAt(position)`
`Write orefName.listPropertyName.GetAt(position)` | 144 | | Display the size of a list | `Write orefListName.Count()`
`Write orefName.listPropertyName.Count()` | 145 | | Clear all the elements of a list | `Do orefListName.Clear()`
`Do orefName.listPropertyName.Clear()` | 146 | | Create a new standalone array
Work with an array property | `Set orefArrayName=##class(%ArrayOfDataTypes).%New()`
Use methods below on an array collection property | 147 | | Insert an element into an array | `Do orefArrayName.SetAt(value, key)`
`Do orefName.arrayPropertyName.SetAt(value, key)` | 148 | | Remove an element from an array | `Do orefArrayName.RemoveAt(key)`
`Do orefName.arrayPropertyName.RemoveAt(key)` | 149 | | Display an element of an array | `Write orefArrayName.GetAt(key)`
`Do orefName.arrayPropertyName.GetAt(key)` | 150 | | Display the size of an array | `Write orefArrayName.Count()`
`Do orefName.arrayPropertyName.Count()` | 151 | | Clear all elements of an array | `Do orefArrayName.Clear()`
`Do orefName.arrayPropertyName.Clear()` | 152 | 153 | ## Relationships 154 | 155 | | Action | Code | 156 | |-----------------------------------------|--------------------------------------------------------------------------------------------------------------| 157 | | Parent-to-children object linking | `Do orefParentName.childPropertyName.Insert(orefChildName)`
`Set orefChildName.parentPropertyName = orefParentName` | 158 | | One-to-many object linking | `Do orefOneName.manyPropertyName.Insert(orefManyName)`
`Set orefManyName.onePropertyName = orefOneName` | 159 | | Write a property of a child object | `Write orefParentName.childPropertyName.GetAt(position).propertyName` | 160 | | Write a property of a many object | `Write orefOneName.manyPropertyName.GetAt(position).propertyName` | 161 | | Display the count of child/many objects | `Write orefParentName.childPropertyName.Count()`
`Write orefOneName.manyPropertyName.Count()` | 162 | | Open a many/child object directly | `Set orefName = ##class(packageName.className).IDKEYOpen(parentID, childsub)` | 163 | | Retrieve the id of a child object | `Set status = ##class(packageName.className).IDKEYExists(parentID, childsub, .childID)` | 164 | | Clear the child/many objects | `Do orefParentName.childPropertyName.Clear()
Do orefOneName.manyPropertyName.Clear()` | 165 | 166 | ## Streams 167 | 168 | | Action | Code | 169 | |-------------------------------------------|---------------------------------------------------------------------------------------| 170 | | Create a new stream | `Set orefStreamName=##class(%Stream.GlobalCharacter).%New()`
`Set streamObject=##class(%Stream.GlobalBinary).%New()`
Use methods below on a stream property | 171 | | Add text to a stream | `Do orefStreamName.Write(text)`
`Do orefName.streamPropertyName.Write(text)` | 172 | | Add a line of text to a stream | `Do orefStreamName.WriteLine(text)`
`Do orefName.streamPropertyName.WriteLine(text)` | 173 | | Read len characters of text from a stream | `Write orefStreamName.Read(len)`
`Write orefName.streamPropertyName.Read(len)` | 174 | | Read a line of text from a stream | `Write orefStreamName.ReadLine(len)`
`Write orefName.streamPropertyName.ReadLine(len)` | 175 | | Go to the beginning of a stream | `Do orefStreamName.Rewind()`
`Do orefName.streamPropertyName.Rewind()` | 176 | | Go to the end of a stream, for appending | `Do orefStreamName.MoveToEnd()`
`Do orefName.streamPropertyName.MoveToEnd()` | 177 | | Clear a stream | `Do orefStreamName.Clear()`
`Do orefName.streamPropertyName.Clear()` | 178 | | Display the length of a stream | `Write orefStreamName.Size`
`Write orefName.streamPropertyName.Size` | 179 | 180 | ## Unit Testing Macros 181 | 182 | | Action | Code | 183 | |-----------------------------|--------------------------------------------------| 184 | | Assert equality | `Do $$$AssertEquals(value1, value2, message)` | 185 | | Assert inequality | `Do $$$AssertNotEquals(value1, value2, message)` | 186 | | Assert status is OK | `Do $$$AssertStatusOK(status, message)` | 187 | | Assert status isn't OK | `Do $$$AssertStatusNotOK(status, message)` | 188 | | Assert condition is true | `Do $$$AssertTrue(condition, message)` | 189 | | Assert condition isn't true | `Do $$$AssertNotTrue(condition, message)` | 190 | | Log message | `Do $$$LogMessage(message)` | 191 | 192 | ## Other Macros 193 | 194 | | Action | Code | 195 | |----------------------------------|-------------------------------------------| 196 | | Return a good status | `Quit $$$OK` | 197 | | Return an error status | `Quit $$$ERROR($$$GeneralError, message)` | 198 | | Throw exception on error | `$$$ThrowOnError(status, code)` or `$$$TOE(status, code)` | 199 | | Check if status is good | `If $$$ISOK(status)` | 200 | | Check if status is an error | `If $$$ISERR(status)` | 201 | | Return a null object reference | `Quit $$$NULLOREF` | 202 | | Place a new line within a string | `Write string1_$$$NL_string2` | 203 | --------------------------------------------------------------------------------