#ObjectScript

0 Seguidores · 219 Publicaciones

InterSystems ObjectScript es un lenguaje de programación para operar con datos mediante cualquier modelo de datos que se encuentre en InterSystems Data Platform (Objetos, relacionales, valores clave, documentos, globales) y desarrollar lógica empresarial para aplicaciones del lado del servidor en InterSystems Data Platform.

Consultar la documentación.

Anuncio Esther Sanchez · nov 28, 2022

¡Hola desarrolladores!

Si os gustan los famosos concursos de Adviento "Advent of Code", con sus retos de programación diarios hasta el día de Navidad, os encantará nuestra versión.

➡️ Participad en el Advent of Code 2022 de InterSystems y podréis ganar alguno de los premios en metálico que hay para los ganadores. ¡Animaos a poner a prueba vuestros conocimientos de ObjectScript! 😄


1
0 148
Artículo Nancy Martínez · nov 14, 2022 2m read

¡Amigos!

Hace poco encontré en la Comunidad varios comandos de ObjectScript de una única línea y creo que estaría genial no perderlos y recopilar más.

Por eso he decidido recoger unos cuantos ejemplos, ponerlos en este proyecto en Open Exchange, y compartirlos con vosotros!

Así es como podéis usarlos.

1. Crear la configuración SSL del cliente

set $namespace="%SYS", name="DefaultSSL" do:'##class(Security.SSLConfigs).Exists(name) ##class(Security.SSLConfigs).Create(name)

Útil si necesitáis leer contenido de una URL.

0
0 142
Artículo Luis Angel Pérez Ramos · oct 28, 2022 8m read

Acceso a Producción Programática

Para editar producciones programáticamente (interfaces) puedes usar una combinación de APIs de Interoperabilidad y consultas SQL.

Namespace actual

Es importante conocer el namespace y la producción en la que estás trabajando.

// Object script 
// El namespace activo se guarda en esta variable
$$$NAMESPACE 
// Imprimir el namespace
Write $$$NAMESPACE
# Python
import iris
# Este método retorna el namespace activo
iris.utils._OriginalNamespace()
# Imprimir el namespace
print(iris.utils._OriginalNamespace())
>>> DEMONSTRACIÓN

Producción activa

También, es importante conocer el nombre de tu producción. Puedes recuperar la producción activa en un namespace usando las siguientes APIs

// ObjectScript
USER>ZN "DEMONSTRATION"
// Obtener la última producción en ejecución
DEMONSTRATION>W ##class(Ens.Director).GetActiveProductionName()
>>> Hospital.HospitalProduction
#  Python
import os
os.environ['IRISNAMESPACE'] = 'DEMONSTRATION'
import iris
active_production = iris.cls('Ens.Director').GetActiveProductionName()
print(active_production)
>>> Hospital.HospitalProduction

Cómo encontrar elementos en una producción

Puedes usar Objectscript o Python para encontrar elementos activos en la producción

1. Consulta SQL para obtener elementos en producción

SELECT Name FROM Ens_Config.Item Where Production = 'Hospital.HospitalProduction'
-- 
['From_Athena_Multi']
['From_Athena_Multi_Router']
['From_Cerner_ADT']
['From_Cerner_ADT_Router']
['From_Cerner_Orders']
['From_Cerner_Orders_Router']
['From_Dictaphone_Results']
['From_Dictaphone_Results_Router']
['From_Lab_Results']
['From_Lab_Results_Router']
['From_Radiology_Results']
['From_Radiology_Results_Router']
['HS.IHE.XDSb.DocumentSource.Operations']
['HS.IHE.XDSb.Repository.Operations']
['To_Cerner_Results']
['To_Dictaphone']
['To_Intellilab']
['To_Lab']
['To_Radiology']
-- 

2. Consulta SQL para obtener elementos activos en producción

SELECT Name, ClassName 
FROM Ens_Config.Item 
WHERE Production = 'Hospital.HospitalProduction' 
  AND Enabled = 1

-- 
Name	                                ClassName
To_Radiology	                        EnsLib.HL7.Operation.FileOperation
To_Lab	                                EnsLib.HL7.Operation.FileOperation
To_Dictaphone	                        EnsLib.HL7.Operation.FileOperation
From_Cerner_ADT	                        EnsLib.HL7.Service.FileService
From_Cerner_ADT_Router	                EnsLib.HL7.MsgRouter.RoutingEngine
From_Radiology_Results_Router	        EnsLib.HL7.MsgRouter.RoutingEngine
From_Lab_Results_Router	                EnsLib.HL7.MsgRouter.RoutingEngine
From_Dictaphone_Results_Router	        EnsLib.HL7.MsgRouter.RoutingEngine
To_Intellilab	                        EnsLib.HL7.Operation.FileOperation
To_Cerner_Results	                    EnsLib.HL7.Operation.FileOperation
From_Cerner_Orders_Router	            EnsLib.HL7.MsgRouter.RoutingEngine
From_Athena_Multi_Router	            EnsLib.HL7.MsgRouter.RoutingEngine
HS.IHE.XDSb.DocumentSource.Operations	HS.IHE.XDSb.DocumentSource.Operations
-- 

3. Acceso por ObjectScript a elementos de la Producción

