Lenguaje de scripting #
El lenguaje de scripting es un lenguaje creado específicamente para programar validaciones personalizadas en capturas de datos. Para cada tipo de dato definido en el Data Model, es posible generar una verificación personalizada a la hora de capturar el dato desde un cliente. Por ejemplo, se pueden verificar cosas simples (como el largo de un nombre de usuario) o más complejas (como el dígito de validación y formato de una cédula de identidad uruguaya).
Ejemplo - Validación de longitud en un nombre de usuario:
function validateUserName (value) do
if len(value) < 6 or len(value) > 20 then
return "La longitud del nombre de usuario debe estar entre 6 y 20 caracteres"
end
end
addValidations(validateUserName)
En la imagen que aparece a continuación observamos el entorno de trabajo y el editor del lenguaje:
Entre las particularidades que posee el lenguaje, podemos mencionar que no es necesario utilizar ;
para cerrar una linea de código. Esto aplica tanto para la asignación de variables, como con el resto de las sentencias y comandos (directamente se puede utilizar un salto de linea).
Variables #
El lenguaje de scripting admite variables del tipo número, texto y boolean (valores lógicos). No es necesario declararlas previamente, sino que pueden ser asignadas directamente. Los nombres de las variables pueden indicarse utilizando estilos como CamelCase o snake_case si se desea. Ambos se aplican a frases o palabras compuestas de la siguiente manera:
CamelCase: se utilizan mayúsculas en la primera letra de las palabras para identificarlas correctamente en la frase, aunque es posible exceptuar esta regla en la primera palabra. Como ejemplos, podemos nombrar
MultipleWordVariable
omultipleWordVariable
.snake_case: las palabras se separan utilizando guiones bajos, como por ejemplo
multiple_word_variable
.
Número #
Es posible asignar números enteros o decimales a variables, e incluso operaciones numéricas.
Ejemplos:
var1 = 35.28745
var2 = 77 + 48/3
Texto #
Las cadenas de texto que se asignen a variables deben estar encerradas entre comillas dobles ""
para su correcto empleo. Las comillas también pueden ser usadas de forma literal dentro de la cadena, utilizando la barra invertida \
para escaparlas. De esta forma, el lenguaje las considera como parte de la cadena en vez de convertirlas en delimitadores del contenido.
Ejemplos:
stringVar = "This is a string"
stringVarWithQuotes = "This is a quoted \"String\""
Texto multilínea #
Se permite asignar texto multilínea a variables como se ilustra a continuación:
Ejemplo:
multiLineText = "Multi Line
Text"
Valores lógicos #
Las palabras clave true
y false
están reservadas como valores booleanos, y pueden ser asignados a variables de la misma manera que los anteriores.
Ejemplos:
varBoolTrue = true
varBoolFalse = false
Funciones #
Las salidas de funciones pueden ser asignadas a variables.
Ejemplo:
functionVar = fncall(127)
Alcance de variables #
El alcance o ámbito de una variable determina en qué partes del programa puede ser utilizada:
- globales (por fuera de cualquier declaración de función), o
- internas en una función.
En el primer caso, si se realizan modificaciones dentro de una función únicamente se verán aplicadas dentro de la misma (no globalmente). Por otro lado, las variables internas de las funciones solamente existen dentro de estas.
Comentarios #
Para realizar comentarios se utiliza el caracter #
, ya sea al comienzo de una línea vacía o a continuación de una que contenga código.
Ejemplos:
# This is a comment.
balance = 224.22 # This is another comment.
Toda la primera línea del código de arriba constituye un comentario, mientras que en la segunda línea comienza luego de balance = 224.22
.
Es importante destacar que la única forma de realizar comentarios multilínea es colocar el carácter #
en cada una de las que se desee comentar.
Operadores aritméticos #
El lenguaje admite los operadores aritméticos clásicos a lo largo del código. Los operadores multiplicación y división poseen mayor precedencia frente a suma y resta.
Operadores disponibles:
Descripción | Operador |
---|---|
Suma | + |
Resta | - |
Multiplicación | * |
División | / |
No es posible utilizar paréntesis para agrupar operaciones.
Concatenación #
El operador suma permite concatenar dos o más cadenas de texto entre sí, o textos con números.
Ejemplos:
concatText = "My name is " + "Ricardo"
outstandingNumber = 2487224
textWithNumber = "I'm fascinated by the number " + outstandingNumber
En estos ejemplos, concatText
y textWithNumber
contienen los valores My name is Ricardo
y I'm fascinated by the number 2487224
, respectivamente.
Comparadores #
Mediante el uso de los siguientes operadores se pueden realizar comparaciones para generar variables booleanas. También pueden emplearse en estructuras de control de flujo condicionales (if
-then
-else
) o bucles (while
).
Descripción | Comparador |
---|---|
Igual a | == |
Distinto a | != |
Menor a | < |
Menor o igual a | <= |
Mayor a | > |
Mayor o igual a | >= |
Ejemplos:
var1 = 8
var2 = 15
comp1 = var1 < var2
var3 = 10
var4 = 15
comp2 = var3 == var4
Como 8 es menor que 15, comp1
es true
. Por el contrario, comp2
toma el valor false
debido a que 10 no es igual a 15.
Declaración de funciones #
La primera alternativa para declarar funciones toma la siguiente forma. El símbolo *
indica que el argumento en cuestión es opcional:
function <ident>(<arg1>*, <arg2>*) do
<body*>
return*
end
donde:
<ident>
es el identificador o nombre de la función.(<arg1>, <arg2>)
son los argumentos de entrada a la función. Se pueden utilizar tantos argumentos como se desee separándolos con comas.<body>
representa las sentencias que conforman la función.return
define la salida de la función, la cual puede devolver tanto variables (que tengan asociadas un tipo de dato o función) como números, booleanos o textos. También es válido no utilizar esta sentence y directamente no devolver nada.
Ejemplo:
function fnWithArgs(value1, value2, value3) do
aux = value1 + value2*value3
return "Function returns: " + aux
end
Otra opción para declarar una función toma el siguiente formato:
ident = function(<arg>*) do
<body*>
return*
end
En este caso, se declara la función y se asigna su valor de retorno a la variable nombrada ident
en un mismo paso.
Funciones con parametros que aceptan funciones #
Una función puede aceptar otra función como posible argumento de entrada.
Ejemplo:
function mean(value1, value2) do
aux = value1 + value2
return aux/2
end
function main(value1, value2, func1) do
result = func1(value1, value2)
return result
end
main(15, 10, mean) # function call with another function as an argument
En el código de arriba, la función main
espera tres argumentos. Los dos primeros (value1
y value2
) son valores dados y el restante una función. Cuando se invoca esta función en la última línea, los dos valores son procesados en realidad por mean
.
Funciones sin parámetros #
Es posible declarar una función sin argumentos simplemente colocando ()
luego del identificador de la función.
Ejemplo:
function fnWithoutArgs() do
return "Global Error"
end
Funciones que no devuelven nada #
En el caso de que la función se vaya a emplear para ejecutar una serie de sentencias y no exista la necesidad de devolver un valor, se debe omitir el comando return
del final.
Ejemplo:
function noReturn (val1) do
if val1 < 48 then
loginfo(val1)
end
end
Estructuras de control #
If-Then-Else #
Es posible utilizar una estructura condicional del tipo if
-then
-else
para el control de flujo en el código. Su forma general es la siguiente:
if <exp> <comp> <expr> then
<body>*
else
<body>*
end
donde se puede obviar la utilización de else
y en tal caso, la estructura anterior se reduce a
if <exp> <comp> <expr> then
<body>*
end
Se permite la utilización de multiples sentencias ubicadas una luego de la otra, tanto dentro de
then
como deelse
.
<exp> <comp> <expr>
es una condición que implica la comparación entre dos expresiones. <comp>
puede ser cualquiera de los comparadores que se presentaron anteriormente en la sección Comparadores. Si la condición se cumple, se ejecuta la sentencia dentro de then
. De lo contrario, si la condición NO se cumple, se hace lo propio con la sentencia dentro de else
(siempre y cuando esta exista).
Ejemplo: If-Then-Else con Else
function conditional (value1, value2) do
if value1 >= value2 then
return "value1 is greater or equal than value2"
else
return "value1 is less than value2"
end
end
Ejemplo: If-Then-Else sin Else (If-Then)
function conditional_without_else (value) do
if value1 == 8 then
result = value*15
end
return result
end
Ejemplo: If-Then-Else con condición directamente booleana
Es válido reemplazar la condición <exp> <comp> <expr>
directamente por una variable booleana <bool>
. Como es de esperarse, se ejecutarán la sentencias dentro de then
si <bool>
es true
, o aquellas dentro de else
(si este existiera) en caso contrario.
El formato general es el siguiente:
if <bool> then
<stm*>
else
<stm*>
end
A continuación, una aplicación concreta:
function condBool(value) do
bool1 = value == 8
if bool1 then
return "Input equals 8"
else
return "Input does not equal 8"
end
end
Ejemplo: If-Then-Else con operadores lógicos entre condiciones
Los operadores lógicos and
u or
pueden estar presentes entre dos o más condiciones. Por otro lado, el operador lógico not()
permite negar una condición.
La utilización del operador and
implica que todas las condiciones individuales unidas por este operador deben ser true
para cumplir la condición completa del if
. En cambio, al emplear el operador or
basta que al menos una de las condiciones individuales sea true
para hacerlo.
Ejemplo:
function condLogic(value1, value2, value3, value4) do
if value1 != value2 and value3 < value4 then
return "Yeah!"
end
if not(value1 != value2) then
return "Value1 is equal to Value2"
end
end
While #
El bucle while
se utiliza para iterar sobre un grupo de sentencias mientras se cumple una determinada condición. Su sintaxis es la siguiente:
while <expr> do
<body>
end
donde <expr>
es una expresión lógica que tiene el mismo formato que en las estructuras if
-then
-else
:
function iterate1 (value) do
while value < 150 do
value = value*2
end
end
Dentro de iterate1
, el bucle continuará multiplicando a value
(inicialmente un argumento a la función) por 2 mientras el resultado sea menor que 150.
Ejemplo: While con operadores lógicos entre condiciones
Se permite la utilización de operadores lógicos and
, or
y not()
de la misma forma que con if
-then
-else
:
function iterate2 (value1, value2) do
while value1 < 150 and value2 < 200 do
value1 = value1+10
value2 = value2+40
end
end
En el caso de arriba, value1
y value2
se incrementan en 10 y 40 unidades en cada iteración. Este proceso continúa hasta que el primero y el segundo sean mayores o iguales a 150 o 200, respectivamente.
Estructuras de Datos #
Arreglos #
Un arreglo es un tipo de datos estructurado que está formado por una colección finita y ordenada de datos. El lenguaje de scripting incluye una serie de funciones que permiten crear y trabajar con arreglos unidimensionales.
Cabe aclarar que los datos almacenados pueden ser de cualquier tipo (número, texto, booleanos) dentro de un mismo arreglo.
array() #
Esta función hace posible la creación de un arreglo. Su uso general toma la forma arr = array()
, donde arr
queda definido como un arreglo.
aset(arr, ind, data) #
Permite introducir el dato data
dentro del arreglo arr
en la posición definida por el índice ind
.
aget(arr, ind) #
Devuelve el dato asociado con el índice ind
en el arreglo arr
.
alen(arr) #
Devuelve la longitud del arreglo arr
.
Ejemplos de trabajo con arreglos #
En los siguientes bloques, se crea un arreglo y luego se agregan datos. Como próximo paso, se calcula su longitud y por último se extraen los elementos del arreglo:
Ejemplo 1:
arreg = array()
aset(arreg,0,25)
aset(arreg,1,454)
aset(arreg,2,422.25)
arrayLength = alen(arreg)
value = aget(arreg,2)
La variable value
toma el valor 422.25 que previamente se había almacenado en la posición 2 dentro del arreglo. Por otro lado, arrayLength
es igual a 3 ya que esa es la longitud del arreglo.
Ejemplo 2:
arrText = array()
aset(arrText, 0, "Braulio")
aset(arrText, 1, "Hector")
aset(arrText, 2, "Hugo")
arrayLength = alen(arrText)
value = aget(arrText, 0)
# variable `value` is Braulio , variable `arrayLength` is equal to 3.
En este caso, value
y arrayLength
contienen los valores Braulio y 3, respectivamente.
Diccionarios #
El lenguaje de scripting facilita una serie de funciones que permiten manipular diccionarios. Estas estructuras son pares clave/valor que requieren que las claves sean únicas.
dict() #
Esta función permite la creación de un diccionario. Su sintaxis general toma la forma dictionary = dict()
mediante la cual se define dictionary
como un diccionario.
set(dictionary, key, data) #
Permite introducir el dato data
, asociado a la llave (clave) key
, dentro del diccionario dictionary
.
get(dictionary, key) #
Devuelve el dato asociado a la llave key
dentro del diccionario dictionary
.
Ejemplos de trabajo con diccionarios #
En el siguiente bloque se ilustra cómo crear un diccionario, introducirle datos, y después recuperarlos.
infoPerson = dict()
set(infoPerson, "name", "Bob")
set(infoPerson, "ID", 8504234)
set(infoPerson, "job", "Sales manager")
set(infoPerson, "car", "BMW")
car1 = get(infoPerson,"car")
Luego de ejecutar las líneas que aparecen arriba, la variable car1
toma el valor BMW.
Funciones Matemáticas #
El lenguaje de scripting proporciona varias funciones matemáticas predefinidas.
abs(value) #
Devuelve el valor absoluto de value
.
round(value) #
Regresa el redondeo de value
.
ceil(value) #
Devuelve el número entero mayor o igual más próximo a value
.
floor(value) #
Retorna el máximo entero menor o igual a value
.
max(value1, value2) #
Acepta uno o más argumentos y regresa el máximo de ellos.
min(value1, value2) #
Recibe uno o más argumentos y devuelve el mínimo entre tales valores.
mod(value1, value2) #
Devuelve el resto de la división entera entre value1
y value2
(value1
/value2
).
random() #
Devuelve un valor aleatorio entre 0 y 1.
Funciones para texto #
regexp(patrón) #
Crea un objeto del tipo expresión regular para buscar coincidencias dentro de un texto a partir de un patrón dado.
Ejemplo:
patternLettersOrNumbers = regexp("[A-Za-z0-9ñÑ]")
En este caso, patternLettersOrNumbers
devolverá coincidencias siempre que encuentre una o más letras minúsculas o mayúsculas (incluyendo la ñ) o números; en síntesis, cualquier caracter alfanumérico cumplirá esta expresión regular.
val(string) #
Convierte la cadena de texto string
a su equivalente valor numérico, siempre y cuando string
este conformado solamente por números.
substring(string, arg1, arg2) #
Extrae caracteres del texto dado por string
comenzando por la posición arg1
(sin incluirlo) hasta arg2
(inclusive). Como el argumento arg2
es opcional, la extracción puede realizarse simplemente desde arg1
hasta el final de la cadena.
Al manipular caracteres dentro de una cadena con esta función, las posiciones dadas por
arg1
yarg2
son basadas en cero.
Ejemplo:
var1 = substring("Complete string",2)
Al ejecutar la línea anterior, var1
tomará el valor mplete string
.
Ejemplo:
var1 = substring("Complete string",2,11)
# var1 is 'mplete st'
En este segundo caso, var1
será igual a mplete string
.
trim(string) #
Elimina los espacios en blanco de ambos extremos de la cadena de texto representada por string
.
Ejemplo:
text = " text with lots of blank spaces "
text2 = trim(text)
Como resultado, el contenido de text2
será text with lots of blank spaces
.
replace(string, arg1, arg2) #
Reemplaza arg1
por arg2
en la cadena string
, donde arg1
puede ser texto o una expresión regular. Si arg1
es texto, el cambio se realiza solamente en la primera ocurrencia. Caso contrario, la modificación se efectúa en toda coincidencia del patrón dentro de la cadena de texto.
Ejemplo (uso como texto):
text = "it is remarkable to remember that the last meeting was remarkable"
out = replace(string, "remarkable", "important")
Luego de ejecutar las sentencias de arriba, el valor de out
será it is important to remember that the last meeting was remarkable
.
Ejemplo (uso como expresión regular):
expReg = regexp("she\w{2}")
string = "She sells seashells on the seashore. The shells she sells are surely seashells. So, if she sells shells on the seashore, I'm sure she sells seashore shells."
out = replace(string, expReg, "star")
En esta instancia, out
será igual a She sells seastars on the seashore. The stars she sells are surely seastars. So, if she sells stars on the seashore, I'm sure she sells seashore stars.
.
match(string, arg) #
Regresa un valor booleano (true
o false
) en base a la presencia o inexistencia de la cadena arg
dentro de string
.
len(string) #
Devuelve la cantidad de caracteres de la cadena de texto representada por string
.
Otras funciones #
loginfo(arg) #
Imprime el contenido de arg
en la consola del navegador.
rest(method, host, headers, params) #
Permite realizar peticiones HTTP a un servicio web. Los argumentos de entrada son los siguientes:
Argumento | Tipo de dato | Descripción |
---|---|---|
method | Texto | Método de la solicitud (puede ser GET , POST , o PUT ) |
host | Texto | URL del host al que se va a realizar la petición |
headers | Diccionario | Encabezado que se enviará al servicio web en la petición |
params | Diccionario | Otras estructuras que se enviarán al servicio web |
La función devuelve un diccionario con las siguientes claves:
Llave | Tipo de dato | Descripción |
---|---|---|
body | Texto | Cuerpo que el servicio web devuelve como resultado de la solicitud |
status | Texto | Código de respuesta HTTP (resultado de la petición) |
Ejemplo:
headers = dict()
params = dict()
host = "https://google.com"
set(headers, "content-type", "application/soap+xml")
set(params, "body", "")
response = rest("GET", host, headers, params)
status = get(response, "status")
bodyReturn = get(response, "body")
Si la solicitud es exitosa, status
tomará el valor 200
mientras que bodyReturn
será igual al cuerpo (body
) de response
.
Flujo de ejecución del código #
El primer paso en el diseño del código debe consistir en declarar las funciones que se invocarán luego. Es importante recordar que llamar a una función que no ha sido definida conducirá a errores en la ejecución. También es recomendable declarar las variables globales que se necesiten en este punto.
Las funciones que se llamen en el cuerpo del código serán ejecutadas inmediatamente al ingresar al paso del funnel en el que se encuentre la captura de datos que se necesite validar. Para que esto suceda, es imprescindible invocar la función predefinida addValidations()
y pasarle como argumento la función que realizará la validación sobre el campo en cuestión.
Función addValidations(f) #
Esta función vincula el campo de captura de datos que se desea validar con el código diseñado. La función f
que se use como argumento de addValidations
será el punto de inicio de la validación. Debido a que se puede invocar addValidations()
varias veces en el mismo código, es posible realizar varias validaciones independientes para un mismo campo.
Como es de esperar,
addValidations(f)
debe ser llamada luego de la declaración def
y de aquellas que sean llamadas por esta última.
Algunos puntos a tener en cuenta:
El argumento de entrada de
f
es el valor que se introducirá en el campo que se está validando.El valor de retorno de la función
f
será el mensaje que se mostrará ante un error en la validación.Si no se devuelve nada, se entiende que la verificación fue correcta, y no es necesario devolver ningún tipo de mensaje. Por ese motivo, se puede omitir el comando
return
en el flujo de código.
Para ilustrar, consideremos el siguiente ejemplo de validación:
function validateRange (value) do
if value > 8 then
return "Valor de entrada debe ser menor o igual a 8"
end
end
addValidations(validateRange)
donde value
es el valor que se ingresa en el campo de captura. Si este valor es mayor a 8, se mostrará un mensaje de error (Valor de entrada debe ser menor o igual a 8):
Al no considerar el caso en que value
sea menor o igual a 8, la captura será correcta y se avanzará en el funnel al presionar el botón Submit cuando se cumpla esa condición.
Múltiples validaciones para un mismo campo #
Como se explicó anteriormente, pueden utilizarse múltiples llamadas a la función addValidations()
para realizar el mismo número de validaciones.
Ejemplo:
function validateRange (value) do
if value > 8 then
return "Valor de entrada debe ser menor o igual a 8"
end
end
function validateNumber (value) do
if match(value,regexp("[^0-9]")) then
return "Valor de entrada solamente puede contener números"
end
end
addValidations(validateRange)
addValidations(validateNumber)
Las dos validaciones que se hacen en este caso son las siguientes:
- Que el contenido del campo sea igual o menor a 8
- Que el caracter que se introduzca sea numérico
Si alguna de estas condiciones no se cumple, se mostrará un mensaje particular para indicar el error.
Ejemplos de códigos para casos comunes de validación #
Ejemplos básicos #
Validación de cantidad de caracteres en campos de texto #
Mediante el siguiente bloque de código, se usa la función checkDataLength
para validar que la cantidad de caracteres presentes en un campo sea mayor que 6 pero menor a 20:
function checkDataLength(value) do
value = trim(value)
if len(value) < 6 or len(value) > 20 then
return "Field length must be between 6 and 20 characters"
end
end
addValidations(checkDataLength)
Como se explicó previamente, la llamada a la función se realiza al pasarla como argumento a addValidations
.
Validación de tipo de caracteres en campo de texto #
En este caso, checkChars
verificará que el campo en cuestión solamente contenga letras y/o números.
function checkChars(value) do
value = trim(value)
if match(value,regexp("[^A-Za-z0-9ñÑ]")) then
return "Only letters and numbers are allowed on this field"
end
end
addValidations(checkChars)
Validación de fechas #
El lenguaje de scripting también permite validar que una fecha sea posterior a otra dada. Para lograr esto, se espera que la entrada de fecha en el campo de captura tenga el formato mm/dd/yyyy o mm-dd-yyyy.
function checkDate(value) do
value = trim(value)
month = val(substring(value,0,2))
day = val(substring(value,3,5))
year = val(substring(value,6,10))
if year == 2002 and month == 6 and day >= 23 then
return "Only dates before 6/23/2002 are accepted"
end
if year == 2002 and month > 6 then
return "Only dates before 6/23/2002 are accepted"
end
if year > 2002 then
return "Only dates before 6/23/2002 are accepted"
end
end
addValidations(checkDate)
En el ejemplo de arriba, una fecha se tomará como válida solamente si es posterior al 23 de junio de 2002.
Ejemplos avanzados #
Validación de cédula de identidad uruguaya #
function normalize_ci(value) do
value = trim(value)
loginfo("trim " + value)
value = replace(value,regexp("[\.-]"),"")
while len(value) < 8 do
value = "0" + value
end
return value
end
function validate_uy_ci (value) do
i = 0
a = 0
digit = 0
value = normalize_ci(value)
v = substring(value,0,7)
dv = substring(value,7)
while i < 7 do
vl = val(substring("2987634",i,i+1)) * val(substring(v,i,i+1))
a = a + vl
i=i+1
end
if mod(a,10) == 0 then
digit = 0
else
digit = 10 - mod(a,10)
end
return digit == dv
end
function validations (value) do
if not(validate_uy_ci(value)) then
return "No es una cédula de identidad válida"
end
end
addValidations(validations)
Validación de nombre de usuario #
El siguiente ejemplo verifica si nombre de usuario tiene la longitud correcta (entre 6 y 20 caracteres), si solamente incluye letras y/o números, y si ya existe o no en la base de datos. Esta última validación se realiza a través de una petición a un servicio web.
function checkUsernameLength(value) do
value = trim(value)
if len(value) < 6 or len(value) > 20 then
return "El nombre de usuario debe tener entre 6 y 20 caracteres de largo"
end
end
function checkUsernameChars(value) do
value = trim(value)
if match(value,regexp("[^A-Za-z0-9ñÑ]")) then
return "El nombre de usuario solamente puede tener letras y números"
end
end
function checkUsernameExists(value) do
headers = dict()
set(headers,"content-type","application/soap+xml")
params = dict()
host = "https://knrioscript.free.beeceptor.com"
body = ""
set(params,"body",body)
response = rest("GET",host,headers,params)
useduser = get(response,"body")
statusresponse = get(response,"status")
loginfo(statusresponse)
if match(useduser,value) then
return "El nombre de usuario no está disponible. Por favor, elija uno diferente."
end
end
addValidations(checkUsernameLength)
addValidations(checkUsernameChars)
addValidations(checkUsernameExists)