Rubens Silva · Aug 2, 2017 go to post

  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.

Rubens Silva · Aug 3, 2017 go to post

Yeah, I had fixed it but didn't posted the update and then started working on output, sorry.

Rubens Silva · Aug 3, 2017 go to post

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.

Rubens Silva · Aug 4, 2017 go to post

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.

Rubens Silva · Aug 7, 2017 go to post

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.
}
}
Rubens Silva · Aug 7, 2017 go to post

%Next does receives a %Status as output. It's not exactly what you suggested, but close enough.
while result.%Next(.sc) {

$$$ThrowOnError(sc)

}

Rubens Silva · Aug 7, 2017 go to post

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)

}
Rubens Silva · Aug 8, 2017 go to post

Since ZEN runs under CSP, maybe %session.AppTimeout = 900 // 15 mins.
Just remember to change it inside OnPreHTTP.

Rubens Silva · Aug 8, 2017 go to post

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;
Rubens Silva · Aug 8, 2017 go to post

a homemade tool

Wow, that's unexpected. Care to share some details about that tool?

Rubens Silva · Aug 8, 2017 go to post

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.

Rubens Silva · Aug 8, 2017 go to post

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.
}

Rubens Silva · Aug 9, 2017 go to post

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.

Rubens Silva · Aug 10, 2017 go to post

Hello Jon.

Please note that the large JSON is not problem itself, but a specific field instead.
I'm already avoiding a round trip by outputting to the device instead of using streams. 
As a side note, I noticed that parsing/serializing using %Dynamic API is much faster and more efficient than %ZEN.proxyObject.

So I'd prefer keep using it if possible. But since you said that Caché cannot get such values I suppose I can advice the user to enable long strings. I don't think any property content could even get close to 3GB.

Rubens Silva · Aug 10, 2017 go to post

Thanks for pointing that out.
PUBLIC=1 is an inheritance from another project with support for 2010 that worked almost the same way. At that time there wasn't a way to determine whether the method should be available to be called via HTTP or if it should be blocked, so I implemented that security flag.
Nowadays, as %CSP.REST uses UrlMap to restrict callable methods, you can consider using PUBLIC as deprecated. My bad.

Rubens Silva · Aug 10, 2017 go to post

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.

Rubens Silva · Aug 10, 2017 go to post

You're right, it's not 3GB. Yes, objects containing base64 binary can be troublesome.
For now a workaround I would suggest is to split the string into chunks and put them inside an array, that way you can use .join('').

Rubens Silva · Aug 11, 2017 go to post

Good! Now can IS do something about shrinking replies?
I also find it difficult to remember that the reply cannot be  marked as ANSWER.
Most of times I end up clicking on "Add comment" instead of creating a reply that could be marked as answer.

Rubens Silva · Aug 29, 2017 go to post

Sadly I haven't the code with me anymore. because it was a a feature implemented on another branch, that's why I posted a Gist instead. I used it to preserve the important implementation parts since I deleted the branch. Check my post again to understand better regarding the placeholders.
Port.SourceControl.Extension.VCS is quite simple actually, %OnNew initialize two temp files path that serve as output and error feedback, inheriting classes use those files as parameters along with >.

Rubens Silva · Aug 29, 2017 go to post

Wouldn't it require all methods to implement the device logic? I guess that's why he asked for device redirection.

Rubens Silva · Aug 30, 2017 go to post

It doesn't, at least not directly. I used it for boosting the comparison speed by using git diff.

The idea as a whole was:
diff - whenever the user selected the "Import" from the Source Control menu, it would run git diff instead, if git was installed and the current project is versioned (has .git).
add - automatically add modified and new files to the staging area whenever the item is saved on Studio.
I ended up dropping those ideas because I noticed I was wasting resources as it need to be less exploitable and it also went against my objective of creating something agnostic to version control systems.
If you want to see the result, here's the project in question.

Rubens Silva · Sep 4, 2017 go to post

Greetings everyone.
I have decided to publish the current version for your pleasure.
I'm now working on implementing SQL support and access policies. But you can already check it out.
Remember to provide some feedback here or using the issues if you feel like it.
Also, if you want to contribute, feel free to do so.
Best regards.

Rubens Silva · Sep 4, 2017 go to post

Cache for Windows (x86-64) 2017.1 (Build 792U) Mon Mar 20 2017 19:13:14 EDT

Rubens Silva · Sep 5, 2017 go to post

Interesting, can you expand this subject a bit or point the documentation? I'm also interested about how to capture user input using the Studio's output.

Rubens Silva · Sep 6, 2017 go to post

No, it's a proof-of-concept, my purpose was to discover a way to express the minimal features needed to develop in a functional fashion.
You can use it with real projects and it will work as it should, the only real issue might be regarding the performance in a long term, since this library uses %ConstructClone to keep the monoids pure.

Rubens Silva · Sep 6, 2017 go to post

I would like to. But I need some clue about how to isolate monoids from the global scope, this would push the development a lot further. I mean... it would be wonderful if subroutines could be passed as parameters, you know. But this is out of my reach yet.
For now what I could do is to add more methods to handle different type of datas.

Rubens Silva · Sep 6, 2017 go to post

Hello.
A new version will be published soon. This version has support for using streams properties and SQL along with %Dynamic instances.
As these features reach stable versions, I'll be publishing a tutorial about how to use Frontier.
If you want to dive deep into it, I recommend taking a look on the router below. It's really self-explanatory.
https://github.com/rfns/frontier/blob/sql/cls/Frontier/UnitTest/Router…
If you import the class WebApplicationInstaller you'll be able test these features by navigating to localhost:57772/api/frontier/test/[one of routes from the router above].
P.S.: Sorry for the bump.

Rubens Silva · Sep 8, 2017 go to post

JavaScript is evolving day by day and really, really fast. Check what ECMAScript 6 and even 7 to see what it's becoming.
COS could evolve faster as well if there was some kind of COS API for parsing and rewriting the language itself. Something that could transpile features unknown to the current COS language. Just like BabelJS does.
However COS is proprietary, and just like Linux and Windows, open-source projects  evolve faster due to the community involvement. I don't mean to make it fully open, but provide the community a way to interact with it by providing with new features proposals.