Vitaliy Serdtsev · Jun 14, 2017 go to post

E.g.:

fileName="C:\temp\test.txt"
f=##class(%FileCharacterStream).%New()
f.Filename=fileName
f1=##class(%FileCharacterStream).%New()
f1.Filename=fileName
   
f.Write("start ")
f.CopyFrom(f1)
f.Write(" end")

f.SaveStream()
f="",f1=""

Result:

test.txt (Before): bla-bla-bla
test.txt (After): start bla-bla-bla end
Vitaliy Serdtsev · Jun 14, 2017 go to post

If you know how to work with ActiveX from MS Excel VBA, then there is no problem.

E.g. (demo.vbs):

Set f = CreateObject("CacheActiveX.Factory")
Set rs = CreateObject("CacheActiveX.ResultSet")
If Not f.IsConnected() Then

  f.Connect("cn_iptcp:127.0.0.1[1972]:SAMPLES:_SYSTEM:SYS")

  Set rs=f.DynamicSQL("select TOP 3 * from Sample.Person")
  rs.Execute()
  while rs.Next
    WScript.Echo rs.Get("SSN"'print of field SSN for first three rows from the table Sample.Person
  wend

  rs.Close()
  Set person = f.Static("Sample.Person")
  age=person.CurrentAge(45678) 'call of method of class Sample.Person

  WScript.Echo age
End If

Running a Query in Visual Basic

Vitaliy Serdtsev · Jun 9, 2017 go to post

Indeed, thank you:

Class demo.B Extends %Persistent Final ]
{

Parameter MANAGEDEXTENT As INTEGER [ Constraint "0,1"Flags = ENUM ] = 0;

Parameter READONLY = 1;

Property P2;

Storage Default
{
<Data name="BDefaultData">
  <Value name="3">
    <Value>P2</Value>
  </Value>
</Data>
<DataLocation>^demo.AD</DataLocation>
<DefaultData>BDefaultData</DefaultData>
<IdLocation>^demo.AD</IdLocation>
<IndexLocation>^demo.AI</IndexLocation>
<StreamLocation>^demo.AS</StreamLocation>
<Type>%Library.CacheStorage</Type>
}

}
Vitaliy Serdtsev · Jun 9, 2017 go to post

Is there any way to "generate" compatible storage? And why SQLStorage? Why not default CachéStorage, but copy-pasted from Class A?
Unfortunately, to use %CacheStorage will not work, since at compile occurs the error:
ERROR #5564: Storage reference: '^demo.AD' used in 'demo.B.cls' is already registered for use by 'demo.A.cls'

ERROR #5030: An error occurred while compiling class 'demo.B'

Vitaliy Serdtsev · Jun 9, 2017 go to post

If you only need SQL access, then will be easier to create a view (CREATE VIEW), if need both, then - %CacheSQLStorage, e.g.:

Class demo.A Extends %Persistent
{

Property P1;

Property P2;

ClassMethod Fill()
{
  ..%KillExtent()
  
  i=1:1:3 {
    t=..%New()
    t.P1="P1_"_i
    t.P2="P2_"_i
    t.%Save()
  }
}

Storage Default
{
<Data name="ADefaultData">
  <Value name="1">
    <Value>%%CLASSNAME</Value>
  </Value>
  <Value name="2">
    <Value>P1</Value>
  </Value>
  <Value name="3">
    <Value>P2</Value>
  </Value>
</Data>
<DataLocation>^demo.AD</DataLocation>
<DefaultData>ADefaultData</DefaultData>
<IdLocation>^demo.AD</IdLocation>
<IndexLocation>^demo.AI</IndexLocation>
<StreamLocation>^demo.AS</StreamLocation>
<Type>%Library.CacheStorage</Type>
}

}

Class demo.B Extends %Persistent FinalStorageStrategy = Default ]
{

Parameter READONLY = 1;

Property P2;

Storage Default
{
<SQLMap name="BDefaultData">
  <Data name="P2">
    <Piece>3</Piece>
  </Data>
  <Global>^demo.AD</Global>
  <RowIdSpec name="1">
    <Expression>{L1}</Expression>
    <Field>ID</Field>
  </RowIdSpec>
  <Subscript name="1">
    <Expression>{ID}</Expression>
  </Subscript>
  <Type>data</Type>
</SQLMap>
<StreamLocation>^demo.AS</StreamLocation>
<Type>%CacheSQLStorage</Type>
}

}