// ObjectScript 
// Acceso para obtener todos los elementos de una producción activa
// Devuelve la lista de elementos
ClassMethod ListItemsInProduction()
{
    Set productionName =  ##class(Ens.Director).GetActiveProductionName()
    Set items = []
    &sql(Declare curr cursor FOR Select Name into :newId from Ens_Config.Item Where Production = :productionName)
    &sql(OPEN curr)
    For {
        &sql(FETCH curr)
        Quit:SQLCODE
        Do items.%Push(newId)
    }
    &sql(CLOSE curr)
    quit items
}

>>> zw ##class(ISC.SE.ProductionTools).ListItemsInProduction()

["From_Athena_Multi","From_Athena_Multi_Router","From_Cerner_ADT","From_Cerner_ADT_Router","From_Cerner_Orders","From_Cerner_Orders_Router","From_Dictaphone_Results","From_Dictaphone_Results_Router"
,"From_Lab_Results","From_Lab_Results_Router","From_Radiology_Results","From_Radiology_Results_Router","HS.IHE.XDSb.DocumentSource.Operations","HS.IHE.XDSb.Repository.Operations","To_Cerner_Results"
,"To_Dictaphone","To_Intellilab","To_Lab","To_Radiology"]  ; <DYNAMIC ARRAY>
# Python
# Obtención del entorno de datos de los elementos activos de la producción

import os
# Definición de variables de entorno
os.environ['IRISNAMESPACE'] = 'DEMONSTRATION'
import iris

def getActiveProductionItems():
    productionName = iris.cls('Ens.Director').GetActiveProductionName()
    df = iris.sql.exec("SELECT Name FROM Ens_Config.Item Where Production = '{}'".format(productionName))
    return df

production_items_df = getActiveProductionItems().dataframe()

#                                      name
# 0                       From_Athena_Multi
# 1                From_Athena_Multi_Router
# 2                         From_Cerner_ADT
# 3                  From_Cerner_ADT_Router
# 4                      From_Cerner_Orders
# 5               From_Cerner_Orders_Router
# 6                 From_Dictaphone_Results
# 7          From_Dictaphone_Results_Router
# 8                        From_Lab_Results
# 9                 From_Lab_Results_Router
# 10                 From_Radiology_Results
# 11          From_Radiology_Results_Router
# 12  HS.IHE.XDSb.DocumentSource.Operations
# 13      HS.IHE.XDSb.Repository.Operations
# 14                      To_Cerner_Results
# 15                          To_Dictaphone
# 16                          To_Intellilab
# 17                                 To_Lab
# 18                           To_Radiology

Cómo manipular producciones vía API

1. Añadir componentes

// ObjectScript
set productionName = ##class(Ens.Director).GetActiveProductionName()
//crear un nuevo servicio de archivos xml
set classname="EnsLib.XML.FileService"	//clase de este elemento
set name="NewService"			//nombre de la configuración
set item=##class(Ens.Config.Item).%New(classname)

set item.Name=name
set item.Comment = "Test Service"
set item.PoolSize = "1"
set item.Enabled = 1
do item.%Save()
//	
// Abrir la clase de la producción
// set prod="Test.configtest"	//el nombre de la producción se define manualmente
// O
set prod = productionName
set prodObj=##class(Ens.Config.Production).%OpenId(prod)
//se graba el nuevo elemento
set tSC=prodObj.Items.Insert(item)
set tSC=prodObj.SaveToClass(item)
set tSC=prodObj.%Save()

// ELIMINACIÓN del elemento anterior
set tSC = prodObj.RemoveItem(item)
set tSC = prodObj.SaveToClass()
set tSC=prodObj.%Save()
# Python
import os
os.environ['IRISNAMESPACE'] = 'DEMONSTRATION'
import iris
active_production = iris.cls('Ens.Director').GetActiveProductionName()
print("Current Production {}".format(active_production))

# Metadata sobre el componente
classname="EnsLib.XML.FileService"	 # clase de esta elemento
name="NewService"			         # nombre de la configuración
item=iris.cls('Ens.Config.Item')._New(classname) # Creación del nuevo componente
item.Name=name
item.Comment = "Test Service"
item.PoolSize = "1"
item.Enabled = 1
item._Save()

# Se carga la clase de la producción
# prod="Test.configtest"	# el nombre de la producción se define manualmente
# O se usa la producción activa anterior
prod = active_production

prodObj=iris.cls('Ens.Config.Production')._OpenId(prod)
# grabamos la producción después de insertar el elemento en ella
tSC=prodObj.Items.Insert(item)
tSC=prodObj.SaveToClass(item)
tSC=prodObj._Save()

# ELIMINAMOS el elemento anterior
tSC = prodObj.RemoveItem(item)
tSC = prodObj.SaveToClass()
tSC=prodObj._Save()

2. Deshabilitar / Habilitar Componentes

// ObjectScript
set productionName = ##class(Ens.Director).GetActiveProductionName()
set itemName = "My.Inbound.HL7"
// Obligatorio para habilitar el elemento
Set componentName = productionName _ "||" _ itemName _ "|"
// Habilitar or deshabilitar
Set enable = 1 // or 0
Do ##class(Ens.Director).EnableConfigItem(componentName, enable, 1)

/// Habilitar o deshabilitar un ConfigItem en una producción. La producción puede estar activa o no.
/// El argumento pConfigItemName proporciona el nombre del elemento de configuración para habilitar o deshabilitar
/// En el caso de múltiples coincidencias de elementos con el mismo nombre de configuración, si alguno ya está habilitado entonces
///  la opción pEnable=1 no hará nada y la opción pEnable=0 deshabilitará el elemento de la producción activa o, si no está activa, 
/// el primer elemento coincidente que esté habilitado que encuentre.
///   
/// Ver el método Ens.Director.ParseConfigName() para ver la sintáxis del string de especificación del nombre del ConfigItem.
ClassMethod EnableConfigItem(pConfigItemName As %String, pEnable As %Boolean = 1, pDoUpdate As %Boolean = 1)
# Python
import os
os.environ['IRISNAMESPACE'] = 'DEMONSTRATION'
import iris

