Rubens Silva · Jul 14, 2017 go to post

If you want to work on a project basis and versionate it, but still want to work with Studio, you can try using Port to test if it satisfies you. By default Port will export your project using UDL format and also a XML for backward compability.

 

If you want to export only files that ends with .csp, you can save your current project with another name and use the Remove classes, Remove routines. From the Source Control menu. Remember though, that Port is made to be ran locally instead of making your pc work like a thin client for Caché. That means your Source code should preferably be local and not remote.
Also, you're welcome to open issues. And, even though there's an alert about the nightly branch is pretty stable for now, since I have been working mostly on adding unit tests and fixing minor bugs.
P.S: Just to take a note. The Port repository is exported using itself.

Rubens Silva · Jul 14, 2017 go to post

/// Returns a list of the Globals in a Cache NameSpace (used for GUI display)<br>
/// <br>
/// <b>Parameters:</b> <br>
/// NameSpace - a Cache namespace. Default is current namespace. <br>
/// Mask - a mask, or comma-separated list of masks, to select globals. Default is "*" for all.<br>
/// SystemGlobals - boolean flag to include system globals in the results. Default is "0".<br>
/// UnavailableDatabases - a returned local array of any databases not currently accessible, i.e. array(name)=status.<br>
/// Index - Internal use only.<br>
/// IgnoreHasData - For faster list of Globals set this to 1 and the HasData column will always be FALSE.<br>
/// Mapped - Return all mapped global nodes when set to 1, the default value of this parameter is 1.
/// <br>
/// Valid masks are as follows:
/// <br>
/// ABC* - All strings starting with ABC<br>
/// A:D - All strings between A and D<br>
/// A:D,Y* - All strings between A and D, and all strings starting with Y<br>
/// A:D,'C* - All strings between A and D, except those starting with C
Query NameSpaceList(
  NameSpace As %String,
  Mask As %String,
  SystemGlobals As %Boolean,
  ByRef UnavailableDatabases As %String,
  Index As %Integer,
  IgnoreHasData As %Boolean = 0,
  Mapped As %Boolean = 1) As %Query(ROWSPEC = "Name:%String,Location:%String,ResourceName:%String,Permission:%String,Empty:%String,Keep:%String,Collation:%String,PointerBlock:%String,GrowthBlock:%String,HasData:%Boolean,Journal:%String,LockLocation:%String,HasSubscripts:%Boolean") [ SqlProc ]
{
}
set s = ##class(%SQL.Statement).%New()

do s.%PrepareClassQuery("%SYS.GlobalQuery", "NameSpaceList")
set r = s.%Execute("SAMPLE", "*")
set $namespace = "SAMPLE"
while r.%Next() { kill @r.%Get("Name") }

Rubens Silva · Jul 14, 2017 go to post

You can use value positioning to match their $list index according to their property definition index. But you will need to describe each property to detect their types and if they are instantiable. In case they do, you can use the value to open the instance and the property type. E.g:
set properties = ##class(%Dictionary.CompiledClass).%OpenId("SQLUser.Table").Properties

for i=1:1:propeties.Count() {
    set listValue = $lg(SQLUser^TableD(1) , i)

    set property = propeties.GetAt(i)
   // if property.Type  inherits from %RegisteredObject
   set value = $System.OBJ.OpenId(property.Type, listValue)
  // else 
   set value = listValue
}
If you don't want to open the class descriptor instance you can also use macros from %occReference.inc. But you will have to deal with $order.
My suggestion takes the longest path. You will need a method that supports each property type behavior,  like that lb(,,"bla","20050502123400").
But why can't you use an instance? Is it because the structure is too old and there isn't any %Persistent class to represent it?

Rubens Silva · Jul 12, 2017 go to post

"The following example uses relative dot syntax (..) to refer to a method of the current object."
If so, it's pretty misleading. But I accept your answer and will mark it as well.

Rubens Silva · Jul 11, 2017 go to post

Oh crap! I knew I had seen it somewhere.
EDIT: Ahh, btw... it's Util, not Utils.

Rubens Silva · Jul 11, 2017 go to post

On %CSP.Session
/// Specifies the timeout value for the session in seconds.
/// <P>If no user requests are received within the specified time period,
/// then the session will end. The default value comes from the CSP application
/// setting for the application that the session starts in which is set in the
/// Cache configuration manager, this is often 900 seconds or 15 minutes.
/// Note that if you start a session in one applicaiton and move to another application
/// the AppTimeout will not be changed to the new applications timeout value, if you wish
/// to modify this when the application changes you can use the session events 'OnApplicationChange'
/// method.
/// <P>For no timeout, set this property to 0.
Property AppTimeout As %Integer [ InitialExpression = 900 ];
You can also specify the timeout for your App as a whole:

 

