Eduard Lebedyuk · Apr 21, 2016 go to post

Since project is a collection of classes (and other items, yes, but we're talking about classes here) the  flow would be something like that:

  • Get project name
  • Project name to class list
  • Get unit tests for class list
  • Run unit tests

I'm just saying that 3rd step can have more callers (alternative routes to 3rd step if you will), for example:

  • Package to class list
  • Wildcard expression to class list
  • Namespace to class list
Eduard Lebedyuk · Apr 21, 2016 go to post

I usually add my classes and system classes I often reference from my classes into the project, so they can be opened faster (without dialog).

Eduard Lebedyuk · Apr 21, 2016 go to post

I would recommend you forego projects entirely and iterate over classes directly with %Dictionary package.

Eduard Lebedyuk · Apr 20, 2016 go to post

>$SYSTEM.Status.GetErrorText(sc)["Something"

Would not always work correctly in applications with users requesting content in several languages (for example web app). Why not use error codes?

If $SYSTEM.Status.GetErrorCodes(sc)[$$$GeneralError $$$ThrowStatus(sc)
Eduard Lebedyuk · Apr 20, 2016 go to post

> you could not implement singleton which will be working across jobs, only for this same process;

Storing something in %/PPG would not work across jobs too. Singleton is a single-thread anyway.

> to share instance to teh object you could emply %-named variable or process-private global.

Wrote another singleton using % variable. How would I do it with PPG (any global)?  When you set global to an OREF it actually sets a string: "int@class".  And you could not  convert it back to an OREF with $$$objIntToOref at a later date because the object would be already destroyed.

/// Another singleton
Class Utils.Singleton2 Extends %SystemBase
{

Property Content As %String;

/// Set a = ##class(Utils.Singleton2).Get()
ClassMethod Get() As Utils.Singleton2
{
    #Define Var %Var
    If '$Data($$$Var) || '$IsObject($$$Var) {
        Set Obj = ..%New()
        Set $$$Var = Obj
    } Else {
        Set Obj = $$$Var
    }
    Return Obj
}

/// Do ##class(Utils.Singleton2).Test()
ClassMethod Test()
{
    Set a = ##class(Utils.Singleton2).Get()
    Set a.Content = $Random(100)
    
    Set b = ##class(Utils.Singleton2).Get()
    Write b.Content
}

}
Eduard Lebedyuk · Apr 20, 2016 go to post

How would you have done it? It's 35 lines of code in total, 25 if we remove persistence (because singleton does not actually has persistence, but OP seemed to need it).

Eduard Lebedyuk · Apr 19, 2016 go to post

A very interesting question. I decided to write a singleton, with the idea that it searches process memory for other instances of this class and returns existing OREF if found. It also stores data in global and retrives it from there on a first load:

/// Singleton pattern implementation
Class Utils.Singleton Extends %Library.SystemBase
{

/// Global to store content
Parameter Global = "^Singleton";

/// Actual object content
/// It can be be anything: string or a dynamic object
Property Content As %String;

/// This method finds OREF for an object of a current class if it exists in a current process
ClassMethod GetOref() As %Integer
{
    // Get current classname
    Set Cls = $ClassName()
    Set OREF = $$$NULLOREF
    
    // This query returns a list of all object instances currently in memory within the current process.
    &sql(SELECT Count(OREF),OREF INTO :Count,:OREFTemp FROM %SYSTEM.OBJ_ObjectList() WHERE ClassName=:Cls)
    
    If Count'=0 {    
        Set OREF = OREFTemp
    }
    Return OREF
}

/// If no object of this class is found in process memory
/// then a new one would be returned
/// with Content value taken from global
///
/// If there is an object of this class in memory
/// then it would be returned
ClassMethod Get() As Utils.Singleton
{
    Set OREF = ..GetOref()
    If OREF = $$$NULLOREF {
        Set Obj = ..%New()
    } Else {
        // Convert integer-oref into real OREF
        Set Obj = $$$objIntToOref(OREF)
    }
    Return Obj
}

/// Test singleton object
/// Do ##class(Utils.Singleton).Test()
ClassMethod Test()
{
    Set a = ##class(Utils.Singleton).Get()
    Set a.Content = $Random(100)
    Set b = ##class(Utils.Singleton).Get()
    Write "object b: " _ b.Content,!
    Do a.Save()
    Kill
    Set c = ##class(Utils.Singleton).Get()
    Write "object c: " _ c.Content
}

/// Constructor, loads data from global
Method %OnNew() As %Status
{
    // Return:($Stack($Stack-2,"PLACE")'["Get") $$$ERROR($$$GeneralError, "Сall Get method")
    Set ..Content = $Get(@..#Global)
    Return $$$OK
}

/// Saves data to global
Method Save()
{
    Set @..#Global = ..Content
}

}

Run:

Do ##class(Utils.Singleton).Test()

I tried to disable direct instantiation with $Stack checking, but it seems to fail from a terminal. I think it would work from non-interactive code, but I had not checked. Another way would be to set some variable in Get method and check it from %OnNew() method.

Download xml from GitHub.

Eduard Lebedyuk · Apr 19, 2016 go to post

I just restart webserver/Cache when I encounter problems after web application configuration changes. It often helps.

Eduard Lebedyuk · Apr 18, 2016 go to post

Sure. To see deprecated classes for some version execute this query against it:

SELECT ID, Description FROM %Dictionary.ClassDefinition WHERE LOWER(Description) [ 'deprecated'

To see deprecated methods execute this query:

SELECT parent AS "Class", Name AS "Method", Description 
FROM %Dictionary.MethodDefinition 
WHERE LOWER(Description) [ 'deprecated'

And to see deprecated properties execute this query:

SELECT parent AS "Class", Name AS "Method", Description
FROM %Dictionary.PropertyDefinition 
WHERE LOWER(Description) [ 'deprecated'

Note, that this queries only return classes/methods/properties which contain "deprecated" in their definition. To catch deprecated inherited classes/methods/properties query %Dictionary.CompiledClass/%Dictionary.CompiledMethod/%Dictionary.CompiledProperty instead.

Eduard Lebedyuk · Apr 18, 2016 go to post

Hello.

How can I get/set pool size value for a Production item programmatically?  I can't find it in a "Settings Defaults" list.

Eduard Lebedyuk · Apr 15, 2016 go to post

Could you please tell what do you want to achieve with making 'Open as read-only' setting default?

Eduard Lebedyuk · Apr 11, 2016 go to post

Okay.

Please note, that only legacy IF sets $TEST and it is also one line long. New IF {} (3 lines, with brackets) does not set a $TEST variable.

Eduard Lebedyuk · Apr 11, 2016 go to post

Consider adding links to cache documentation from the page that describes the issue, we now have version agnostic urls even. For example this issue could use a link to documentation.

 

Another question is: what's wrong with postconditionals? They exist not only in caché objectscript and in fact very useful as they allow elimination of all 3 lines blocks with one if condition and one action to execute if if is true. Can I disable an issue type for a project?

Eduard Lebedyuk · Mar 31, 2016 go to post

Sure. You could use %CELLZERO instead:

WITH  MEMBER [MEASURES].[Revenue as Percent of Total] AS 'Measures.[Amount Sold] / %MDX("select Measures.[Amount sold] on 1 
from holefoods","%CONTEXT","filters|columns")',FORMAT_STRING='##.##%;;;;' 
MEMBER [MEASURES].[Revenue Percent Cumulative] AS 'Measures.[Revenue as Percent of Total] + %CELLZERO(0,-1)',
FORMAT_STRING='##.##%;;;;' SELECT {[Measures].[Amount Sold],[MEASURES].[REVENUE PERCENT CUMULATIVE]} ON 0,
NON EMPTY ORDER([Product].[P1].[Product Category].Members,Measures.[Amount Sold],BDESC) ON 1 FROM [HoleFoods] 

Jokes aside, why do you  need to remove %CELL usage?

Eduard Lebedyuk · Mar 30, 2016 go to post

There is also this way:

set p = ##class(%ZEN.proxyObject).%New()
set p.a = 1
set p.b = 2
do p.%ToJSON()
kill p.%data("b")
do p.%ToJSON()

kill p.%data("b") would completely remove b property. But better use Caché 2016.1 with native JSON support.

Eduard Lebedyuk · Mar 29, 2016 go to post

Formatted your code a little.

getst(getvars, StBeg, temp) ; Save call stack in local or global array
 ; In:
 ; getvars = 1 - save variables defined at the last stack level
 ; getvars = 0 or omitted - don't save; default = 0
 ; StBeg - starting stack level for save; default: 1
 ; temp - where to save ($name).
 ; Out:
 ; temp - number of stack levels saved 
 ; temp(1) - call at StBeg level
 ; temp(2) - call at StBeg+1 level
 ; ...
 ; temp(temp) - call at StBeg+temp-1 level
 ;
 ; Calls are saved in format:
 ; label+offset^rouname +CommandNumberInsideCodeLine~CodeLine w/o leading spaces"
 ; E.g.:
 ; temp(3) = First+2^%ZUtil +3~i x=""1stAarg"" s x=x+1 s c=$$Null(x,y)"
 ; Sample calls:
 ; d getst^%ZUtil(0,1,$name(temp)) ; save calls w/o variables in temp starting from level 1
 ; d getst^%ZUtil(1,4,$name(^zerr($i(^zerr)))) ; save calls with variables in ^zerr starting from level 4
    new loop,zzz,StEnd
    set getvars = $get(getvars)
    set StBeg = $get(StBeg, 1) 
    kill @temp 
    set @temp = 0 
    set StEnd = $STACK(-1)-2
    for loop = StBeg:1:StEnd {
        set @temp = @temp+1
        set @temp@(@temp) = $STACK(loop, "PLACE") _ "~" _ $zstrip($STACK(loop, "MCODE"), "<W") 
        if getvars,(loop=StEnd) {
            set zzz="" 
            for { 
                set zzz = $order(@zzz)
                quit:zzz=""  
                set @temp@(@temp,zzz) = $get(@zzz)
            }
            if $zerror'="" {
                set @temp@(@temp,"$ze") = $zerror
            }
         }
    }
     quit 1
Eduard Lebedyuk · Mar 29, 2016 go to post

You can fill $zerror by passing a name into LOG^%ETN:

Do LOG^%ETN("test")

It would record your current $zerror  value, replace it with "test", execute LOG captue, and at the end restore $zerror to what it was before the call.

Eduard Lebedyuk · Mar 24, 2016 go to post

I assume that's a comment  to my answer?

If yes, here's the working code. Maybe I missed some changes in my answer. Because, again, I needed to do too much changes to run the code in the first place.

Eduard Lebedyuk · Mar 24, 2016 go to post

I made the following changes:

1.  Changed

Set mimePart.ContentType = "application/pdf"

to

Set mimePart.ContentType = "application/x-object"

2. Commented out:

Do mimeMsg.SetHeader("Content-Disposition","attachment; name=""Files1""; filename="""_docName_"""")          
Set mimeMsg.ContentTransferEncoding = "base64"

3. Uncommented:

 //Set req.ContentType = "multipart/form-data;boundary=" _mimeMsg.Boundary

After these change I received non-empty files object.

 "files": {
              "uploadedfile": "cd \\\r\n@echo start >> date.txt\r\n"
          }

Howether. Here's some tips on how to ask questions like these (with not working code samples):

1.  It must run everywhere. Remove the calls to other code (hardcode values if needed), like:

##class(HData.SiteSettings).GetFaxLogin()

2. Remove unused arguments, like coverpath

3. Remove non essential code like:

Set req.SSLConfiguration = "CardChoice"
Do req.InsertFormData("Username",##class(HData.SiteSettings).GetFaxLogin())
Do req.InsertFormData("Password",##class(HData.SiteSettings).GetFaxPassword())
Do req.InsertFormData("ProductId",##class(HData.SiteSettings).GetFaxProductId())
Do req.InsertFormData("cookies","false")
Do req.InsertFormData("header","header")

//Create job name for fax
Set sendDate = +$H
Set sendTime = $P($ZTIMESTAMP,",",2)+(15*60)
Set schedDate = $ZDATETIME(sendDate_","_sendTime,1,4)
Set schedDate = $E(schedDate,1,16)_" "_$E(schedDate,17,18)
Set docList = $LFS(docpath,"\")
Set docName = $LISTGET(docList,$LL(docList))
Do req.InsertFormData("JobName",schedDate_docName)
    
//format phone number and set billing code
Set num = $REPLACE(phone,"-","")
Do req.InsertFormData("BillingCode",num)
Do req.InsertFormData("Numbers1",num)
   
    
//get notification email
Set email = ##class(HData.SiteSettings).GetFaxNotifyEmail()
If (email '= "") Do req.InsertFormData("FeedbackEmail",email)

4. Comment out unused and misleading code paths like

Set base64 = ##class(%SYSTEM.Encryption).Base64Encode(docStream)
Set mimeMsg.ContentTransferEncoding = "base64"

5. Provide a GitHub Gist snippet of an xml file (with the code) to import and write how to run it.

Your goal, is to make it extremely easy for everyone to take a look at your snippet. All the process must consist of these two steps:

  1. Drag&Drop xml file into Studio
  2. Copy&Paste a line into a terminal
Eduard Lebedyuk · Mar 18, 2016 go to post

Since XMLEnabled class can be not persistent and, therefore, not have an extent at all, there is no such method. If you need to write  XMLExportToFile()  for several classes you can write method generator which generates a method that would:

  1. Use current extent query
  2. Iterate through it
  3. Output each object into a file

And inherit all classes, in which you have a need of XMLExportToFile()  method from this class.