active_production = iris.cls('Ens.Director').GetActiveProductionName()
item_name = "My.Inbound.HL7"
componentName = active_production + "||" + item_name + "|"

enable = 1 # or 0
iris.cls('Ens.Director').EnableConfigItem(componentName, enable, 1)

Estado de la producción a través de API

// ObjectScript
/// Este método devuelve el estado de la producción por los parámetros de salida.
/// pProductionName: Devuelve el nombre de la producción cuando el estado es en ejecución, suspendida o con problemas.
/// pState: Salidas del estado de la producción. Los valores válidos son:
///          $$$eProductionStateRunning == 1
///          $$$eProductionStateStopped == 2
///          $$$eProductionStateSuspended == 3
///          $$$eProductionStateTroubled == 4
Set sc = ##class(Ens.Director).GetProductionStatus(.productionName, .productionState) 
Write productionName, " -- ", productionState
import os
# Configurar el namespace a las malas
os.environ['IRISNAMESPACE'] = 'DEMONSTRATION'

import iris

# TEST 2 con variables de salida
productionName, productionState = iris.ref('productionName'), iris.ref('productionState')
status = iris.cls('Ens.Director').GetProductionStatus(productionName, productionState) 

print("Status: {}".format(status))
# ver .value
print("Production: {}".format(productionName.value))
# ver .value
print("Production State: {}".format(productionState.value))
0
1 169
Artículo Nancy Martínez · oct 18, 2022 1m read

¡Hola desarrolladores!

Para mí, una de las peores cosas de ObjectScript es tener que escribir la sentencia ##class(Class).Method() para llamar a un método de clase en código o en un terminal. Incluso envié una idea para simplificarlo en ObjectScript.

¡Pero! Hay una nueva funcionalidad en VSCode ObjectScript que acaba de ser añadida al plugin - ¡Copy Invocation!

Solo hay que poner el cursor sobre el enlace Copy Invocation que aparece encima de cada método de clase del código, hacer clic en él y la invocación se copia en el buffer:

Pegadlo donde queráis que se ejecute.

0
0 161
Anuncio Esther Sanchez · sep 21, 2022

Si no sabes por dónde empezar a aprender ObjectScript... ¡te lo ponemos fácil!

Acabamos de actualizar el curso “Getting Started with InterSystems ObjectScript” ("Introducción a InterSystems ObjectScript") con tres nuevos vídeos de 5 minutos + un ejercicio final para ayudarte a recapitular todo lo que has aprendido.

Contenido del curso:

🤝 Introducción a InterSystems ObjectScript

🤿 Análisis a fondo de los comandos y las funciones

🤔 Entendiendo los tipos de datos y las variables

👨‍💻 Creando una definición de clase

Ya puedes hacer el curso completo aquí:

🎓Getting Started with InterSystems ObjectScript >>🎓
 

Nota.- La formación oficial de InterSystems es en inglés. Si prefieres la formación en español:

1
0 166
Artículo Nancy Martínez · ago 19, 2022 1m read

¡Hola desarrolladores!

Me gustaría compartir con vosotros un truco útil para presentar código de ObjectScript en ficheros Script en VSCode.

Los ficheros Script son simplemente ficheros con líneas de ObjectScript que introducimos en cualquier sitio, por ejemplo en IRIS durante el procedimiento Docker baking.

Caso de uso típico - este es el Scriptfile

este es el Dockerfile donde lo introducimos.

Y así es como se ve normalmente en VSCode:

Screenshot 2021-08-05 at 09 51 06

Podría ser más elegante, ¿verdad?

0
0 139
Artículo Nancy Martínez · ago 2, 2022 1m read

¡Hola desarrolladores!

Solo quiero compartir una antigua pero muy relevante buena práctica en el cambio de namespaces que @Dmitry Maslennikov compartió conmigo.

Consideremos el método:

classmethod DoSomethingInSYS() as %Status

{

set sc=$$$OK

set ns=$namespace

zn "%SYS"

// try-catch in case there will be an error

try {

// do something, e.g. config change

}

catch {}

 zn ns    ; returning back to the namespace we came in the routine

return sc

}
0
0 172
Artículo Alberto Fuentes · abr 28, 2021 8m read

Introducción

Si resuelves problemas complejos en ObjectScript, probablemente tienes mucho código que funciona con los valores de %Status. Si has interactuado con clases persistentes desde una perspectiva de objetos (%Save, %OpenId, etc.), casi seguro que las ha visto. 

Un %Status proporciona una envoltura alrededor de un mensaje de error localizable en las plataformas de InterSystems. Un estado OK ($$$OK) simplemente es igual a 1, mientras que un mal estado ($$$ERROR(errorcode,arguments...)) se representa como un 0 seguido de un espacio seguido de una lista $ListBuild con información estructurada sobre el error.

$System.Status (mira la referencia de clase) proporciona varias APIs útiles para trabajar con los valores de %Status; la referencia de clase es útil y no os molestaré duplicándola aquí. También ha habido algunos otros artículos/preguntas útiles sobre el tema (consulta los enlaces al final de esta publicación). En este artículo me centraré en algunas técnicas de depuración en vez de escribir las prácticas recomendadas (de nuevo, si las estás buscando, consulta los enlaces al final).