<your server ip:57772>/csp/sys/sec/%25CSP.UI.Portal.Applications.Web.zen?PID=%2Fcsp%2Fuser

Rubens Silva · Jul 11, 2017 go to post

Since you didn't specified which service you're using it's hard to simulate your doubt.

However I did a quick research and noticed that 'padding' actually refers to OAEP, this could be the method you want to use to encrypt. I'm not sure though.
/// This method performs RSA encryption as specified in
/// PKCS #1 v2.1: RSA Cryptography Specifications, section 7 Encryption Schemes.
/// <br><br>
/// Input parameters:
/// <br><br>
/// Plaintext - Data to be encrypted.
/// <br><br>
/// Certificate - An X.509 certificate containing the RSA public key to be used for encryption,
/// in PEM encoded or binary DER format.
/// Note that the length of the plaintext can not be greater than the length of the modulus of
/// the RSA public key contained in the certificate minus 42 bytes.
/// <br><br>
/// CAfile - The name of a file containing trusted Certificate Authority X.509 Certificates in PEM-encoded format, one of which was
/// used to sign the Certificate (optional).
/// <br><br>
/// CRLfile - The name of a file containing X.509 Certificate Revocation Lists in PEM-encoded format that should be checked
/// to verify the status of the Certificate (optional).
/// <br><br>
/// Encoding - PKCS #1 v2.1 encoding method (optional):<br>
///     1 = OAEP (default)<br>
///     2 = PKCS1-v1_5<br>
/// <br><br>
/// Return value: Ciphertext.
ClassMethod RSAEncrypt(
Plaintext As %String,
Certificate As %String,
CAfile As %String,
CRLfile As %String,
Encoding As %Integer) As %String
{
}

Rubens Silva · Jul 4, 2017 go to post

As long as there's an abstract API for parsing, lexing, transpiling and serializing. It would be possible to even port any FP or FRP language to Caché.
Since you demo'ed JavaScript, it seems programming on a functional way could be possible if we could simply pass methods as parameters.
Well, that's actually the core rule for a language that supports functional paradigms.
Since there's no current support for such paradigm. Maybe we could wrap it using indirections or xecutes?
 

set array = []

do a.%Push({ "value":  "some value to be replaced" })

set result = ##class(FP.Functor).%New()From(array).Map($this, "...ValueWithIndex",  scopeParam)
Method ValueWithIndex(item As %DynamicObject, index As %Integer, scopeParams... As %String) 
{
    // Second core rule: always keep it pure. Map should always Clone the item, which could be implicit for Map.
   // But for this case I'll demonstrate it manually.
   
      set clonedItem = item.Clone() // Or %ConstructClone if possible.
      set clonedItem .%Set("value", "modified with "_index)
      return  clonedItem
}
Please note that this still doesn't provide the possibility to use high-order functions. The closest we could have I think is embedding subroutines within your context method. Which could also be reproduced as:
 

// Can also be Method.

ClassMethod YourContextMethod() As %String
{

set scopeParam = "blahblahblah"

// Now assume we're using %ZEN.proxyObject. Omitted for brevity.
set result = ##class(FP.Functor).From(array).Map("$$HOMapWithScopeParam",  scopeParam)
HOMapWithScopeParam(result, item, value, scopeParam...)

  // Now Implicitly cloned into result param.
  set result.value = scopeParam(0) // Could be improved.
  quit result
}

Nope, I forgot that procedures are exclusive for the subroutine that's defining them. 

Rubens Silva · Jul 4, 2017 go to post

