Timothy Leavitt · Oct 28, 2021 go to post

Agreed - this feels like the biggest gamechanger among Virtual Summit announcements since we've called it Virtual (or Global) Summit.

Timothy Leavitt · Oct 13, 2021 go to post

One note here, $$Quote^%qcr will represent control characters with $c(decimalAsciiCode) syntax - like the newlines in this case. I think there's some more official classmethod that's equivalent, but I don't recall where it is off the top of my head.

Timothy Leavitt · Oct 13, 2021 go to post

Hi Erica,

$$$Text generates content into the message globals at compile time. Here's one way to solve the problem:

Class Erica.DemoLocalizedXData
{

Parameter DOMAIN = "Demo";

XData LocalizedEmail [ MimeType = text/html ]
{
<body>
<p>
Text to be translated into another language
</p>
</body>
}

ClassMethod GetLocalizedContent(xDataName As %String) As %String [ CodeMode = objectgenerator ]
{
    do %code.WriteLine(" Quit $Case(xDataName,")
    set key = ""
    for {
        set xdata = %class.XDatas.GetNext(.key)
        quit:key=""
        set data = xdata.Data.Read() // Assumptions about length here...
        do %code.WriteLine("   "_$$$QUOTE(xdata.Name)_":$$$Text("_$$Quote^%qcr(data)_"),")
    }
    do %code.WriteLine("   :"""")")
}

}

After compilation you'll have:

^IRIS.Msg("Demo")="en"
^IRIS.Msg("Demo","en",3630108798)="<body>"_$c(13,10)_"<p>"_$c(13,10)_"Text to be translated into another language"_$c(13,10)_"</p>"_$c(13,10)_"</body>"_$c(13,10)

If you want to localize individual strings in the XData block independent of the HTML markup that gets a little more complicated. I'd think it's simpler/possibly better to localize the entire block at once though.

Timothy Leavitt · Oct 11, 2021 go to post

Neat trick for the expression for r! (Which I hope to never see again outside of the context of contests like this. :))

Timothy Leavitt · Oct 8, 2021 go to post

Gaming the system and using the code provided to check the result length, 3:

ClassMethod AddWater(p As %String, t = {$e(p)}, r = {$case(p,"":"",:..AddWater($e(p,2,*)))}, f = {$Select(t:$tr($j(t,t)," ",t)_r,1:r)}) As %String [ CodeMode = expression ]
{
f
}
Timothy Leavitt · Oct 8, 2021 go to post

Edit: down from 73 to 67 to 58 (but still looking for better...)

Missing an important test case:
Do $$$AssertEquals(##class(CodeGolf.MagicTowel).AddWater("00020"), "22")

Timothy Leavitt · Aug 23, 2021 go to post

@Craig Regester I'd be really interested to see what you're doing with the server hooks. (Maybe @Raj Singh would be interested too?) It sounds like you're rolling your own; what are you integrating with? Git, Perforce, SVN, etc.?

A few tidbits that may or may not be relevant for your use cases:

%Studio.Extension.Base:UserAction (see https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic…) with Action=3 will also launch an external browser given a full URL provided in Target (not necessarily CSP, but could be). This isn't truly a "modal" window but would give you a little bit more control perhaps.

For the sake of VSCode specifically there's a different API (not really documented) for closing a page opened with Action = 2 - specifically, sending a message to the parent frame (a VSCode web view) with data {"result":"done"}. This looks something like (in %CSP.Page:OnPage):

    If (%request.Get("EndTemplate") = 1) {
        If (%request.UserAgent [ " Code/") {
            // For VSCode only:
            Set %response.ContentType="text/html",%response.CharSet="UTF-8"
            Write "<html>",!
            Write "<script type=""text/javascript"">",!
            &js<if (window.parent) {
                window.parent.postMessage({"result":"done"},'*')
            }>
            Write "</script>",!
            Write "</html>",!
        } Else {
            Set %response.ContentType="text/xml",%response.CharSet="UTF-8"
            Set delim = ##class(%CSP.StudioTemplateError).#DELIM
            Write "<?xml version=""1.0""?>",!
            Write "<template><![CDATA[BODY",delim
            Write delim,"]]]]><![CDATA[></template>",!
        }
        Quit $$$OK
    }
Timothy Leavitt · Aug 17, 2021 go to post

@Evgeny Shvarov, the power of generators and projections is more in the creation of frameworks/tools that speed up building solutions. This includes many parts of IRIS itself, but moves into the user space for larger/more complicated applications (e.g., what we've done with AppS.REST both for REST-enabling older ObjectScript-based applications and for quickly building out new applications using IRIS and modern web technologies).

Timothy Leavitt · Aug 16, 2021 go to post

If only da Vinci had provided explicit documentation (preferably written above the painting, in Latin) about how to maintain it going forward...

Timothy Leavitt · Aug 16, 2021 go to post

@Evgeny Shvarov anything with CodeMode=objectgenerator, typical uses of projections, anything that works with %Dictionary.(.*Definition|Compiled.*), anything that works directly with %Library.Routine / %Library.RoutineMgr... there are examples all over the place, including throughout both of my Open Exchange projects.

https://openexchange.intersystems.com/package/Test-Coverage-Tool
https://openexchange.intersystems.com/package/apps-rest

These don't go to the level of dynamically modifying/instrumenting code, but another of my projects (a mock framework for ObjectScript, still internal to InterSystems) does.

Timothy Leavitt · Aug 6, 2021 go to post

I've found writing applications INSIDE the DB environment to be the single coolest thing about InterSystems' technology. The code itself being stored in the database and accessible through both object and relational modes (just like the actual data) makes metaprogramming a really natural part of the language.

Timothy Leavitt · Jul 9, 2021 go to post

Internally, we've developed a mock framework for ObjectScript based on Mockito that can do exactly this (and many other things). If we're able to get it out on the Open Exchange at some point I'll let you know.

Timothy Leavitt · Jun 24, 2021 go to post

I'd recommend using browser developer tools to inspect the CSS and see what's causing it - I wouldn't expect the rows to be particularly tall, but there could be stray CSS from somewhere else that is making them so. See docs on using Chrome's developer tools (for example - but they're all similar these days) here: https://developer.chrome.com/docs/devtools/css/

Timothy Leavitt · Jun 23, 2021 go to post

I'd expect that to work provided the id (not name) of the combobox/dataCombo is 'MyCombo'. e.g., this works fine:

Class DC.Demo.FindElement Extends %ZEN.Component.page
{

XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ]
{
<page xmlns="http://www.intersystems.com/zen">
<form>
<tabGroup showTabBar="true">
<tab caption="Tab One">
<combobox id="MyCombo" editable="true" />
</tab>
<tab caption="Tab Two">
</tab>
</tabGroup>
</form>
<button onclick="zenPage.alertComboValue()" />
</page>
}

ClientMethod alertComboValue() [ Language = javascript ]
{
    alert(zen('MyCombo').findElement('input').value);
}

}
Timothy Leavitt · Jun 21, 2021 go to post

Just to add - I'm successfully using both source control and non-source control extensions (all using the %Studio.Extension framework) from VSCode. If there are specific issues you see, I'd be curious to hear about them.

Timothy Leavitt · Jun 10, 2021 go to post

@Utsavi Gajjar in case you end up reaching out to InterSystems Support about this (which I'd recommend if you're blocked on the issue), the internal Jira reference for the wrong level of %DynamicObject being passed to %JSONNew is DP-406169

Timothy Leavitt · Jun 10, 2021 go to post

This is a little messy and I'm going to report part of the answer as a bug internally. But regardless, here's one way to make it work - in short, have all of the things that could be listed as a recipient extend a common parent class, and in that class override %JSONNew to detect which type it is.

Class DC.Demo.Container Extends (%RegisteredObject, %JSON.Adaptor)
{
Property recipient As DC.Demo.Recipient;
ClassMethod Demo()
{
    for json = {"recipient":{"dob":"2021-06-10"}}, {"recipient":{"reference":"foo"}} {
        set inst = ..%New()
        do inst.%JSONImport(json)
        write !,json.%ToJSON(),!,$classname(inst.recipient),!
    }
}
}

Class DC.Demo.Recipient Extends (%RegisteredObject, %JSON.Adaptor)
{
/// Get an instance of an JSON enabled class.<br><br>
/// 
/// You may override this method to do custom processing (such as initializing
/// the object instance) before returning an instance of this class.
/// However, this method should not be called directly from user code.<br>
/// Arguments:<br>
///     dynamicObject is the dynamic object with thee values to be assigned to the new object.<br>
///     containerOref is the containing object instance when called from JSONImport.
ClassMethod %JSONNew(dynamicObject As %DynamicObject, containerOref As %RegisteredObject = "") As %RegisteredObject
{
    // This is weird: shouldn't need to reference .recipient here
    if dynamicObject.recipient.%IsDefined("dob") {
        quit ##class(DC.Demo.Patient).%New()
    } elseif dynamicObject.recipient.%IsDefined("reference") {
        quit ##class(DC.Demo.Reference).%New()
    } else {
        quit ..%New()
    }
}
}

Class DC.Demo.Reference Extends DC.Demo.Recipient
{
Property reference As %String;
}

Class DC.Demo.Patient Extends DC.Demo.Recipient
{
Property dob As %Date;
}

Output is:

 d ##class(DC.Demo.Container).Demo()
{"recipient":{"dob":"2021-06-10"}}
DC.Demo.Patient
{"recipient":{"reference":"foo"}}
DC.Demo.Reference

Only problem is, %JSONNew (as advertised in class reference documentation) should get the %DynamicObject representing the object itself, not the parent %DynamicObject. This would only really work if each type is used in exactly one context like this, which seems unlikely.

Timothy Leavitt · May 19, 2021 go to post

First off, it's generally best to avoid xecute. ;)

In the first case, routine is private (it isn't visible in the xecute stack frame). In the second, it's public, so it is visible there.

You could get the best of both worlds with:

ClassMethod Run()
{
   set routine="variable"
   set call="(routine) write routine,!"
   xecute (call, routine)
   quit
}

For more info see https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KE…