sábado, 8 de agosto de 2015

Clase Cuenta Bancaria en JS usando Joose


Problema

Modelizaremos una cuenta bancaria normal. En esta cuenta se puede depositar dinero, retirar dinero y comprobar su saldo actual. No se puede retirar más dinero que el que hay en la cuenta. Además modelizaremos una cuenta corriente con una protección contra sobregiros opcional. La protección contra sobregiros protegerá al propietario de la cuenta corriente retirando automáticamente los fondos necesarios de la cuenta de sobregiro para asegurar que un cheque no rebotará.

Solución

Module("Banco", function (m) {
  Class("Cuenta", {
    has: {
      balance: {
        is: "rw",
        init: 0
      }
    },
    methods: {
      depositar: function (monto) {
        this.setBalance(this.getBalance() + monto)
      },
      retirar: function (monto) {
        if(this.getBalance() < monto) {
          throw "Cuenta sobregirada"
        }
        this.setBalance(this.getBalance() - monto);
        return this.getBalance();
      }
    }
  });
  
  Class("CuentaCorriente", {
    isa: m.Cuenta,
    has: {
      cuentaSobregiro: {
        isa: m.Cuenta,
        is: "rw"
      }
    },
    before: {
      retirar: function (monto) {
        var cantidadEnRojo = monto - this.getBalance()
        
        if(this.cuentaSobregiro && cantidadEnRojo > 0) {
           this.cuentaSobregiro.retirar(cantidadEnRojo);
           this.depositar(cantidadEnRojo);
        }
      }    
    }
  })
}) 

Explicacion

La clase Cuenta tiene un "balance" inicializado en 0 y dos métodos: "depositar" y "retirar". La CuentaCorriente es una subclase de Cuenta que tiene una "cuentaSobregiro" opcional que es de tipo Cuenta. Antes de retirar un monto de la CuentaCorriente verifica si puede sobregirar la cuenta.

Módulos