I did a little experiment and here's the result:
  ClassMethod testing(item)
{
  set array = ##class(%ListOfObjects).%New()
  for i=1:1:10 {
    set proxy = ##class(%ZEN.proxyObject).%New()
    set proxy.value = i
    do array.Insert(proxy)
  }
  
  set DoubleItemValueSumTwo = $classname()_":DoubleItemValueSumTwo"
  set Odds = $classname()_":Odds"
  set BiggerThanFive = $classname()_":BiggerThanFive"
  
  set result = ##class(FP.Functor).From(array).Map(DoubleItemValueSumTwo).Filter(Odds).Every(BiggerThanFive).Result()
  quit result
}
ClassMethod DoubleItemValueSumTwo(
item As %ZEN.proxyObject,
i)
{
  set item.value = (item.value * 2) + $random(2)
  quit item
}
ClassMethod Odds(
item As %ZEN.proxyObject,
i)
{
  quit (item.value # 2 '= 0)
}
ClassMethod BiggerThanFive(
item As %ZEN.proxyObject,
As %Integer)
{
  quit item.value > 5
}
There is space for a lot of improvements I think. It's not exactly what you would call a performatic implementation. But that's a beginning.

Rubens Silva · Jul 4, 2017 go to post

Instead of creating multiple repositories. Can't you just create a single repository and keep all projects inside it?
This is what a monorepository approach does.
Big companies like Google opted-out for using this approach because they started to find it too hard to manage issues across multiple repositories, since one should clone the issued repository along with a tree of dependencies to make it testable. And that's only one use case.
Some few examples:
https://eng.uber.com/ios-monorepo/
https://medium.com/@pejvan/monorepos-85e608d43b57

https://blog.ghaering.de/post/monorepo-march/
Now you must consider if that's a option for your company. Since using monorepo actually seems to be a trend and can lead you into traps.

Rubens Silva · Jul 3, 2017 go to post

Though, I do think Google I/O and Apple are also streamed over YouTube.
Can't say much about Oracle...

Rubens Silva · Jul 3, 2017 go to post
set key = ""
set var1 = "code2"
for  {
    set key = $order(^myglobal(key), 1, list)
    quit:key=""
    if $listget(list, 1) = var2 kill ^myglobal(key)
}

Use $order to iterate over global subscripts.

Rubens Silva · Jul 3, 2017 go to post

Although Caché does have a %DataTypes layer for SQL, the database engine itself is purely based on globals, which is losely typed.

Thus, what I can say for you is:
Local variables (memory) is by default around 32 kilobytes, you can upgrade this amount up to 50x.

So, straight from the documentation:
 

Caché supports two maximum string length options:

  • The traditional maximum string length of 32,767 characters.

  • Long Strings maximum string length of 3,641,144 characters.

Globals can go way beyond, since it's persisted.
How huge a number can go? I suppose you're talking about floating precision.
This might help you.
And this explains about how Caché manages variables.
Caché also provides you the possibility to redefine the memory allocation size for the context process.

Rubens Silva · Jul 3, 2017 go to post

Yeah. I actually prefer using $$$GeneralError than creating my own error and having it display with <domain> before it's message. I think that" <domain>-errorcode: Message" pollutes the message for the end-user.
I did a test using the tutorial you posted (which is great by the way!), but the way the Status API display the error by default kind made me fallback to 5001, which is cleaner.

Rubens Silva · Jul 3, 2017 go to post

You can't use positive numbers because most of them are already defined inside %occErrors.inc and there's no way to inform which domain should the Error() use.

Rubens Silva · Jun 22, 2017 go to post

Here is the procedure sample:
http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY…

 

ClassMethod CalcAvgScore(firstname As %String,lastname As %String) [sqlproc]
{
  New SQLCODE,%ROWID
  &sql(UPDATE students SET avgscore = 
    (SELECT AVG(sc.score) 
     FROM scores sc, students st
     WHERE sc.student_id=st.student_id 
       AND st.lastname=:lastname
       AND st.firstname=:firstname)
     WHERE students.lastname=:lastname
       AND students.firstname=:firstname)

  IF ($GET(%sqlcontext)'= "") {
    SET %sqlcontext.%SQLCODE = SQLCODE
    SET %sqlcontext.%ROWCOUNT = %ROWCOUNT
  }
  QUIT
}

 

Instead of using SQL to define PROCEDURES, even though you can, it's easier to create one using your own class. Just declare it as [ SqlProc] and it'll be available to use inside SQL. You can use that way to define a SQL function as well.

Rubens Silva · Jun 22, 2017 go to post

Is your class located within the User package? Ex: User.Location?
If you REALLY want to use _ then at least wrap the alias inside double quotes.
 

Rubens Silva · Jun 21, 2017 go to post

Is the file using BOM? If so you can check the header for the following signature: EF BB BF


This can be described as: $c(239, 187, 191)

Now keep in mind that most of editors abandoned the use of BOM in favor of digraphs and trigraphs detection heuristics as a fallback, yes, fallback. Because many assume you're already working with UTF-8 and won't work well with some charsets neither output BOM characters unless you tell it to use the desired charset.
 

You can try checking it against the US-ASCII table that goes from 0 to 127 code points, however that still wouldn't be 100% assertive about the stream containing UTF-8 characters.

Rubens Silva · Jun 20, 2017 go to post

I develop using a mix of Caché Studio with Visual Studio Code.
I use Visual Studio Code for dealing with front-end code, while using Caché Studio for back-end.
I don't use Caché Studio to edit static files.
 

I'm actually doing experiments using my Port library for managing export/import operations.

 

About how I keep the server code close to it's client counterpart is quite simple. By default Port exports project following the template /CacheProjects/{NAMESPACE}/{PROJECT}, so instead of depending on it, I overwrite that path to /my-repo/server.
From this point exported files will follow:
/my-repo/server/cls/My/Class.cls

/my-repo/server/cls/Another/Deep/Package/Whatever.cls

/my-repo/server/int/myroutine.int
/my-repo/server/mac/myroutine.mac

/my-repo/server/dfi/mydef.dfi

/my-repo/server/int/myinclude.inc
And so on, for every recognized Caché file format.
Now notice that I didn't said anything about static files. That's where a module bundler like Webpack is used to orchestrate the client-side workflow.
Now Caché only needs to send readable data back to the SPA (preferably JSON using %CSP.REST).
When the project repo  reaches a milestone. I build a release to actually export the files to the path, like this:
 

/my-repo/server/web/app/bundle-[chunkhash].js

/my-repo/server/web/app/bundle-[chunkhash].css


Since  [chunkhash] is unique per build, the consumer shouldn't have any issues with browser cache.
Now there's an issue: the bundled files still aren't inside the CSP folder, so I need to import the project back to Studio using Port.
Files are imported using UDL instead of XML. But Port always keep a project XML up-to-date along with the UDL code.
As you can see I can work with Caché code AND Client-code, alternating between both editors, thus keeping their own development scope, even though their code remain inside the same repo.

Rubens Silva · Jun 20, 2017 go to post

http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY…
Check the "Configuring an ECP Application Server" section. However, I advise you to read everything.
Remote databases should be created on the client instance (A). They represent the connection with the local database on the server instance (B).
If you follow the steps from the guide, when creating a remote database you'll notice that the wizard will ask you for a server, that's where you should select the ECP server you just defined.
Now create a local database on the client instance if you need to store something on it (like, let's say... code). And finally create a namespace, this is where you can define where to use local or remote database (GLOBALS AND/OR ROUTINES).
CAUTION: If you configure globals to use the remote database, any changes you do on the client instance will reflect on it's provider counterpart. The same applies for routines.
EDIT: Answering your question. No, as I far as I know, using this method you can't have more than one remote database for each global or routines. But I think you can workaround it by defining another namespace, from another remote database that links a local database on instance C and map what you want.
But you'll be responsible for dealing with name conflicts.

Rubens Silva · Jun 20, 2017 go to post

cls/My/Deep/Class.cls

I don't think subdirectories should be applied for routines, originally routines aren't supposed to have submodules or subpackages and dots might be part of their name. Also if you need some complexity, you wouldn't prefer using routines but classes to keep things organized.
 

I DO NOT recommend using src, unless you want to mix both back-end and front-end code. 
Or you want to keep the server code in a separated repository.
Here is a scaffolding based on React for better understanding.
my-app-project /
   package.json
    server
          cls
          mac
          int
          csp <- this is our build path, nothing should be added manually because everything is handle by the bundler.

    scripts /

         test.js
         build.js
        dev.js
   config /
         webpack.config.dev.js
         webpack.config.prod.js
         webpack.config.test.js
   src /

         components
               Header
                   index.js
                   Header.js                   
                   HeaderSearchBar.js
               Footer

                   index.js
                   Footer.js
                   FooterCopyright.js
                AppContainer
                    index.js
                   AppContainer.js
          containers
                App.js
           tests
               components

                  Header
                      Header.js                   
                      HeaderSearchBar.js
                  Footer
                      Footer.js
                      FooterCopyright.js
                   AppContainer
                       index.js
                   AppContainer


                              
You can use folders to separate both client and server codes inside the same project. You can even structure your project

using a monorepo approach if you want to keep multiple application modules together.
                  

Now since React will be using webpack's hot module reloading along with webpack-dev-middleware that builds everything within the memory, your Caché server should only work following SPA conventions and providing consumable data.
There's a catch though, whenever the developer builds a new version (using webpack.config.prod), it's mandatory to delete the older bundle and import the project back to Caché to keep the source in sync on the server and the project.
     

Rubens Silva · Jun 14, 2017 go to post

That's what I though, but it seemed slow.
Isn't there anyway to move the stream cursor back without erasing  the existing data?
And if not, couldn't that be done using a device directly?

Rubens Silva · Jun 14, 2017 go to post

Use #server if you want to wait for a response, but be warned though that JavaScript is one threaded, and using #server with a left-hand side (LHS) variable would lock the current thread.

If you don't specify a LHS you can continue using #call, that will inform the CSP Gateway to execute the request asynchronously.

More details here: http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY…


If you need something closer to a callback then must do your callback on the server using &js< /* your javascript code here */ >. This way the server will return a "runtime" JavasScript to execute remaining operations on the client side.