go to post Rubens Silva · Aug 10, 2017 This works without implementing anything else. So I'll consider it as a coding style or preference.Hmm, I didn't know I could use $classmethod or $method with expression methods as well.
go to post Rubens Silva · Aug 9, 2017 Thank you for your suggestion, however the error still happens.I also didn't made it clear, but the payload doesn't have a class to be reflected from, it can be abstract, requiring it to be parsed with %DynamicObject or %DynamicArray.Since there's no class with %Stream.* property, %ConvertJSONToObject cannot decide what to do with long strings.The ideal would be to detect the $$$MaxStringSize size and fallback to an internal %Stream instance before assigning the value to the proxy.Also, I'm trying to avoid using %ZEN.proxyObject due to issues with stacking many objects for a single process. So the class parameter should be something inheriting from %DynamicAbstractObject. Here's is the step-by-step: DEV>set f = ##class(%Stream.FileCharacter).%New() DEV>do f.LinkToFile("/temp/lorem.json") DEV>set sc = ##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject(f,, .o) DEV>if $System.Status.IsError(sc) do $System.OBJ.DisplayError(sc) ERRO #5002: Erro Caché: <MAXSTRING>%ParseJSON+290^%ZEN.Auxiliary.jsonProvider.1 This won't happen if I have long strings enabled.
go to post Rubens Silva · Aug 8, 2017 Yes, like... %DynamicObject and %DynamicArray.I still don't quite get why to use %objlasterror instead of returning the error. Method %ToJSON(outstrm As %Stream.Object) As %String{if $D(outstrm) {if $isobject(outstrm) {if '(outstrm.%IsA("%Stream.Object")) {throw ##class(%Exception.General).%New("%ToJSON - argument passed is not an instance of %Stream.Object")}try {set ans = $zu(210,27,outstrm)} catch {do $$$APPERROR1($$$LASTERROR) <- This appends the error to %objlasterror and that's it.}
go to post Rubens Silva · Aug 8, 2017 I see, we have been using an in-house tool with features similar to yours as well, at that time files were still being exported as XML instead of UDL. But now we're moving our development to local and enforcing project usage using this tool. We also had our share of pain with editing live production code and I have to say, it's not the greatest feeling.
go to post Rubens Silva · Aug 8, 2017 a homemade toolWow, that's unexpected. Care to share some details about that tool?
go to post Rubens Silva · Aug 8, 2017 I don't know what you're extending your class from, but try calling ##super() before you set the timeout.EDIT: Hold on, try using %response.Timeout instead. Maybe your problem is not the session itself, but the response timeout instead. /// Can be set in the OnPreHTTP() method of a page in which case this changes the amount of time the /// CSP gateway will wait for a response from the server in seconds before it reports the 'Server is not /// responding' error message. This is useful if you know that this page is doing an expensive SQL /// query that will take a couple of minutes and you want to set the server response timeout on /// the CSP gateway to a minute and yet wait three minutes for this page to respond. It will just /// change the server response timeout for this page only. If not set the the CSP gateway uses its /// default timeout value specified in the CSP gateway configuration. Property Timeout As %Integer;
go to post Rubens Silva · Aug 8, 2017 NOTE:Expert programmers try to keep away from using GOTO because it can break greatly the code workflow consistency. This is a basic concept for using structured programming.So, before you think about using it, try to render the same effect using subroutines and methods instead.
go to post Rubens Silva · Aug 8, 2017 Since ZEN runs under CSP, maybe %session.AppTimeout = 900 // 15 mins.Just remember to change it inside OnPreHTTP.
go to post Rubens Silva · Aug 8, 2017 That's quite a topic for complex discussions.Do you use an issue tracking / collaboration system? If so which one. Any you would recommend or immediately dismiss based on personal experience?I use Github plus repository issues.How do you keep track of large code bases? Thousdands of folders named backup1, backups2, ..., SVN, git?Git.Do you have a development server to which you commit and test features there, or do you rather run a local copy of caché and implement features locally first, then push to the server?Locally implemented and tested. tested and implemented.Bonus question: How do you handle legacy code (and I mean the using lots of $ZUs kind of legacy) code? Leave it untouched and try to implement new features elesewhere? Rewrite the entire thing?It depends, the more complex the code is, more I consider creating modern API wrappers instead of re-writting it.
go to post Rubens Silva · Aug 7, 2017 Since booleans can be only true or false, you usually don't need to know about anything else, so let the caller handle what it should do. ClassMethod AssertClassExists(ClassName As %String) As %Status { if '..ClassExists(ClassName) return $$$ERRROR($$$ClassDoesNotExist, ClassName) return $$$OK } ClassMethod ClassExists(ClassName as %String) as %Boolean { return ##class(%Dictionary.CompiledClass).%ExistsId(ClassName) }
go to post Rubens Silva · Aug 7, 2017 I only return the result when I'm absolutely sure that the method cannot throw any error. Otherwise I follow the rule:1 - Obligatory arguments first.2 - Result by second.2 - Parameter with initial values third.3 - Rest parameters for last.(obligatoryParamA, obligatoryParamB, obligatoryParamC, result, optionalA, optionalB, rest...)
go to post Rubens Silva · Aug 7, 2017 %Next does receives a %Status as output. It's not exactly what you suggested, but close enough.while result.%Next(.sc) {$$$ThrowOnError(sc)}
go to post Rubens Silva · Aug 7, 2017 Not sure if I understood what you meant, but I'll try to answer:When using class methods you cannot reference your THIS instance. Have you every used Java, C# or whatever programming language that supports object oriented-programming paradigms? If so then class methods are the same as static methods.Common (non-static) methods have the advantage of providing you the instance context. Which means that all properties are accessible inside this kind of method, even the private ones. Class Sample.Person Extends %Persistent { Property Name As %String; ClassMethod GetPersonName(instance As Sample.Person) { return instance.Name // This works fine, GetPersonName is receiving an external Sample.Person. } // 'My' is used to empathize that this method refers to it's own instance name. Method GetMyName() As %String { return ..Name // This also works fine. Notice that this method doesn't receives the instance, it's because you can only call it from a instance already. } ClassMethod GetMyInstanceName() As %String { return ...Name // This won't work. Since there's no context (instance). The compiler will even warn about it. } }
go to post Rubens Silva · Aug 4, 2017 No, there isn't.What he meant is that Caché supports binding for other languages. But there isn't a native COS implementation for PGP.The easiest way of achieving what you want is to use gpg using $zf (CallOut). Where you can emit a comand directly to the host OS.Remember that by default Windows haven't a gpg command. But there's a version for it as well.You can also use method ##class(%Net.Remote.Utility).RunCommandViaZF(cmd,,.output,,) for brevity. Where cmd should be your gpg command.
go to post Rubens Silva · Aug 4, 2017 %request.Content will provide you the raw string (or stream) contained on your request payload.
go to post Rubens Silva · Aug 3, 2017 The hard-coded offset does not come from a variable. A lack of comments in the code makes it very difficult to understand and debug.No wonder... Maybe you could use Studio or Atelier debugger to verify their values. Unless you need that log file of course.Maybe you could also use break instead, just before the routine ends. And zwrite the context variables manually within the terminal.I still don't know exactly what you want to achieve neither what are your limitations, I only know that you want to see the content from LINE, so I might be speaking something unnecessary.
go to post Rubens Silva · Aug 3, 2017 Since you said it's arbitrary, where did you get that +3? Is that a constant or variable value?Maybe if you update your line to:ZBREAK *LINE:"T"::"ZWRITE LINE(I+C)"Where C should be your other arbitrary data (but provided as variable as well).
go to post Rubens Silva · Aug 3, 2017 Yeah, I had fixed it but didn't posted the update and then started working on output, sorry.
go to post Rubens Silva · Aug 3, 2017 I finally got it to work as I desired. Here's the source code, take a look on output routine:This will:Limit usage of ! to one per write.Prevent initial write ! as I don't want to skip any line on the beginning.Display compiler messages correctly.Prevent from writing new lines for empty buffers. Include portutilsClass Port.SourceControl.ExtendedHooks [ Abstract ]{ClassMethod Call(sourceControl As %Studio.Extension.Base,hookName As %String = "",parameters... As %String) As %Status [ Internal, ProcedureBlock = 0 ]{ new sc, content, implementer, alreadyRedirected, currentMnemonic, childSC, expectingContent, firstLine set sc = $$$OK set childSC = $$$OK set implementer = ##class(Port.Configuration).GetExtendedHooksImplementer() set alreadyRedirected = ##class(%Device).ReDirectIO() set expectingContent = 0 set firstLine = 0 if '##class(%Dictionary.CompiledMethod).%ExistsId(implementer_"||"_hookName) return sc set content = ##class(%Stream.GlobalBinary).%New() if implementer '= "" { write !, "Port: "_$$$FormatMsg("Port Log Messages", $$$RunningCustomHook, hookName, implementer) try { set currentMnemonic = "^"_##class(%Device).GetMnemonicRoutine() use $io::("^"_$zname) do ##class(%Device).ReDirectIO(1) set sc = $classmethod(implementer, hookName, sourceControl, parameters...) } catch ex { set content = "" set sc = ex.AsStatus() } } if alreadyRedirected { do ##class(%Device).ReDirectIO(1) use $io::(currentMnemonic) } if $isobject(content) { do content.OutputToDevice() } write ! if $$$ISOK(sc) { write "Port: "_$$$FormatMsg("Port Log Messages", $$$HookReturnedOK, hookName) } else { set errorText = $System.Status.GetOneStatusText(sc) write "Port: "_$$$FormatMsg("Port Log Messages", $$$HookReturnedError, hookName, errorText) set childSC = sc set sc = $$$PERROR($$$FailedWhileRunningExtendedHook, hookName) set sc = $$$EMBEDSC(sc, childSC) } return sc rchr(c) quitrstr(sz,to) quitwchr(s) do output($char(s)) quit wff() do output($char(12)) quitwnl() if firstLine = 0 set firstLine = 1 else set firstLine = -1 do output($char(13,10)) quitwstr(s) do output(s) quitwtab(s) do output($char(9)) quitoutput(s) // Skips writing the first !, we leave it to our write. if firstLine = 1 quit // Remaining writes ! are always a standalone buffer so we can check it's equality. if s = $c(13,10) { // However we can only write if the the next buffer has indeed some content. // So we defer it to the next call where we can actually assert it. set expectingContent = 1 // This catches writes with embedded CRLF (like the compiler ones). } elseif $extract(s, 1, 2) = $c(13,10) { set expectingContent = 1 do output($replace(s, $c(13,10), "")) set expectingContent = 0 quit } elseif $length(s) > 0 { // After deferring it, we can finally write a CRLF and the content, as long as it's not empty. if expectingContent = 1 { set expectingContent = 0 do content.WriteLine() do content.Write($$$FormatText("Port (%1): ", hookName)) } // Writes without ! must be written on the same line. do content.Write(s) } quit}}
go to post Rubens Silva · Aug 2, 2017 if $isobject(content) { do content.Rewind() while 'content.AtEnd { set ^ck($i(i)) = content.ReadLine() } do content.OutputToDevice() do content.Rewind() do content.MoveTo(content.Size -3) do content.ReadLine(,,isEOL) } ^ck(1)="" <-- This is because write !, "First line"^ck(2)="Port (OnAfterSave): First lineSecond line" <-- Though First line is actually pushed down and merged with "Second line"^ck(3)="Port (OnAfterSave): Third line" <-- Third works fine, because second finishes with !^ck(4)="Port (OnAfterSave): " <-- write "Third line", ! creates a blank line though my prefix is displaying.I'm starting to think there's no way to prevent all situations.