Ejemplo de código motivador

Nota: ¡nunca escribas un código como este! :) Revisa siempre tus estados y devuélvelos/lánzalos como excepciones (por ejemplo, $$$ThrowStatus(someErrorStatus)) y hará que la depuración sea MUY sencilla.

Class DC.Demo.MaskedErrorStatus Extends %Persistent
{

Property Answer As %TinyInt;

ClassMethod Run() As %Status
{
    Set instance = ..%New()
    Set instance.Answer = 9000
    Do instance.%Save()
    
    Set instance = ..%OpenId(1,,.sc)
    Set instance.Answer = 42
    Do instance.%Save()
    
    Quit $$$OK
}

}

Cuando se ejecuta desde el terminal, se lanza una excepción; es evidente que algo salió mal.

USER>d ##class(DC.Demo.MaskedErrorStatus).Run()
 
 Set instance.Answer = 42
 ^
&lt;INVALID OREF>zRun+5^DC.Demo.MaskedErrorStatus.1

Truco #1 de depuración con %Status: $System.OBJ.DisplayError()

Siempre puedes ejecutar $System.OBJ.DisplayError() para imprimir el último estado de error que se creó. Esto funciona porque cada vez que se crea un estado de error (por ejemplo, por medio de $System.Status.Error), la variable %objlasterror se establece en ese estado. También puedes utilizar zwrite %objlasterror (de forma equivalente). En el caso anterior:

USER 2d1>d $system.OBJ.DisplayError()
ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1'

Truco #2 de depuración con %Status: seguimiento de stacks

Dentro de cada %Status hay un seguimiento del stack (pila de llamadas) en el que se creó el error. Se puede ver esto al utilizar el estado zwrite:

USER 2d1>zw %objlasterror %objlasterror="0 "_$lb($lb(5809,"DC.Demo.MaskedErrorStatus","1",,,,,,,$lb(,"USER",$lb("e^%LoadData+18^DC.Demo.MaskedErrorStatus.1^1","e^%Open+16^%Library.Persistent.1^1","e^%OpenId+1^%Library.Persistent.1^1","e^zRun+4^DC.Demo.MaskedErrorStatus.1^1","d^^^0"))))/* ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1' */