Result:

USER>##class(demo.A).Fill()
 
USER>d $SYSTEM.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
 
The command prefix is currently set to: <>.
Enter q to quit, ? for help.
USER>>select * from demo.A
1.      select * from demo.A
 
ID      P1      P2
1       P1_1    P2_1
2       P1_2    P2_2
3       P1_3    P2_3
 
3 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.1426s/46110/260143/45ms
          execute time(s)/globals/lines/disk: 0.0004s/16/809/0ms
                          cached query class: %sqlcq.USER.cls12
---------------------------------------------------------------------------
USER>>select * from demo.B
2.      select * from demo.B
 
ID      P2
1       P2_1
2       P2_2
3       P2_3
 
3 Rows(s) Affected
statement prepare time(s)/globals/lines/disk: 0.0696s/44550/243602/0ms
          execute time(s)/globals/lines/disk: 0.0002s/4/619/0ms
                          cached query class: %sqlcq.USER.cls13
---------------------------------------------------------------------------
USER>>quit
 
USER>##class(demo.B).%OpenId(3).P2
P2_3
Vitaliy Serdtsev · Jun 8, 2017 go to post

Do not forget about the method signature:

ClassMethod main() As %String

Need not print the number, but return it.

Vitaliy Serdtsev · Jun 7, 2017 go to post

Ok, exclusively for fun.

I made some improvements and now my score is 9, but if you try very hard, even - 0!
Who less ? ;)

Here is the code:

Class ITPlanet.Task2 Abstract ]
{

Parameter p = {$zwbunpack("㤸㜶㔴㌲㄰")};

ClassMethod main() As %String
{
 ..#p
}

}

Class ITPlanet.Test Abstract ]
{

ClassMethod length(
  class = {$classname()},
  method "main"As %Integer CodeMode = expression ]
{
##class(%Dictionary.MethodDefinition).IDKEYOpen(classmethod).Implementation.Size
}

ClassMethod test(makeDeploy = {$$$NO})
{
  ;do ##class(ITPlanet.Test).test()

  set classname="ITPlanet.Task2"
  set check=9876543210
  do:makeDeploy $system.OBJ.MakeClassDeployed(classname)
  set result=$classmethod(classname,"main")
  write !,result,!,check,
        !,"correct: ",$select(result=check:"yes",1:"no"),
        !,"length: ",..length(classname)
}

}
USER>do ##class(ITPlanet.Test).test()
 
9876543210
9876543210
correct: yes
length: 9
USER>do ##class(ITPlanet.Test).test(1)
 
9876543210
9876543210
correct: yes
length: 0
Vitaliy Serdtsev · Jun 6, 2017 go to post

Rhetorical question - for what?
If for fun, then this is possible.
But I will tell the solution here later.

Vitaliy Serdtsev · Jun 6, 2017 go to post

You can use the class %ZEN.proxyObject, e.g.:

Class demo.Customer Extends %RegisteredObject
{

Property p1 As %String;

Property p2 As %String;

Property list As %Collection.ListOfDT;

/// d ##class(demo.Customer).Test()
ClassMethod Test()
{
  customer=##class(demo.Customer).%New()
  customer.p1="p1"
  customer.p2="p2"
  customer.list=##class(%ListOfDataTypes).%New()
  customer.list.InsertList($lb(1,"two",,"four"))
  
  appointment=##class(%ZEN.proxyObject).%New()
  appointment.a1="a1"
  appointment.a2="a2"
  appointment.list=##class(%ListOfDataTypes).%New()
  appointment.list.InsertList($lb(2,3,"test",8))
  
  ; Cancellation
  response=##class(%ZEN.proxyObject).%New()
  response.Code="Cancellation"
  response.Info="Info_Cancellation"
  response.Code,":",! response.%ToJSON(,"2aelow")
  
  response.%Clear()

  ; Customer
  response.Code="Customer"
  response.Info=customer
  
  !!,response.Code,":",! response.%ToJSON(,"2aelow")

  response.%Clear()

  ; Appointment
  response.Code="Appointment"
  response.Info=appointment
  
  !!,response.Code,":",! response.%ToJSON(,"2aelow")
}

}
USER>##class(demo.Customer).Test()
Cancellation:
{
  "Code":"Cancellation",
  "Info":"Info_Cancellation"
}
 