Module("Banco", function (m) {
...
} 

La función Module() crea el namespace "Banco". Toma como segundo argumento una función que crea las clases Cuenta y CuentaCorriente. La función obtiene un objeto módulo como parámetro, que más tarde será utilizado para facilitar el acceso a otras clases ubicadas dentro del namespace.

Restricciones de Tipo

has: {
  cuentaSobregiro: {
    isa: m.Cuenta,
    is: "rw"
  }
}, 

Aquí introducimos un nuevo atributo para la clase CuentaCorriente llamado "cuentaSobregiro". Usando la palabra clave isa ponemos una restricción de tipo sobre el atributo. Esta restricción es chequeada cada vez que el método setCuentaSobregiro() es usado. Ese método fue creado automáticamente cuando se definió el atributo como rw.

Modificador de método "before"

before: {
  retirar: function (monto) {
    var cantidadEnRojo = monto - this.getBalance()
    
    if(this.cuentaSobregiro && cantidadEnRojo > 0) {
       this.cuentaSobregiro.retirar(cantidadEnRojo);
       this.depositar(cantidadEnRojo);
    }
  }    
} 

Introducimos un método que será ejecutado antes del método retirar(). De esta manera el método original se ve aumentado en su comportamiento. Si el dinero actual en el balance de la cuenta no es suficiente para el retiro, se extrae lo que falta de la cuentaSobregiro y se deposita en la cuenta.

Modificador de método "override"

Otra posible implementación podría haber sido sobreescribir el método retirar() usando el modificador override, el cual permite llamar al método de la superclase haciendo this.SUPER()
override: {
  retirar: function (monto) {
    var cantidadEnRojo = monto - this.getBalance()
      
    if(this.cuentaSobregiro && cuentaSobregiro > 0) {                
      this.cuentaSobregiro.retirar(cantidadEnRojo);
      this.depositar(cantidadEnRojo);
    }

    this.SUPER(amount)
  }
} 

Instanciación de los objetos

Todos los objetos Joose reciben un inicializador estándar que permite inicializar los objetos usando argumentos por nombre en el constructor:
var cajaAhorros= new Banco.Cuenta({ balance: 100 });
var cuentaCorriente= new Banco.CuentaCorriente({ 
    balance:         200, 
    cuentaSobregiro: cajaAhorros
});
console.log("Una cuenta: ");
console.log(cajaAhorros);
console.log("Una cuenta corriente: ");
console.log(cuentaCorriente);
console.log("Operamos en la cuenta con un retiro parcial de 100");
cuentaCorriente.retirar(100);
console.log(cuentaCorriente);
console.log(cajaAhorros);
console.log("Sobregiramos la cuenta con un retiro mayor: 150");
cuentaCorriente.retirar(150);
console.log(cuentaCorriente);
console.log(cajaAhorros);
console.log("Sobregiramos demasiado la cuenta: 300");
cuentaCorriente.retirar(300);

Una cuenta: 
 a Banco.Cuenta { balance=100, meta=a Joose.Class, initialize=initialize(), más...}
Una cuenta corriente: 
 a Banco.CuentaCorriente { balance=200, cuentaSobregiro=a Banco.Cuenta, meta=a Joose.Class, más...}
Operamos en la cuenta con un retiro parcial de 100
 a Banco.CuentaCorriente { balance=100, cuentaSobregiro=a Banco.Cuenta, meta=a Joose.Class, más...}
 a Banco.Cuenta { balance=100, meta=a Joose.Class, initialize=initialize(), más...}
Sobregiramos la cuenta con un retiro mayor: 150
 a Banco.CuentaCorriente { balance=0, cuentaSobregiro=a Banco.Cuenta, meta=a Joose.Class, más...}
 a Banco.Cuenta { balance=50, meta=a Joose.Class, initialize=initialize(), más...}
Sobregiramos demasiado la cuenta: 300
uncaught exception: Cuenta sobregirada

lunes, 3 de agosto de 2015

Web API Console

Comandos


console.log(object[, object, …])
Escribe un mensaje en la consola. Como primer argumento puede aceptar un objeto o un string con especificadores de formato. Por ejemplo %s para string, %d para enteros, %f para flotantes o %o para objetos.
console.log("Usuario %s posee %d créditos", userName, creditCount);

console.debug(object[, object, ...])
Idéntico a console.log.

console.info(object[, object, ...])
Idéntico a console.log pero agrega un ícono "info".

console.warn(object[, object, ...])
Idéntico a console.log pero agrega un ícono "warning".

console.error(object[, object, ...])
Similar a console.log pero agrega un ícono "error" y una traza de la pila.

console.assert(expression[, object, ...])
Testea que una expresión sea verdadera. Si no lo es escribirá un mensaje de error en la consola junto a una traza de la pila. Por ejemplo:
console.assert(list.childNodes.length < 10, "List item count is >= 10");

console.clear()
Limpia la consola.

console.dir(object)
Muestra las propiedades del objeto.

console.trace()
Muestra una traza de la pila en el punto donde es llamado.

console.time(label)
Comienza un timer con una etiqueta label asociada. Cuando console.timeEnd(label) es llamado, el timer es detenido y se muestra en la consola el tiempo transcurrido.

console.profile([title])
Inicia el CPU profile con un título opcional. Para completar el profile llame a console.profileEnd() para que imprima el reporte.

console.count([label])
Escribe el número de veces count() fue ejecutado con el mismo label o las veces que se ejecutó la misma línea de código donde count() fue llamado y ejecutado.

console.table(data[, columns])
Similar a console.log pero muestra los datos en formato de tabla. Por ejemplo:
var languages = [
    { name: "JavaScript", fileExtension: ".js" },
    { name: "Perl", fileExtension: ".pl" },
    { name: "Python", fileExtension: ".py" },
    { name: "Erlang", fileExtension: ".erl" },
    { name: "PHP", fileExtension: ".php" }
];

console.table(languages);
Muestra por consola:

name fileExtension
"JavaScript" ".js"
"Perl" ".pl"
"Python" ".py"
"Erlang" ".erl"
"PHP" ".php"

domingo, 2 de agosto de 2015

HTML DOM querySelector()

Ejemplo

document.querySelector(".ejemplo");
Obtiene el primer elemento en el documento con la clase "ejemplo".
El método querySelector() retorna el primer elemento del documento que matchea con un específico selector CSS. Si se quiere obtener todos los elementos se puede usar querySelectorAll().

Sintaxis

document.querySelector(CSS selectors)
El parámetro especifica uno o más selectores CSS separados por comas, para buscar un elemento en el documento. Retorna un objeto NodeList o null si no es encontrado.

Más ejemplos

document.querySelector("p");
Obtiene el primer elemento párrafo en el documento.
document.querySelector("p.ejemplo");
Obtiene el primer elemento párrafo en el documento con la clase "ejemplo".
document.querySelector("#demo").innerHTML = "Hola Mundo!";
Obtiene el primer elemento con id="demo" y modifica su texto.
document.querySelector("div > p");
Obtiene el primer elemento párrafo cuyo parent es un elemento div.
document.querySelector("a[target]");
Obtiene el primer elemento <a> que tenga un atributo "target".

Ejemplo con múltiples selectores

<h2>Titulo de nivel 2</h2>
<h3>Titulo de nivel 3</h3>

document.querySelector("h2, h3").style.backgroundColor = "red";
El primer elemento que encuentra es un título de nivel 2, por eso lo colorea de rojo y no sigue buscando elementos de título de nivel 3.
En el ejemplo siguiente colorea el elemento de título de nivel 3 porque es el primer elemento que matchea con alguno de los selectores, luego no sigue buscando.
<h3>Titulo de nivel 3</h3>
<h2>Titulo de nivel 2</h2>

document.querySelector("h2, h3").style.backgroundColor = "red";

sábado, 1 de agosto de 2015

Clase Selfie en Joose JS

Código

Class("Fotografia", {
    has: {
        alto: {is: "ro"},
        ancho: {is: "rw"},
    },
    methods: {
        clear: function () {
            this.alto = 0;
            this.setAncho(0);
        }
    }
})
Class("Selfie", {
    isa: Fotografia,
    has: {
        etiqueta: {init: "yo"}
    },
    after: {
        clear: function () {
            this.etiqueta = "yo";
        }
    }
})
var foto= new Selfie({ancho: 360, alto:240});
foto.etiqueta= "Ricky Martin";

Explicación


Este es un ejemplo de Joose para crear una clase que hereda de otra. La función Class() toma dos parámetros: el nombre de la clase y la definición de la clase pasada como un objeto literal. La sintaxis para definir la clases es declarativa y fácil de leer. Las palabras clave como "has", "methods", "isa" y "after" son usadas para expresar diferentes aspectos de la clase.

Atributos


has: {
        alto: {is: "ro"},
        ancho: {is: "rw"},
    }, 

El primer elemento de la declaración de la clase Fotografía es un bloque "has" que define dos atributos de la clase: alto y ancho. El bloque alto: {is: "ro"} define "alto" como de sólo lectura. Mientras que el bloque ancho: {is: "rw"} define "ancho" como un atributo de lectura y escritura. Joose automáticamente crea los métodos getAlto() para "alto" y getAncho() y setAncho() para "ancho". El nombre de estos métodos sigue la convención camelCase, colocando la primera letra del atributo en mayúscula.

has: {
        etiqueta: {init: "yo"}
    }, 

El bloque etiqueta: {init: "yo"} define el atributo "etiqueta" con un valor por default que es el string "yo". Como no se especifica si es rw o ro, por defecto no se crean los métodos de acceso getEtiqueta() y setEtiqueta().

Métodos


methods: {
        clear: function () {
            this.alto = 0;
            this.setAncho(0);
        } 

El bloque "methods" define el método clear() para la clase Fotografía. Podemos poner todos los métodos que queramos. En este caso la función setea "alto" accediendo a la variable directamente, pero modifica "ancho" usando su método modificador creado con la declaración "rw".

Herencia


 isa: Fotografia, 

La cláusula "isa" en la definición de Selfie define que su super clase es Fotografía. Selfie hereda los métodos y atributos de su superclase.

Modificador de método


after: {
        clear: function () {
            this.etiqueta = "yo";
        }
    } 

Joose soporta los llamados modificadores de métodos. Uno de los 5 modificadores que declaran métodos con un comportamiento especial es "after". Aquí define que acciones ejecutar después de llamar al método con el mismo nombre definido en su superclase.

Inicialización


var foto= new Selfie({ancho: 360, alto:240}); 

El objeto literal {ancho: 360, alto:240} es pasado como argumento de un método especial llamada initialize() que lo usa para inicializar los valores del atributo del nuevo objeto.