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.
- Log in to post comments
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.
Hi Timur! Your short answer is funny, and your longer answer makes sense (even though my preferences are different).
This is not quite an answer to your question. Just some clarifications:
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.
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?
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.
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).
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.
Brendan's comment from the other thread summarizes it nicely. Here's a little more...you have 2 good alternatives to parent-children:
Also note:
James, for this one, there is no easy answer. But first, here are some corrections FYI:
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.
As Eduard said, use a custom resource. Now it depends on what you want to do:
Hope that's clear.
There is no Merge in the Native API. Maybe in a future release?
Why are you using NoExtent here?
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.
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.
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.
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".
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.
I'm sorry to mislead; ReturnResultSets doesn't make a difference after all. I played around a little more, calling a simple stored procedure from a JDBC client (DBeaver).
Select Sample.Employee_StoredProcTest('jj') returns the result, and ignores anything in %sqlcontext unless you throw an exception, and the %sqlcontext properties are nested.
Call Sample.Employee_StoredProcTest('jj', '') shows the un-nested %SQLCode and %Message properties of %sqlcontext, but doesn't return the result.
So maybe the solution is to return tMsg.Size by reference and use Call?
Not sure exactly what you're asking for...
You can add additional properties to your custom unit test class (inheriting from %UnitTest.TestCase) and use them to share data between the methods in the class. But this doesn't show up automatically in the results.
You can use the $$$LogMessage macro to display whatever you want in the results, but this is just text, not a new property.
do $$$LogMessage(key _ ":" _ value)
This is a very good article. I have 2 comments:
tstart with either a tcommit or trollback 1. So the difference between $tlevel and tInitTLevel should always be 1. And no code should ever use trollback without the 1. Your code should roll back your transaction and no one else's.tstart and tcommitat the beginning of the code that is about to change globals, keeping it isolated as much as possible from anything else. I’d suggest moving the tstarts in your examples lower in the code block, right before the “do the important stuff” code.I've changed my recommendation on this a little. Return the result of %ValidateObject() as the final argument, but don't return that status as the return value of the method. That way, if there are any required properties, and the call to %New() doesn't supply them, %OnNew() still works. Here's the updated example:
/// constructor
Method %OnNew(name As %String = "", phone As %String = "", dob as %Date, Output valid As %Status) As %Status [Private]
{
set valid = $$$OK
set ..Name = name
set ..Phone = phone
set ..DOB = dob
set valid = ..%ValidateObject() // validate the new object
return $$$OK
}Here is some information about debugging CSP pages using Studio. I actually just taught this to students in class yesterday!
1. You can't set a breakpoint inside the ObjectScript on a CSP page (inside a <script> tag). But the workaround for this is to use the View > View Other Code and load the class definition that's generated from the CSP page. Then you can set breakpoints normally.
2. Once you've set breakpoints, you can set your CSP page as the Debugging Target, and launch it directly from Studio. The page runs in a special "debug mode". This means that the normal timeouts (typically 60 seconds) are suspended, so that the browser and the CSP Gateway will wait as long as necessary while you step through the code.
You can step through the code that is called when the page is first being built, and you can also step through code that is called via a hyperevent.
The only issue with stepping through the code is that Studio does not seem to be highlighting the current line as it normally does. I think this used to work fine, so I may bring this up with the developers. But a workaround for now is to use the "Call Stack" tab of the Watch Window. The top line of the call stack shows the label+offset of the current line. As you step, the top line is updated with the current label+offset. You can simply click the top line whenever you want and the Code Window will scroll to that line, also showing you the current values of the variables.
It is possible to programmatically add a relationship to two persistent class definitions at runtime, and then compile those classes. That gives you the same result you'd get if you had added the relationship to the class definitions at design time. So I don't think that's what you want.
The term "Relationship" as defined in Caché means "objects of these classes can be linked at runtime, and this relationship will be stored when the objects are saved." So your need to create relationships between persistent objects "as and when they're required" doesn't really match up with this definition. Either a persistent class is in a relationship with another persistent class, or it isn't. It's not possible to have some objects of the class without the relationship definition, and other objects of the class with the relationship definition.
Maybe you just need to substitute one-many relationships for all of your parent-children relationships. One-many relationships are independent; the relationship is not required like it is in parent-children relationships. In v2013.1 and later, you can set the OnDelete action of the one-many relationship to "Cascade" so you get delete behavior similar to parent-children.
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.
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.
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")%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
}