Joel Solon · Dec 23, 2019 go to post

%OnNew() must return a %Status to %New(). But the code that calls %New() also needs the %Status. I think a "best practice" is simply to add an Output  %Status argument to %OnNew() that will therefore be returned to %New(). So the %Status is being returned using return (for %New()) and by using a pass-by-reference argument (for the code that calls %New()).

/// constructor
Method %OnNew(name As %String = "", phone As %String = "", dob as %Date, Output st As %Status) As %Status [Private]
{
    set st = $$$OK
    set ..Name = name
    set ..Phone = phone
    set ..DOB = dob
    set st = ..%ValidateObject() // validate the new object
    return st
}
Joel Solon · Nov 18, 2019 go to post

Since transactions can be nested, and TROLLBACK rolls back all transactions, it's best practice to pair each TSTART with TROLLBACK 1, which will rollback only the currenct transaction.

Joel Solon · Nov 18, 2019 go to post

In the example for READ UNCOMMITTED, after the second (right-hand) Terminal session sets ^t(1)=2, when the first (left-hand) Terminal session writes ^t(1), the example shows/states that a "3" appears, but that's wrong; it should be a "2".

Joel Solon · Oct 28, 2019 go to post

I started as an M guy, abbreviating everything, for many years. Once extra spaces were allowed in ObjectScript, I re-trained myself to use extra spaces and fully spelled out commands and $functions. When I'm writing code for myself in the Terminal, I don't always spell everything out. But any code I write that others will see, I spell everything out.

Some folks in this thread talk about abbreviating some commands but not others, or some $functions and not others. Imagine code at one company written by 10 programmers, all of whom choose their own way of abbreviating or not abbreviating. A new programmer joining the company will see a mess. Then imagine trying to come up with an agreed upon set of standards at the company ("you can abbreviate these commands but not those, and you can abbreviate these functions but not those") so that the code appears (at least) consistent. A new programmer joining the company will still see a messy standards document that they'll have to keep checking until they have it memorized.

Compare that to: "spell everything out, spaces around all operators, a space after every comma" (what I do) or something similar. I think the end result justifies the extra typing.

Joel Solon · Oct 3, 2019 go to post

If you want to allow subclasses of class A with a property that references Abstract %Persistent Class B, and allow A objects to use that property to reference any of the subclasses of B, class B must have its own extent (all subclass data stored in the same global). Otherwise, since the A objects only store the ID of the referenced B objects, how would the system determine which subclass of B ID #52 refers to? When all the subclass data is stored in the same global, IDs are unique across all subclasses, and ID #52 refers to one object only.

Adding a BitMap extent index to class B helps compensate for the fact that all B objects are stored together, and improves the performance of queries on the subclasses.

Joel Solon · Oct 3, 2019 go to post

OK. In your original post, you wrote "storing data in separate globals for each class" which made me ask my question. You should probably edit that to be "storing data in separate globals for each subclass".

And, yes, that is the correct way to achieve that.

Joel Solon · Jul 29, 2019 go to post

As Eduard said, use a custom resource. Now it depends on what you want to do:

  • If you want to restrict certain Portal pages to a subset of users, create a custom resource (R1 for this example), add it to the pages you want the subset of users to access, and then add Use permission on the R1 resource to an existing or new role. Only users that are members of that role will have access to the pages.
  • If you want to restrict certain users to a subset of Portal pages (your question), create a custom resource (R2 for this example), add it to all the pages the users should not access, and then add Use permission on the R2 resource to an existing or new role. All other users should be in this role, and they will have access to all the pages, as they did before these changes. Any pages that aren't protected by R2 will be the only pages available to the users that aren't in the role. 

Hope that's clear.

Joel Solon · Jul 29, 2019 go to post

James, for this one, there is no easy answer. But first, here are some corrections FYI:

  • Since what you're passing in to execute contains quotes ("SYSTEM"), surround that with single quotes instead of double quotes
  • Since the Security.System class is in %SYS, specify the namespace in your command.

So it's this:

csession TEST -U %SYS '##class(Security.System).AutheEnabledGetStored("SYSTEM")'

But the problem is that this expression doesn't output its value, and you can't include "write" at the beginning. You should probably describe exactly what you're trying to do in more detail, and someone here will make a suggestion.

Joel Solon · Jul 29, 2019 go to post

Security.System is a persistent class with 1 object in it. The ID of the object is "SYSTEM". So this works

set ss = ##class(Security.System).%OpenId("SYSTEM")
write ss.AutheEnabled

The approach above is best if you want to access several properties. But if all you really want is one or two properties of Security.System, you can do this:

write ##class(Security.System).AutheEnabledGetStored("SYSTEM")
Joel Solon · Mar 19, 2019 go to post

Also note:

  • InterSystems IRIS v2019.1 also provides direct access to globals, using a feature called the Native API for .Net. Check the doc for it here
  • The Native API for .Net can connect to InterSystems IRIS on the same machine or another machine. On the same machine, the connection speed between .Net and InterSystems IRIS is even faster than with Caché, through the use of a shared memory connection. Read more about it here.
Joel Solon · Mar 5, 2019 go to post

Brendan's comment from the other thread summarizes it nicely. Here's a little more...you have 2 good alternatives to parent-children:

  • Without using either relationship option, just use a simple reference from the "many/child" class to the "one/parent" class, and add the FK constraint yourself (using "cascade"). This allows bitmap indexes in either class.
  • Simulate parent-children relationship using one-many. After setting up the one-many relationship, in the Relationship definition in the "many" class, add the bold items shown here: [Cardinality = one, Inverse = Orders, Required, OnDelete = cascade]. This allows bitmap indexes in either class.
Joel Solon · Jan 31, 2019 go to post

To me, the answer is Yes. I guess using CompileAfter signals to another developer that the code doesn't have to be runnable at compile time, but DependsOn signals that it does.

Joel Solon · Jan 30, 2019 go to post

From @Simon Player: as an alternate approach for Example #1 above:

I would be inclined to abstract all the  ‘shared’ params to a superclass (or more than one if it makes sense due to conflicts or large number of params), and inherit that class where needed in all the places where is it referenced – then simply use {..#PARAMBBB}

This is easier to manage, more balanced, and the class compiler can take care of the dependencies, and is arguably cleaner than having a number of cross dependencies (assumes there are not too many, and no conflicts in param names).

Joel Solon · Jan 8, 2019 go to post

Evgeny, if I understand you, you're saying that they downloaded the quotes to a CSV file and then they read in the CSV file. I'm now wondering if/how they "chunked" the quotes. Did they read in and store each quote one at a time, or did they read in 1000 quotes into a Java array and then store those quotes, read in another 1000 and store those, etc.

Joel Solon · Jan 7, 2019 go to post

Great post! I have a followup question: How did you load the Finam data? Was it "over the wire" all at once? Or were you reading from one or more csv files?

Joel Solon · Jan 2, 2019 go to post

It sounds like you want to do pattern matching, but for some reason you don't want to use ObjectScript pattern match syntax for your mask. So you've decided that you want to use * for "any number of any character" and ? for "a single character" which are pretty common.

Remember that ObjectScript also has the $match Regex function. So your match() method could use that syntax for the search mask and you wouldn't have to write any code to change * into .E and ? into 1E and you'd get the benefit of additional options offered by $match.

Joel Solon · Oct 31, 2018 go to post

You are correct. EventData is the field to use. Here's an example  query (run from %SYS namespace):

SELECT UTCTimeStamp, EventSource, EventType, Event, EventData, Username, Description
FROM %SYS.Audit
WHERE (UTCTimeStamp BETWEEN '2018-10-27 00:00:00' and '2018-10-27 23:59:59') AND (EventData = '12345') AND (Namespace = 'ABC')
ORDER BY UTCTimeStamp DESC, SystemID DESC, AuditIndex DESC

You probably want to add a WHERE clause on the Namespace column. The docs for the %SYS.Audit class (https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic…) suggest using a WHERE clause on UTCTimeStamp to speed up the search.

Joel Solon · Oct 12, 2018 go to post

I think you are asking: "If I have an object open in one process, and another process updates that object or its corresponding row, what's the best way for the first process to make sure that it has the latest version of the data?"

I recommend using %Reload(). It makes it clearer to another developer what you're actually doing. A quick glance at the %Reload() code makes me think that it would be faster than killing the object and calling %OpenId() to reopen it.

Of course, you might want to make use of Concurrency options so that if you have an object open in one process, other processes are prevented from updating it.

Joel Solon · Sep 5, 2018 go to post

This is not quite an answer to your question. Just some clarifications:

  • Your example is a %Query class query, where you write ObjectScript code for the query rather than using SQL. I haven't written one of those for a while. I'm assuming that ROWSPEC and CONTAINID are required for these types of class queries, but I'm not 100% sure.
  • A "newer" way to write a custom ObjectScript-based class query, using the class %SQL.CustomResultSet, doesn't require ROWSPEC either. Sample.CustomResultSet is an example of this technique.
  • For a %SQLQuery class query, written using SQL, ROWSPEC and CONTAINID have not been required for a loooong time. They are dynamically created from the query itself.
Joel Solon · Aug 23, 2018 go to post

Hi Timur! Your short answer is funny, and your longer answer makes sense (even though my preferences are different). 

Joel Solon · Aug 23, 2018 go to post

This is nice. The only thing I'm wondering about is why there are #dim statements for regular variables? @Timur Safin, why did you include the #dim statements? In my opinion, #dim statements for variables that are not object references are misleading:

  1. #dim gives developers that are new to ObjectScript the impression that you must declare all variables like in some other languages. Declaring variables like this is not necessary in ObjectScript.
  2. #dim's main purpose is to help Atelier/Studio provide code completion for variables that are object references. If you write this code #dim i as %Integer, and then on the next line you type do i. ,  after the period Atelier/Studio will suggest methods from the %Integer class. If you happen to accept one of the suggestions, the resulting code will not compile. This is because our datatype classes (like %String and %Integer) are not object classes.
Joel Solon · Aug 23, 2018 go to post

Yes, Track Variables in Studio is great! But I don't see any value in Option Explicit, which forces #dim, which leads to the issues in my original comment.

Joel Solon · Feb 5, 2018 go to post

Both running VMs and containers allow multiple processes inside. I'm not asking you for a deep dive, but can you explain if the mechanism that allows this is the same or similar between VMs and containers, or if they use completely different mechanisms.

Joel Solon · Jan 31, 2018 go to post

I would do it this way to avoid Else:

Open <some device>:"R":0 If '$test { Write "could not open device" Quit }

Joel Solon · Jan 31, 2018 go to post

Danny and Robert, of course I understand what you are saying (we are the same generation!). Also, I am not trying to be the Developer Community police.

In posts that are asking "what's the recommended way to do this?" we should answer that question. There's no point in mentioning old syntax in that context. Saying "don't do this, but you might see it someday" helps the subset of people that might work with old code, but doesn't help and may confuse the people that are working with new code. It doesn't belong.

On the other hand, if there's a post asking "what are some older/confusing syntaxes I might encounter when working with M or ObjectScript?" we should answer that question. We all have examples of that.

Joel Solon · Jan 31, 2018 go to post

C'mon, let's stop mentioning old features like argumentless Do and legacy ELSE

There's just no point in doing so.

Joel Solon · Jan 31, 2018 go to post

The legacy versions of If and For commands don't use curly braces. But there are no legacy versions of While and Do/While, which require curly braces. So rather than trying to remember which commands use or don't use curly braces, just use curly braces all the time for these, as well as Try/Catch.

Regarding post-conditionals, it's a good habit to put parentheses around the condition, which allows you to put spaces in the condition.

This is valid syntax: set:a=4 y=10

This is valid syntax and more readable: set:(a = 4) y = 10

This is invalid syntax: set:a = 4 y = 10