¿Quieres ver el seguimiento de stacks en el texto de error (que es más sencillo de utilizar) para cada estado (por ejemplo, utilizando $System.OBJ.DisplayError() o $System.Status.GetErrorText(someStatus))? Puedes hacerlo al establecer `%oddENV("callererrorinfo",$namespace)' a valores 1 o 2. Puedes ver el efecto aquí:

USER>set ^%oddENV("callererrorinfo",$namespace)=1
USER>d $system.OBJ.DisplayError()
ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1' [%LoadData+18^DC.Demo.MaskedErrorStatus.1:USER]
USER>set ^%oddENV("callererrorinfo",$namespace)=2
USER>d $system.OBJ.DisplayError()
ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1' [e^%LoadData+18^DC.Demo.MaskedErrorStatus.1^1 e^%Open+16^%Library.Persistent.1^1 e^%OpenId+1^%Library.Persistent.1^1 e^zRun+4^DC.Demo.MaskedErrorStatus.1^1 d^^^0:USER]
USER>k ^%oddENV("callererrorinfo",$namespace)
USER>d $system.OBJ.DisplayError()
ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1'

Ten en cuenta que esto sólo es apropiado en un entorno de desarrollo - no querrás que tus usuarios vean el interior de tu código -. En realidad, es mejor evitar mostrar los valores de %Status directamente a los usuarios. Son preferibles los mensajes de error específicos de la aplicación, más fáciles de utilizar. Pero ese es un tema para otro día.

Truco #3 de depuración con %Status: el elegante zbreak

Aquí es donde se pone difícil - en el caso de este fragmento de código, la causa raíz es un %Status no verificado de %Save() que antes estaba en el fragmento de código. Es fácil imaginar un ejemplo mucho más complicado en el que encontrar lo que ha fallado es realmente difícil, especialmente si se trata de un error que se produce en algún punto del código de la plataforma. Mi método preferido para gestionar esto, sin saltar a un depurador interactivo, es utilizar un comando zbreak en el terminal:

USER>zbreak *%objlasterror:"N":"$d(%objlasterror)#2":"set ^mtemptl($i(^mtemptl))=%objlasterror"

...¿qué quiere decir eso?

zbreak <cada vez que %objlasterror cambie>:<no haga nada en el depurador mismo>:<mientras %objlasterror esté definido y tenga un valor (por ejemplo, no ha pasado de estar definido a estar indefinido)>:<ejecuta el código para establecer el siguiente subíndice de un global con un subíndice entero que no está en journal (porque comienza con mtemp, en caso de que estemos en una transacción cuando se crea %Status y se haya revertido para cuando observemos el registro; también, con mis iniciales como parte del global para que si alguien lo encuentra en el código comprometido o en una base de datos inflada sepa que debe llamarme) al estado de error>

Nota adicional sobre zbreak: puedes ver los breakpoints/watchpoints definidos actualmente si ejecutas "zbreak" sin argumentos, y puedes/debes desactivar estos breakpoints cuando hayas terminado con ellos ejecutando break "off", por ejemplo:

USER>zbreak
BREAK:
 No breakpoints
%objlasterror F:E S:0 C:"$d(%objlasterror)#2" E:"set ^mtemptl($i(^mtemptl))=%objlasterror"
USER>break "off"
USER>zbreak
BREAK:
 No breakpoints
 No watchpoints

Entonces, ¿qué sucede cuando el método problemático se ejecuta con el watchpoint establecido?

USER>zbreak *%objlasterror:"N":"$d(%objlasterror)#2":"set ^mtemptl($i(^mtemptl))=%objlasterror"
 
USER>d ##class(DC.Demo.MaskedErrorStatus).Run()
 
 Set instance.Answer = 42
 ^
&lt;INVALID OREF>zRun+5^DC.Demo.MaskedErrorStatus.1
 
USER 2d1>zw ^mtemptl
^mtemptl=6
^mtemptl(1)="0 "_$lb($lb(7203,9000,127,,,,,,,$lb(,"USER",$lb("e^zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1^1","e^%ValidateObject+3^DC.Demo.MaskedErrorStatus.1^4","e^%SerializeObject+3^%Library.Persistent.1^1","e^%Save+4^%Library.Persistent.1^2","d^zRun+3^DC.Demo.MaskedErrorStatus.1^1","d^^^0"))))/* ERROR #7203: Datatype value '9000' greater than MAXVAL allowed of 127 */
^mtemptl(2)="0 "_$lb($lb(7203,9000,127,,,,,,,$lb(,"USER",$lb("e^zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1^1","e^%ValidateObject+3^DC.Demo.MaskedErrorStatus.1^4","e^%SerializeObject+3^%Library.Persistent.1^1","e^%Save+4^%Library.Persistent.1^2","d^zRun+3^DC.Demo.MaskedErrorStatus.1^1","d^^^0")),"0 "_$lb($lb(5802,"DC.Demo.MaskedErrorStatus:Answer",9000,,,,,,,$lb(,"USER",$lb("e^EmbedErr+1^%occSystem^1"))))))/* ERROR #7203: Datatype value '9000' greater than MAXVAL allowed of 127-   > ERROR #5802: Datatype validation failed on property 'DC.Demo.MaskedErrorStatus:Answer', with value equal to "9000" */
^mtemptl(3)="0 "_$lb($lb(7203,9000,127,,,,,,,$lb("zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1","USER",$lb("e^zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1^1","e^%ValidateObject+3^DC.Demo.MaskedErrorStatus.1^4","e^%SerializeObject+3^%Library.Persistent.1^1","e^%Save+4^%Library.Persistent.1^2","d^zRun+3^DC.Demo.MaskedErrorStatus.1^1","d^^^0"))))/* ERROR #7203: Datatype value '9000' greater than MAXVAL allowed of 127 */
^mtemptl(4)="0 "_$lb($lb(5802,"DC.Demo.MaskedErrorStatus:Answer",9000,,,,,,,$lb("EmbedErr+1^%occSystem","USER",$lb("e^EmbedErr+1^%occSystem^1"))))/* ERROR #5802: Datatype validation failed on property 'DC.Demo.MaskedErrorStatus:Answer', with value equal to "9000" */
^mtemptl(5)="0 "_$lb($lb(7203,9000,127,,,,,,,$lb("zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1","USER",$lb("e^zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1^1","e^%ValidateObject+3^DC.Demo.MaskedErrorStatus.1^4","e^%SerializeObject+3^%Library.Persistent.1^1","e^%Save+4^%Library.Persistent.1^2","d^zRun+3^DC.Demo.MaskedErrorStatus.1^1","d^^^0")),"0 "_$lb($lb(5802,"DC.Demo.MaskedErrorStatus:Answer",9000,,,,,,,$lb("EmbedErr+1^%occSystem","USER",$lb("e^EmbedErr+1^%occSystem^1"))))))/* ERROR #7203: Datatype value '9000' greater than MAXVAL allowed of 127-   > ERROR #5802: Datatype validation failed on property 'DC.Demo.MaskedErrorStatus:Answer', with value equal to "9000" */
^mtemptl(6)="0 "_$lb($lb(5809,"DC.Demo.MaskedErrorStatus","1",,,,,,,$lb(,"USER",$lb("e^%LoadData+18^DC.Demo.MaskedErrorStatus.1^1","e^%Open+16^%Library.Persistent.1^1","e^%OpenId+1^%Library.Persistent.1^1","e^zRun+4^DC.Demo.MaskedErrorStatus.1^1","d^^^0"))))/* ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1' */

Hay un poco de ruido ahí, pero el problema clave salta a la vista:

/* ERROR #7203: Datatype value '9000' greater than MAXVAL allowed of 127 */

¡Debería haber sabido que no debería utilizar %TinyInt! (Y, lo que es más importante, siempre debes verificar los valores de %Status devueltos por los métodos que llamas).

Lecturas relacionadas

Mis patrones de codificación preferidos para la gestión y reporte de errores
%Status frente a otros valores de retorno en los métodos ObjectScript de Caché
Sobre %objlasterror
Cómo configurar $$$envCallerErrorInfoGet
Fragmentos de código para gestión de errores de ObjectScript
Comando ZBREAK

1
1 220
Artículo Ricardo Paiva · jul 7, 2022 15m read

Si tuvieras la oportunidad de cambiar algo en el Visualizador de Mensajes de Interoperabilidad en IRIS, ¿qué harías?

Después de publicar el artículo Panel de Control "IRIS History Monitor", recibí algunos comentarios muy interesantes y varias peticiones. Una de ellas fue un Visualizador de Mensajes mejorado.
 
Si aún no lo has hecho, echa un vistazo al proyecto: merece la pena que le dediques un rato, y además ganó el 3er premio (Bronce) a Los mejores desarrolladores y aplicaciones de InterSystems Open Exchange en 2019.
 
Empecé a pensar algunas ideas sobre lo que me gustaría incluir en el "nuevo" Visualizador de Mensajes pero ¿cómo podría mostrar estos recursos de la forma más rápida y sencilla?
0
0 174
Artículo Muhammad Waseem · jun 28, 2022 3m read

¡Hola Comunidad!

Recientemente, migré una serie de repositorios de Objectscript de formato XML a UDL. Después de la migración, me decepcionó un poco la presentación en la interfaz web de GitLab.

Dado que la sintaxis de Objectscript es compatible con GitHub, pensé que también sería compatible con GitLab. Desafortunadamente, la librería utilizada por GitLab para resaltar el código no tiene una extensión para admitir Objectscript.

GitLab usa la librería Rouge, que puede resaltar más de 200 idiomas. Es una librería escrita en Ruby (consulta la página de GitHub), pero Objectscript no está en la lista. Así que decidí desarrollar una extensión de Objectscript.

El subproyecto Rouge está disponible en mi página de GitHub. Para propósitos de prueba, preparé un repositorio objectscript-syntax-for-gitlab para crear fácilmente un contenedor con un entorno Ruby listo y una página de prueba.

Estado de desarrollo actual:

CaracterísticasEstado
&JS<>implementado
&HTML<>implementado
&SQL<>Parcial
Método con lenguaje Pythonimplementado
Método con lenguaje Javascriptimplementado
XData contiene XMLimplementado
XData contiene JSONimplementado
Almacenamiento (definición XML)implementado
ConsultaParcial
Etiqueta en métodoimplementado
RutinaImplementado
CSPAún no
Archivo de macros (INC)Aún no

Instalación

Simplemente clona el repositorio, extrae la imagen y empieza:

git clone https://github.com/lscalese/objectscript-syntax-for-gitlab.git
cd objectscript-syntax-for-gitlab
docker pull ghcr.io/lscalese/objectscript-syntax-for-gitlab:latest
docker-compose up -d

Prueba

Cuando se inicia el contenedor, puedes abrir esta URL para mostrar una página de prueba: http://localhost:9592/objectscript

Es posible seleccionar un tema diferente en la parte inferior de la página.

Si quieres probar el resaltado con tu propio código, puedes usar este pequeño formulario: http://localhost:9692/csp/irisapp/lscalese.objectscript4gitlab.test.cls (el login/password es el estándar _system/SYS)

Copia/pega tu código en el área de texto y haz clic en enviar. Tu código se abrirá en una nueva pestaña Advertencia: El navegador podría bloquear la apertura de la nueva página. Por favor acepta si es necesario.

El botón “reset” permite restaurar la página de prueba por defecto.

No dudes en enviarme comentarios sobre tus pruebas creando un Issue en mi GitHub. Necesito información para detectar errores y mejorar tanto como sea posible antes de enviar un pull request a Rouge.

Espero que este proyecto sea de utilidad para la comunidad y merezca vuestro apoyo ;D

¡Gracias!

0
0 173
Artículo Alberto Fuentes · jul 29, 2019 4m read

¡Hola a tod@s!

Hoy os traigo un artículo de Kyle Baxter sobre búsquedas de texto libre que vale la pena guardar como referencia :)

¿Os gustaría buscar de forma eficiente campos de texto libres almacenados en vuestra aplicación? ¿Lo habéis intentado alguna vez pero no habéis encontrado una manera que os ofrezca un buen rendimiento? Hay un truco especial que resuelve el problema :)

Como es habitual, si preferís la versión TL;DR (Demasiado largo, no lo he leído), podéis ir directamente al final del artículo, pero preferiríamos que leyeseis el artículo entero para evitar herir sentimientos.

1
0 331
Artículo Muhammad Waseem · jun 14, 2022 2m read

Esta es una comparación creada en Python y Objectscript en InterSystems IRIS.

El objetivo es comparar la velocidad para enviar y recibir mil solicitudes/mensajes desde un BP a un BO en Python y en Objectscript.

Consultar https://github.com/LucasEnard/benchmark-python-objectscript para más información.

IMPORTANTE : Aquí están los resultados del tiempo en segundos, para enviar 1000 mensajes de ida y vuelta desde un bp a un bo usando Python, Graph Objectscript y Objectscript.

Los mensajes de cadena se componen de diez variables de cadena.

Los mensajes de objeto se componen de diez variables de objeto, cada objeto como su propio int, float, str y List(str).

Cadenas de mensajesTiempo (segundos) para 1000 mensajes de ida y vuelta
Python BP1.8
BPL1.8
ObjectScript1.4
Objetos de mensajesTiempo (segundos) para 1000 mensajes de ida y vuelta
Python BP3.2
BPL2.1
ObjectScript1.8

La función en la fila tiene x veces el tiempo de la función en la columna:

Cadenas de mensajesPythonBPLObjectScript
Python111.3
BPL111.3
ObjectScript0.760.761

Por ejemplo, la primera fila nos dice que el tiempo de cadena de Python es 1 vez el tiempo de la función de cadena de gráficos de Objectscript y 1,3 veces el tiempo de la función de cadena de Objectscript.
(gracias a la primera tabla podemos verificar nuestros resultados:
1.3 * 1.4 = 1.8
1.3 es la x en la tabla en la última columna de la primera fila, 1.4s es el tiempo para los mensajes de cadena en Objectscript vistos en la primera tabla de esta sección y 1.8s es de hecho el tiempo para los mensajes de cadena en python que podemos encontrar buscando en la primera tabla de esta sección o mediante el cálculo como se mostró antes).

Tenemos la función en la fila que tiene x veces el tiempo de la función en la columna:

Cadenas de mensajesPythonBPLObjectScript
Python11.51.8
BPL0.6611.2
ObjectScript0.550.831
0
0 91
Artículo Muhammad Waseem · jun 1, 2022 3m read

¡Hola comunidad!

Esta publicación es una introducción a mi aplicación iris-fhir-client en Open Exchange.

Usando Python embebido y con la ayuda de la librería fhirpy, iris-fhir-client puede conectarse a cualquier servidor FHIR abierto.
Obten información de recursos por terminal y mediante el uso de la aplicación web CSP.

0
0 312
Artículo Ricardo Paiva · ago 11, 2021 3m read

Me encontré con un interesante caso de uso de ObjectScript con una solución general que quería compartir.

Caso de uso:

Tengo una matriz JSON (específicamente, en mi caso, una matriz de problemas de Jira) que quiero agregar en algunos campos, por ejemplo: categoría, prioridad y tipo de problema. Después quiero combinar los agregados en una lista simple con el total de cada uno de los grupos. Por supuesto, para la agregación, tiene sentido utilizar una matriz local en el formulario:

agg(category, priority, type) = total

De tal manera que para cada registro en la matriz de entrada simplemente puedo hacer lo siguiente:

Do $increment(agg(category, priority, type))

Pero, cuando haya hecho la agregación, quiero conseguir un formulario más fácil sobre el que iterar, como una matriz con subíndices enteros:

summary = n
summary(1) = $listbuild(total1, category1, priority1, type1)
...
summary(n) = $listbuild(totalN, categoryN, priorityN, typeN)

Solución básica:

El enfoque sencillo es simplemente tener tres bucles "For" anidados con $Order, por ejemplo:

Set category = ""
For {
    Set category = $Order(agg(category))
    Quit:category=""
    
    Set priority = ""
    For {
        Set priority = $Order(agg(category,priority))
        Quit:priority=""
        
        Set type = ""
        For {
            Set type = $Order(agg(category,priority,type),1,total)
            Quit:type=""
            
            Set summary($i(summary)) = $listbuild(total,category,priority,type)
        }
    }

}

Esto es lo que empecé a hacer, pero es mucho código, y si tuviera más dimensiones que agregar se volvería difícil de manejar rápidamente. Esto me hizo preguntarme: ¿hay una solución general para conseguir lo mismo? ¡Resulta que sí la hay!

Mejor solución con $Query:

Decidí que usar $query podría ayudar. Ten en cuenta que esta solución asume una capacidad uniforme de los subíndices/valores en toda la matriz local, sucederían cosas extrañas si se vulnerara esta suposición.

ClassMethod Flatten(ByRef deep, Output flat) [ PublicList = deep ]
{
    Set reference = "deep"
    For {
        Set reference = $query(@reference)
        Quit:reference=""
        Set value = $listbuild(@reference)
        For i=1:1:$qlength(reference) {
            Set value = value_$listbuild($qsubscript(reference,i))
        }
        Set flat($i(flat)) = value
    }
}

Así que el fragmento de código anterior se sustituye por:

Do ..Flatten(.agg,.summary)

Hay que tener en cuenta algunas cosas sobre esta solución:

  • deep necesita estar en la PublicList de $query para ser capaz de operar en ella
  • en cada iteración, reference se cambia para hacer referencia al siguiente conjunto de subíndices que tenga un valor en deep, por ejemplo el valor podría ser: deep("foo","bar")
  • $qlength devuelve el número de subíndices en reference
  • $qsubscript devuelve el valor del enésimo subíndice de reference
  • Cuando las listas $listbuild están concatenadas, el resultado es una lista $listbuild válida con las listas combinadas (¡esto es mucho mejor que usar cualquier otro separador!)

Resumen

$query, $qlength y $qsubscript son útiles para lidiar con matrices globales/locales de capacidad arbitraria.

Lecturas adicionales

$Query: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_FQUERY
$QSubscript: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=RCOS_fqsubscript
$QLength: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=RCOS_fqlength

1
0 201
Artículo Pierre-Yves Duquesnoy · mayo 3, 2022 14m read

Encontrar errores en tu código o examinar un comportamiento inesperado es el principal objetivo de la depuración.

Trataré de actualizar las herramientas tradicionales aparte de las ayudas que tienen Studio, VScode, Serenji... Las herramientas básicas que han estado ahí antes de que tu EDI preferido lo utilizara en segundo plano.

0
2 193
Anuncio David Reche · mayo 3, 2022

Hola comunidad, os anuncio el lanzamiento de la versión 1.8.0 de la extensión VS Code, que contiene las siguientes mejoras y correcciones de fallos que paso a resumir.

La más importante es el soporte a archivos de proyecto del lado del servidor, es una funcionalidad similar a la de los proyectos de Studio, como los más viejos del lugar recordarán. Si trabajas en el lado del cliente, VS Code ya tiene buenas funcionalidades para la gestión de proyectos. Puedes simplemente usar una carpeta como un proyecto, o usar workspaces multi-raíz. Pero si trabajas en el lado del servidor, podrás apreciar algunas mejoras en la gestión de artefactos. Puedes saber más en el nuevo capítulo Projects de la documentación.

0
0 132
Artículo Rizmaan Marikar · abr 21, 2022 13m read

Hay varias maneras de generar ficheros Excel usando tecnología InterSystems: por ejemplo utilizando informes generados con InterSystems Reports, o los antiguos informes ZEN, o incluso haciendo uso de librerías Java de terceros. Las posibilidades son casi infinitas.

Pero, ¿qué pasa si quieres crear una sencilla hoja de cálculo sólo con ObjectScript? (sin aplicaciones de terceros)

En mi caso, necesito generar informes que contengan muchos datos sin procesar (a los financieros les encantan), pero mi antiguo informe ZEN fallaba y me da lo que me gusta llamar un "archivo con cero bytes". Básicamente, Java se queda sin memoria y provoca una sobrecarga en el servidor de informes.

0
0 189
Anuncio David Reche · abr 6, 2022

InterSystems anuncia la versión 2.0.0 del Language Server para VS Code. Language Server incrementa la extensión VS Code ObjectScript para ofrecer un mejor resaltado de sintaxis, documentación embebida, autocompletado de código y más.

Todos los detalles están disponibles en el archivo README del repositorio de GitHub. La versión 2.0.0 añade soporte a varias arquitecturas de plataformas, incluyendo M1 Macs! También reduce el tamaño del paquete, mejora el coloreado del SQL y arregla otros problemas que se detallan en el CHANGELOG. 

0
0 142
Anuncio David Reche · abr 5, 2022

Me complace anunciar el lanzamiento de la versión 1.4.4 de la extensión VS Code, que contiene las siguientes mejoras y correcciones de fallos. Los enlaces que llevan a las issues de GitHub han sido corregidos. Como siempre, si ya tienes la extensión instalada, VS Code debería actualizar automáticamente tu extensión. Si no la tienes instalada, sigue estas instrucciones para empezar. 

Registro de cambios

0
0 120
Artículo Daniel Aguilar · feb 25, 2022 6m read

Hola!

Llevaba un tiempo queriendo dedicarle un rato para implementar alguna DLL o algo que pudiese usar desde Caché y al final he sacado un huequillo, si te interesa poder producir mensajes que se envíen a Kafka de una manera rápida estás en el lugar adecuado ;-)

Antes de daros la chapa con lo que vamos a ver os hago un resumen para que decidáis si os puede interesar leer el artículo.

En este artículo nos vamos a centrar "solo" en la parte de producir mensajes y enviarlos a Kafka:

¿Cómo funciona?

Uso una DLL de .Net (Netframework 4.5)  que he hecho (está dentro de la carpeta dll del repositorio)

0
0 977
Pregunta Arturo Masero · feb 17, 2022

Hola compañeros/as, necesito de vuestra sabiduría.

En mi compañía programamos en ObjectScript y .int todo el día. Cuando entré nadie sabía apenas nada de clases y su uso era prácticamente sólo para definición de almacenamiento. Durante mi autoaprendizaje en caché descubrí que la programación orientada a objetos y el desarrollo de clases era posible en caché y comencé a codificar en .cls . Al ser algo autodidacta, puede que tenga algunas dudas básicas que se me hayan pasado en mis lecturas de documentación.

2
0 196
Artículo Kurro Lopez · feb 14, 2022 4m read

Viajar en el tiempo es como visitar París. No puedes simplemente leer la guía, tienes que arrojarte. Come la comida, usa los verbos equivocados, recibe el doble de cargos y terminas besando a completos desconocidos.

El Doctor

Vamos ahora a viajar por el tiempo, osea, vamos a ver fechas futuras y pasadas y como calcularlas en diferentes formatos. La TARDIS no espera, ponte a los mandos y sujétate fuerte.

Travel in TARDIS

0
0 287
Artículo Alberto Fuentes · ene 27, 2022 6m read

@Ming Zhou hizo una buena pregunta: ¿Cómo conseguir todas las propiedades definidas en una clase desde %Dictionary.ClassDefinition? y la respuesta resume exactamente por qué ObjectScript es mi lenguaje favorito.

Cuando describo por primera vez ObjectScript o IRIS a alguien, siempre explico que puedes escribir una clase, compilarla, obtener una tabla y trabajar con tus datos desde una perspectiva orientada a objetos o relacional - la que resulte más natural. En cualquier caso, es sólo un fino envoltorio alrededor de los Globals, las estructuras de datos super rápidas existentes por debajo, y que también puedes usar cuando realmente necesitas ese acelerón extra.

0
0 183
Artículo Jose-Tomas Salvador · sep 6, 2021 2m read

Desde hace ya algunos años he echado en falta poder ofrecer, a todos los que se interesan por ObjectScript, un tutorial más o menos completo de iniciación a ObjectScript. Algo que pueda ayudar más y facilitarles las cosas a esos nuevos desarrolladores que se acercan a nuestra tecnología... algo intermedio, a medio camino entre el típico "Hola Mundo", que no te permite ir mucho más allá, y el "Curso Avanzado de Programación", que te resulta imposible por carecer de tiempo o medios.

Si existiese algo que de verdad sirviera no sólo como introducción al ecosistema, sino cómo empujón y punto de partida para empezar a hacer cosas de verdad en ObjectScript y avanzar por ti mismo... ¿no sería estupendo?

3
0 526