go to post Sean Connelly · Apr 18, 2017 Does this help...http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_vjob
go to post Sean Connelly · Apr 18, 2017 Here is an expanded example... Method logProps(parent) As %String [ CodeMode = objectgenerator ]{ set x=%code for i=1:1:%compiledclass.Properties.Count() { #dim p As %CompiledProperty set p=%compiledclass.Properties.GetAt(i) set name=p.Name if $extract(p.Name)'="%" do x.WriteLine(" do ..logProp("""_p.Name_""",.."_p.Name_")") }}Method logProp(propName, propValue){ // call your macro logger here, or replace call to logProp with your macro above}Let's say you had a property... Property firstName As %String;When you compile the class, your logProps method would look like this... zlogProps(parent) public { do ..logProp("firstName",..firstName) }There is an article on DC that explains method generators in more depth..https://community.intersystems.com/post/exploring-code-generation-cach%C3%A9-method-generators
go to post Sean Connelly · Apr 18, 2017 Hi Alexandr,If property is in the context of this class then you could tryset value=$property($THIS,propName)If you make the code blocks outer method a generator, e.g. [ CodeMode = objectgenerator ]then you can bake the property accessor into the underling INT code, e.g.do %code.WriteLine(" set value=.."_propName)this approach means you don't have to make any repetitive IO calls to dictionary at run time.If you need an expanded example then I can bash something out.Sean.
go to post Sean Connelly · Apr 18, 2017 Hi Tomas,Its a good point, but performance doesn't have to be an issue.In the main, the lint tool would only need to check the class that is being edited and compiled at that time. If coded optimally (e.g. memoize dictionary lookups etc) then is should only add milliseconds to a compilation cycle. This would hardly be noticed by the developer.There is a use case where the edited class could break implementations of that class. In this instance a "lint all" process could be hooked into various other non disruptive steps, such as running a full unit test.Losing milliseconds at compile time is a small trade off to the collective time lost on supporting these types of errors.
go to post Sean Connelly · Apr 15, 2017 All of the uses cases are around the implementation of instance methods and static methods.In all cases class A either extends or uses class B. For class A to compile, class B must be present and compiled first, no matter what module or namespace it belongs to. If class B implements a generic interface, then all variances of class B should adhere to the same interface (despite being a manual verification check at the moment).The types of examples you have raised sound like you are hot calling functional code, in which case I agree it would be very hard to lint this style of coding.
go to post Sean Connelly · Apr 14, 2017 Hi Clark,Can you provide a more concrete example.In particular, why would mappings change the formal spec or return type of a method.Are you using mappings in some kind of environmental polymorphism?For me the implementation should never be affected by mappings. Mappings are just a convenience, not a logical influence.I'm struggling to understand the limitation that you are suggesting?
go to post Sean Connelly · Apr 13, 2017 wow, that's interesting, never seen it used in methods that way, I guess its one way to work around the command errorso as a fringe case, if the lint tool found a $quit in a method then it would assume the developer knows what they are doing and ignore the whole method, that way no false postives
go to post Sean Connelly · Apr 13, 2017 That's very true, rawContent is limited to 10,000 characters.If you have messages that are larger then you could do it this way... ClassMethod DisplaySegmentStats(){ write !!,"Segment Statistics...",!! &sql(declare hl7 cursor for select id into :id from EnsLib_HL7.Message) &sql(open hl7) &sql(fetch hl7) while SQLCODE=0 { set msg=##class(EnsLib.HL7.Message).%OpenId(id) set raw=msg.getSegsAsString(id) for i=1:1:$l(raw,$C(13))-1 { set seg=$p($p(raw,$c(13),i),"|") set stats(seg)=$G(stats(seg))+1 } &sql(fetch hl7) } &sql(close hl7) zw stats}
go to post Sean Connelly · Apr 13, 2017 I'm still trying to get my head around this.Are you saying that developers actively use $quit inside method code?
go to post Sean Connelly · Apr 13, 2017 > QUIT from inside a loop is considered quitting a loop rather then a function, so it should always be without a value.100% agreed.My point was if the compiler will go as far as protecting the developer from this type of quit misshap, then could the compiler not also warn on potential quit <COMMAND> errors.
go to post Sean Connelly · Apr 13, 2017 It does look interesting. I might find a few of the rules a little querulous, but there are a some gems in there, this ones priceless :)Property of type %String without a MAXLENFor what I want a 100 line linter would suffice for studio output. But I can see value in cachéquality if I was back managing a large team of developers again.
go to post Sean Connelly · Apr 13, 2017 Various Options...1. Call out to Java on the command line using $ZFhttps://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY...2. Access POJO's directly using Jalapeñohttps://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY...3. Consume a web service using the Cache soap wizard...https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY...4. Publish a web service from Cache...https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY...
go to post Sean Connelly · Apr 13, 2017 Code reviews is a great suggestion. Having a style guide is a good supplement to this.I was wondering if anyone had attempted a community driven style guide yet?As an example, I have adopted this style guide for all of my JavaScript development...https://github.com/airbnb/javascript
go to post Sean Connelly · Apr 13, 2017 Hi James, Nothing simple that I can think of (perhaps DeepSee?). Alternatively, I normally bash out a few lines of code, something like this... ClassMethod DisplaySegmentStats() { write !!,"Segment Statistics...",!! &sql(declare hl7 cursor for select rawContent into :raw from EnsLib_HL7.Message) &sql(open hl7) &sql(fetch hl7) while SQLCODE=0 { for i=1:1:$l(raw,$C(13))-1 { set seg=$p($p(raw,$c(13),i),"|") set stats(seg)=$G(stats(seg))+1 } &sql(fetch hl7) } &sql(close hl7) zw stats }
go to post Sean Connelly · Apr 12, 2017 Yes, excellent addition!Rubber ducking is a great tool, and if no one is around then I will actually talk the problem out loud to myself. There is something about the vocal feedback loop to the brain that really helps. I've even asked my two dogs, to very curious looks lol.
go to post Sean Connelly · Apr 12, 2017 Not from the try block, but you could add a break to your catch block.
go to post Sean Connelly · Apr 12, 2017 Just wondering if you are missing a trick here.If you are using try catch then your catch will return %Exception.AbstractException, this has a Location property.
go to post Sean Connelly · Apr 12, 2017 You could create a macro that conditionally compiles the break points into your code based on the existence of a global variable.
go to post Sean Connelly · Apr 12, 2017 Sounds like you might be adding unnecessary complexity.> What is the most efficient way to process this large file?That really depends on your definition of efficiency.If you want to solve the problem with the least amount of watts then solving the problem with a single process would be the most efficient.If you add more processes then you will be executing additional code to co-ordinate responsibilities. There is also the danger that competing processes will flush data blocks out of memory in a less efficient way.If you want to solve the problem with speed then its important to understand where the bottlenecks are before trying to optimise anything (avoid premature optimisation).If your process is taking a long time (hours not minutes) then you will most likely have data queries that have a high relative cost. It's not uncommon to have a large job like this run 1000x quicker just by adding the right index in the right place.Normally I would write a large (single) process job like this and then observe it in the management portal (System>Process>Process Details). If I see its labouring over a specific global then I can track back to where the index might be needed.You will then get further efficiencies / speed gains by making sure the tables are tuned and that Cache has as much configured memory cache as you can afford.If you are writing lots of data during this process then also consider using a temporary global that won't hit the transaction files. If the process is repeatable from the file then there is no danger of losing these temp globals during a crash as you can just restart the job after the restore.Lastly, I would avoid using Ensemble for this. The last thing you want to do is generate 500,000 Ensemble messages if there is no need to integrate the rows of data with anything other than internal data tables.Correction. It's perfectly fine (for Ensemble) to ingest your file and process it as a single message stream. What I wouldn't do is split the file into 500,000 messages when there is no need to do this. Doing so would obviously cause additional IO.