Customer:
{
  "Code":"Customer",
  "Info"{
    "p1":"p1",
    "p2":"p2",
    "list":["1","two","","four"]
  }
}
 
Appointment:
{
  "Code":"Appointment",
  "Info"{
    "a1":"a1",
    "a2":"a2",
    "list":[2,3,"test",8
    ]
  }
}
Vitaliy Serdtsev · Jun 2, 2017 go to post

See still of the source code for the following methods:

  • ##class(%Net.MIMEWriter).EncodeStreamBase64()
  • ##class(%Net.SMTP).EncodeStreamBase64()
  • ##class(%Atelier.v1.Utils.General).Base64FromStream()
  • ##class(%XML.Writer).WriteBase64()
Vitaliy Serdtsev · May 29, 2017 go to post

I want to say that this question was already asked and there it was given some answer. No more than.
I personally use Studio and will continue to use.

Vitaliy Serdtsev · May 26, 2017 go to post

Initially the question was about alternative ways of solving the issue (in addition to recursive FileSet and $ZSEARCH).

I just proposed a third method, namely using the capabilities of the OS itself. Maybe someone here didn't know about it.

Which option at end to choose - to solve the developer.


We here vote for the best solution or in general for offered solutions?

If the first, then I'll pass.

Vitaliy Serdtsev · May 26, 2017 go to post

And if so?

##class(%Net.Remote.Utility).RunCommandViaZF($$$FormatText("dir /A-D /B /S %1",$$$quote("C:\InterSystems\Atelier")),.tFileName,,,$$$NO)
f=##class(%Stream.FileCharacter).%New()
f.Filename=tFileName
while('f.AtEnd{f.ReadLine(),!}
f=""
##class(%File).Delete(tFileName)

Vitaliy Serdtsev · May 26, 2017 go to post

Anything with this flag can change or be removed with no warning.
There are a few comments:
  • Let's say that the developers changed something in new versions of the DBMS. Is this a problem?

    It is enough to check Caché Recent Upgrade Checklists, where usually there is a ready list of changes that may affect existing user code, for example.

    Note that this can apply to absolutely any member of a class, and not even marked as [Internal]. Suffice it to recall the recent story with JSON support.

  • For a class does not exist flag [Internal], the warning is only on the level of comments.

    As for the other members of the class, according to the documentation this flag is for other purposes, namely:
    Internal class members are not displayed in the class documentation. This keyword is useful if you want users to see a class but not see all its members. proof
  • In any case, the final choice for developer.

Vitaliy Serdtsev · May 25, 2017 go to post

Does this example need to handle this?

Correct, of course, to close the device:

#include %systemInclude

#dim cDev As %String $IO

; see "dir /?" and RunCommandViaZF()
##class(%Net.Remote.Utility).RunCommandViaCPIPE(...,.dev,.con)
/*
...
*/
c:($get(dev)'=""dev:"I"
cDev

Also, do you not worry that its an internal class?

No, since for me internal ≠ deprecated.

However, given the openness of the source code, you can make own similar method, thereby to protect yourself from possible issues in the future.

Vitaliy Serdtsev · May 24, 2017 go to post

<...> but maybe there's a better solution?

Yes, of course.

E.g. (for Windows x64):

#include %systemInclude

; see "dir /?"
##class(%Net.Remote.Utility).RunCommandViaCPIPE($$$FormatText("dir /A-D /B /S %1",$$$quote("C:\Program Files (x86)\Common Files\InterSystems")),,.con)
; string -> array
##class(%ListOfDataTypes).BuildValueArray($lfs($e(con,1,*-2),$$$NL),.array)
zw array