Eduard Lebedyuk · Jul 26, 2016 go to post

You can define a property of  %Stream.Object class and store %Stream.FileBinary/%Stream.FileCharacter there.

Eduard Lebedyuk · Jul 26, 2016 go to post

I imported your example, executed:

Do ##class(Wendy.LTCodes).SetData()

Then executed this sql:

SELECT
ID, Code, Description, Invalid
FROM Wendy.LTCodes

and received the following results:


ID Code Description   Invalid
N001 N001 ANYOLD DESC 1
N002 N002 C5 REPEAT 1  
N111 N111 SPECIMEN COMMENT  
N200 N200 MSUD SCREEN  
N500 N500 Sickle Cell Screen  

seems to be working.

But then I didn't really understand the use of Parameter InvalidGLVN = "^Utils.GlobalPropP";

it's for use with indirection.  Example:

set ^Utils.GlobalPropP = 123
set glvn = "^Utils.GlobalPropP"
write glvn
> ^Utils.GlobalPropP
write @glvn
> 123
Eduard Lebedyuk · Jul 26, 2016 go to post

These changes fixed it:

  • Removed highlited part of storage definition
  • Made invalid property Transient
  • Added 0 default to $Get
Class Wendy.LTCodes Extends %Persistent [ StorageStrategy = LTCStorage ]
{

Property Code As %String;

Property Description As %String;

Property Invalid As %Library.Boolean [ SqlComputeCode = { set {*} = ##class(Wendy.LTCodes).GetInvalid({Code})}, SqlComputed, Transient ];

Index CodeIndex On Code [ IdKey, PrimaryKey, Unique ];

ClassMethod GetInvalid(WSCode) As %Boolean
{
    Quit $G(^LTCODES(WSCode,"INVALID"),0)
}

Method InvalidGet() As %Boolean
{
    Quit ..GetInvalid(i%Code)
}

Method InvalidSet(value As %Boolean) As %Status
{
    Set ^LTCODES(i%Code,"INVALID")=value
    quit $$$OK
}

/// Do ##class(Wendy.LTCodes).SetData()
ClassMethod SetData()
{
    kill ^LTCODES
    S ^LTCODES("N001")="ANYOLD DESC"
    S ^LTCODES("N001","INVALID")=1
    S ^LTCODES("N002")="C5 REPEAT 1"
    S ^LTCODES("N111")="SPECIMEN COMMENT"
    S ^LTCODES("N200")="MSUD SCREEN|MSUD"
    S ^LTCODES("N500")="Sickle Cell Screen"
}

Storage LTCStorage
{
<SQLMap name="LTCMap">
<Data name="Description">
<Delimiter>"|"</Delimiter>
<Piece>1</Piece>
</Data>
<Global>^LTCODES</Global>
<Subscript name="1">
<Expression>{Code}</Expression>
</Subscript>
<Type>data</Type>
</SQLMap>
<StreamLocation>^Wendy.LTCodesS</StreamLocation>
<Type>%CacheSQLStorage</Type>
}

}
Eduard Lebedyuk · Jul 28, 2016 go to post

Okay, then you need to use your original storage definition, but when selecting invalid, select it in a NVL function:

SELECT
ID, Code, Description, NVL(Invalid, 0)
FROM Wendy.LTCodes

It should return 0 instead of empty string.

Eduard Lebedyuk · Aug 3, 2016 go to post

ParseZU49Info method in SYS.Database class translates $ZU(49) call into SYS.Database properties.

Eduard Lebedyuk · Aug 3, 2016 go to post

Check where in ParseZU49Info it takes field 14 and what property does it fills with that information.  Check %syDatabase.inc for more information.

#; Pieces of return value from $zu(49)
#define mountpiece 1
#define blksizpiece 2
#define uicpiece 3
#define filsizpiece 4
#define expandpiece 5
#define maxblkpiece 6
#define glodirpiece 7
#define gloptrpiece 8
#define rdirpiece 9
#define rgrpiece 10
#define glogrpiece 11
#define freezepiece 12
#define colpiece 13
#define sfnpiece 14
#define totvolpiece 15
#define formatpiece 16
#define attribpiece 17
#define statuspiece 18
#define exptimepiece 19
#define nojrnpiece 20
#define bigdbpiece 21
#define curblkspiece 22
#define blkspermappiece 23
#define curmapspiece 24
#define resourcepiece 25
#define enckeyidpiece 26
Eduard Lebedyuk · Aug 3, 2016 go to post

Sorry, I forgot that SYS.Database is deployed. You'll need to match $zu(49) to properties manually. But the macros usually have the same name. For example field 14 is sfnpiece macro ("piece" part of the name is irrelevant, so just sfn it is) and it corresponds to SFN property in SYS.Database.

To test, you can write the following method:

ClassMethod Test( Directory )

{
    Set db=##Class(SYS.Database).%OpenId(Directory)
    Write db.SFN = $p($zu(49),",",$$$sfnpiece) //14th piece
    // More checks
}
Eduard Lebedyuk · Aug 3, 2016 go to post

12th piece is the FreezeOnError property for a Database and is always set to on (since 2008.1.0.217.0). The corresponding property FreezeOnError was removed from SYS.Database class.

Eduard Lebedyuk · Aug 3, 2016 go to post

Are you in %SYS namespace? Underlying database (CACHELIB) is probably mounted as read-only.

Eduard Lebedyuk · Aug 4, 2016 go to post

I doubt that this kind of information is available.

Have you thought about wrapping global calls in a method?

ClassMethod SetVal(glvn, value)
{
  // track statistics, like $Username changed glvn from oldvalue to value, etc.
  set @glvn = value
}
Eduard Lebedyuk · Aug 10, 2016 go to post

Please post the property definition.

by default she send the value as DISPLAYLIST

How do you get property value?

Eduard Lebedyuk · Sep 7, 2016 go to post

Looks nice, but does not resolve short class references to the library package (%String for example) and current package.

 

Eduard Lebedyuk · Sep 16, 2016 go to post
  1. Can you access Caché studio?
  2. Do you see ApplicationID field in iSkillsetStat (or SQLUSER.iSkillsetStat) table in any database explorer application?
Eduard Lebedyuk · Sep 16, 2016 go to post

Yes, I use it for web development, when I frequently need to login as different users.

Eduard Lebedyuk · Sep 19, 2016 go to post

IF we use cookies, they will be stored in the Session Cookie Path.

Cookie has a property named path. Whed browser determines, does the cookie apply to a current page, it checks if the cookie path is less or equal to current URL.

I'm thinking that this login cookie would be used somehow if the Login Cookie is selected? Or not used? 

It would be used, if checked.

what does happen if the Login Cookie is selected in the web application?

Login Cookies hold information about the most recently logged-in user. If you want to keep your users from having to log in too often, but you want your applications to remain distinct and unconnected, use Login Cookies. For Login Cookies, place each application in a separate session. Then authentication is shared only when an application is entered for the first time. Login Cookies applications do not form a group. So after login, changes in authentication in one application do not affect the other applications. Documentation.

What could we store in a cookie? Can we possibly find out if a second tab has been opened by using a cookie?

You can store text values in cookie. I suggest you read wiki article on them.

Eduard Lebedyuk · Sep 21, 2016 go to post

For each can be implemented via macros:

#define ForEach(%in,%gn) s gn%in=$na(%gn) s %in="" f { s %in=$o(@gn%in@(%in)) q:%in=""
#define EndFor  }

$$$ForEach(key, "^global")
    Write key,!
$$$EndFor
Eduard Lebedyuk · Sep 21, 2016 go to post

Great article, however, I strongly disagree on the testing tools choice. Curl is not really the option, as CLI tools are really not the best for json editing. Upon the rest of your testing suggestions, I think there are better alternatives, such as:

  • REST clients: “Advanced REST Client” for Chrome and “REST Client” for Firefox, some are also available as a standalone applications
  • Debugging web proxies: Charles, Filddler etc. for the rare cases where REST clients do not offer enough information
Eduard Lebedyuk · Sep 23, 2016 go to post

This method links stream object to an existing file:

Method imagefile()
{
  set obj = ##class(Sam.binary).%New()
  read "enter path to image: ",img
  set st = obj.Image.LinkToFile(img)
  write $System.Status.GetErrorText(st)
  set st = obj.%Save()
}
Eduard Lebedyuk · Sep 27, 2016 go to post

Here's an example:

Class User.NewClass1 Extends %Persistent
{

Property streams As list Of %Stream.GlobalCharacter;

ClassMethod Test()
{
    do ..%KillExtent()
    
    set obj = ..%New()
    set stream1 = ##class(%Stream.GlobalCharacter).%New()
    do stream1.WriteLine("Hi")
    set stream2 = ##class(%Stream.GlobalCharacter).%New()
    do stream2.WriteLine(123)
    
    do obj.streams.Insert(stream1)
    do obj.streams.Insert(stream2)
    write $System.Status.GetErrorText(obj.%Save())
    
    kill
    
    set obj = ..%OpenId(1)
    for i=1:1:obj.streams.Count() {
        write "Stream #", i, ": ", obj.streams.GetAt(i).Read($$$MaxCacheInt)
    }
}
}