Compra ordenadores seminuevos y ayuda al medio ambiente

Todas las persona que habitamos en la Tierra dejamos un impacto. Muchas veces no nos damos cuenta, pero ahí lo dejamos. Dejamos una huella para la posteridad. En sociedades como la nuestra gastamos mucho más de lo necesario. Un buen paso para combatir este desperdicio de recursos es adquirir tecnología de segunda mano. Mucha gente piensa que este tipo de tecnología estará desfasada, no obstante, con la información y ayuda adecuada, puede aguantar todavía muchos años más.

Los ordenadores seminuevos son un claro ejemplo de un producto que puede aguantar muchos años más. Estos vienen frecuentemente de usuarios profesionales y exigentes con sus equipos. Cuando vamos al detalle nos encontramos de que son equipos con prestaciones muy decentes para el mercado actual y a unos precios considerablemente más bajos. Son productos de gama alta de la generación anterior, que en la generación actual, sin llegar a ser tope de gama, no dejan de ser un buen partido. Pero no solo eso, sino que ayudaremos a reducir el impacto medioambiental de nuestras compras.

En el caso de los ordenadores de segunda mano, una ventaja que hay respecto a otras tecnologías es el gran impacto que tiene el software. Esto significa que los componentes de hardware (lo físico del ordenador) están muy condicionados por la capa de software (programas) que se ejecutan por encima. Si tenemos un sistema actualizado, con las últimas versiones de los programas, tendremos un equipo seguro, fiable y con nuevas características de poco en poco que mejoren lo ya existente. Actualmente, los sistemas operativos como Windows han reducido su nivel de consumo y no será un problema de ningún tipo tener la última versión de Windows 10 en estos equipos.

No obstante, yo en lo personal recomiendo siempre que sea posible usar un sistema operativo Linux de forma habitual. Suelen estar mejor actualizados y su rendimiento es mejor, incluso en equipos más antiguos. El único requisito para dar el paso es asegurarse que todos los programas que vayamos a usar o bien sean compatibles con Linux o bien existan reemplazos. Si acostumbras a usar Adobe Photoshop, quizá Linux no sea para ti; si sin embargo solo realizas tareas de ofimática con Word y Excel, quizá puedas usar sin problemas LibreOffice, presente en Linux.

A muchos nos puede asaltar la duda: “Vale, muy bien, los ordenadores seminuevos son más baratos, plenamente funcionales y ayudan al medio ambiente, ¿pero y si tengo algún fallo con el equipo?”. Efectivamente, se trata de una duda razonable. Por suerte, actualmente existen vendedores que no solo ofrecen equipos de segunda mano sino que se hacen cargo de la garantía desde la fecha en que compramos el producto. Como si fuese un equipo nuevo.

En definitiva, esta alternativa a comprar un equipo nuevo, es más respetuosa con el medio ambiente, con equipos igual de potentes y fiables que muchos en el mercado y a un precio reducido. En particulares, la diferencia se nota. Pero en empresas donde se compran ordenadores en más cantidades y muchas veces no se necesitan grandes equipos para la realización de tareas ofimáticas, la compra de ordenadores seminuevos puede suponer un gran ahorro.

 

loading...

Cuando ‘bad_style’ se convierte en algo ofensivo

Voy a hacer un breve comentario sobre un asunto que ha levantado polémica dentro del mundo de Rust. Se trata de esta issue. Básicamente viene a decir que usar el término bad para referirse a el estilo de programación de Rust no estándar es incorrecto. Que puede ofender a la gente. Particularmente lo encuentro jocoso, pero hay mucha gente que no. La discusión se ha bloqueado (alguien diría que censurado) y parece que finalmente se ha adoptado la decisión de eliminar bad por ser ofensivo.

Con gran acierto el autor profetizó que esta issue traería mucho bikeshedding, aunque no únicamente en el nombre de la sustitución. Sin embargo me parece que este tema merece que le dedique un poco de tiempo.

En primer lugar se culpa de que usar bad hace a la gente sentirse mal, que es un término pasivo-agresivo y que echa las culpas al programador por usarlo. ¿Qué hace bad_style? Es un lint del compilador de Rust que emite advertencias si usamos una convención distinta la habitual. Por ejemplo,si usamos camelCase en vez de snake_case obtendremos un warning por este linter.

A mí en particular en condiciones normales me daría igual el término exacto de este linter, pero los argumentos que han dado me han parecido muy soprendentes. Supuestamente si algo tiene la palabra bad podría haber gente que se sienta mal y le afecte, que los principiantes se sientan bienvenidos en una comunidad con un lenguaje que no te hace usar la palabra bad cuando vas a saltarte la convención (y además les desmotiva).

El caso es que me resulta difícil entender como el flag para que el compilador no ejecute el susodicho linter pueda herir los sentimientos de alguien. El compilador no es algo personal, no te está insultando a ti, es una herramienta, y sí, existen cosas malas. Si llevamos esto al extremo el compilador no debería dar errores sino “sintaxis malointerpretada” Programar en bad_style es una mala práctica porque reduce la interoperabilidad y legibilidad entre proyectos. No importa si es camelCase o snake_case pero todos estamos de acuerdo que si un lenguaje adopta una forma por convención es mucho mejor adherirse a ella, podremos leer código ajeno más rápidamente y mejor. Si realmente te afecta emocionalmente usar bad_style porque es bad entonces quizá tu problema sea otro, mucho más grave y que necesites ayuda de un profesional.

Por otra parte la discusión presenta la más que alarmante falta de visión más allá del mundo angloparlante que presenta mucha gente. No es que les ignoran, es que creen que ciertas cosas que son aplicables a EEUU son aplicables al resto de culturas del mundo.

Fue en otra conversación donde se propuso cambiar stupid por foolishrude, ya que era menos ofensivo. Como si estúpido en español fuese un gran insulto. Para mí estúpido puede llegar a ser un insulto tierno. Sin embargo el grupo de trabajo dedicado a revisar las palabras prefería abolir sus usos con la excusa de que nadie pudiera sentirse ofendido. Yo no lo sabía pero existen detectores automáticos de estas palabras y muchas otras que no deben ser usadas. Para mí todo esto me parece sacado de contexto de forma excesivo y aunque no es algo único a la informática, es aquí donde me lo encuentro más a menudo. Sobre este asunto estoy bastante de acuerdo con Slavoj Zizek.

No sé donde quedó aquello de que no había libertad de expresión si no había libertad de ofender a la gente como decían Orwell o Chomsky. Cada vez más las comunidades que dicen ser welcoming me alejan más. Dije antes que este asunto no solo ha pasado en el mundo de Rust. Efectivamente, en Node.js también eliminaron suicide del módulo cluster porque era ofensivo también. No pudieron eliminar killhost porque eran ya términos históricos de UNIX, aunque les hubiese gustado.

Tutorial de CouchDB

Hoy vengo a hablar de CouchDB, una base de datos que no es precisamente nueva ni es precisamente famosa, pero que en mi opinión tiene unas características únicas.

En primer lugar vayamos con la teoría. Apache CouchDB es una base de datos NoSQL que almacena documentos JSON y tiene una potente API REST. Está programada en Erlang y es un proyecto Apache desde 2008. Usa JavaScript para la operativa interna. Visto este resumen uno podría pensar que estamos ante un clon de MongoDB (a pesar de que CouchDB es mucho más antigua) pero no. CouchDB parte de una filosofía distinta, ser la base de datos de la era de la web y se nota mucho como toma decisiones que la alejan de las bases de datos relacionales y de las bases de datos NoSQL más populares.

La API REST

Una parte fundamental de CouchDB es la API REST, que es la única API que dispone. CouchDB de este modo puede funcionar de dos formas:

  • En un modelo tradicional, con una servidor que haga las peticiones. Este servidor puede estar programado en cualquier cosa (Java, PHP, Node.js, C#, Python, Ruby, Elixir,…) y que no es muy distinto a como nos conectamos por ejemplo a MongoDB o a MySQL.
  • En un modelo innovador donde la base de datos está conectada directamente a Internet y no hace falta ningún servidor que gestione la comunicación. Este modelo da lugar a las CouchApps, aplicaciones CRUD pero sin servidor propiamente dicho, solo base de datos y un alojamiento estático de archivos. A través de JavaScript en el lado del cliente podemos obtener la información, conectándose directamente el navegador con la base de datos.

La posibilidad de hacer esto último es una de las mayores ventajas de CouchDB en mi opinión, ya que según el proyecto, esto puede ahorrar mucho tiempo y prevenir muchos posibles bugs en operaciones tontas. El desafío de este artículo será crear una aplicación en CouchDB que pudiera simular a una aplicación CRUD cualquiera. Esta aplicación reducirá costes respecto a una tradicional ya que eliminamos la parte del servidor, solo mantenemos la base de datos (y un proxy).

Instalando CouchDB

CouchDB está en casi todas las distros Linux. Es posible que no esté disponible la última versión, pero para lo que vamos a hacer no es necesario. Yo voy a usar CouchDB 1.4, una versión bastante antigua, pero es la que tiene Debian en sus repos. En Ubuntu tenéis CouchDB 1.6 y en la página oficial está la versión 2.0

Creando una base de datos

En CouchDB las bases de datos son cubos sin fondo. Podemos arrojar documentos JSON sin control.

Vamos a usar la API REST para crear la base de datos “supermercado”.

curl -X PUT http://127.0.0.1:5984/supermercado

Podemos comprobar que la base de datos ha sido creada con GET

curl -X GET http://127.0.0.1:5984/supermercado

En CouchDB ciertas rutas que comienzan por barra baja son especiales. Por ejemplo si queremos pedir una lista de todas las bases de datos podemos hacer GET a _all_dbs.

curl -X GET http://127.0.0.1:5984/_all_dbs

Para borrar la base de datos usaríamos DELETE

curl -X DELETE http://127.0.0.1:5984/supermercado

Pero no lo hagas todavía. Si has pensado en lo que hemos hecho te estarás alarmando. Hemos creado una base de datos, por HTTP y no se nos ha pedido ninguna autorización ni nada. Por defecto CouchDB es inseguro ya que arranca en un modo llamado Admin Party, pero rápidamente veremos que si vamos a exponer CouchDB a Internet vamos a necesitar seguridad, y CouchDB la trae. Pero antes, vamos a trabajar con documentos.

Insertando un documento

Insertar un documento es tan fácil como tirar documentos a la papelera (bueno, quizá no tan fácil).

curl -X PUT http://127.0.0.1:5984/supermercado/UUID -d '{"nombre": "Manzana", "tipo" : "fruta", "precio" : 5}'

Donde UUID es un UUID. Si no tienes un UUID a mano, CouchDB te ofrece uno a través de _uuids.

curl -X GET http://127.0.0.1:5984/_uuids?count=10

En realidad no es estrictamente necesario poner un UUID, pero es la convención habitual. Una vez hagamos el PUT obtendremos de respuesta un JSON con tres campos muy importantes. Ok para decirnos si la operación fue bien o no, _id, con el ID del documento (es el UUID de antes) y el _rev. Este último campo, nos indica con que versión del documento hemos interactuado, en nuestro caso, era la primera así que hay un 1 delante. Esto es muy importante en CouchDB y tiene que ver con como gestiona la concurrencia. CouchDB es eventualmente consistente. Quiere decir que en una situación de escritura y lectura simultáneas, la base de datos no se bloquea sino que puede mandar distintas versione de los documentos. Cuando editemos un documentos tendremos que indicar  sobre que versión hacemos la modificación, y si algún otro cliente se nos adelantó y modificó el documento antes, tendremos que basarnos en su revisión para que sea aceptada por la base de datos. Hay que tener que CouchDB no es un sistema de control de versiones, las revisiones antiguas pueden desaparecer sin previo aviso. Esta característica, la de ser eventualmente consistente, también facilita mucho su desempeño en clústeres, pero no vamos a entrar en ello. De momento hay que saber que en CouchDB, a diferencia de otras bases de datos, para actualizar un documento necesitaremos su ID y su revisión.

Para ver el documento conociendo su ID hacemos un simple GET

curl -X GET http://127.0.0.1:5984/supermercado/UUID

Nos devuelve el documento entero junto con las propiedades _id y _rev.

Actualizando un documento

El precio de las manzanas ha cambiado, vamos a actualizarlo. El proceso es muy simple, es como insertar un documento pero añadimos la revisión sobre la que queremos hacerlo. Es importante mencionar que CouchDB no preserva nada en las actualizaciones. Si quieres dejar campos sin tocar, tendrás que volver a subirlos.

curl -X PUT http://127.0.0.1:5984/supermercado/98c003b03bc8aa87cb05983d1c000713 -d '{"_rev": "1-eba25568090eb2dfffad770b55147a67","nombre": "Manzana", "tipo" : "fruta", "precio" : 4}'

Para borrar documentos necesitas usar DELETE indicando el número de revisión.

curl -X DELETE http://127.0.0.1:5984/supermercado/98c003b03bc8aa87cb05983d1c000713?rev=2-298fdb46385be60609b242b3e5cc3566

(pero no lo hagas)

Vistas

Todo muy bonito, pero, ¿cómo accedo a la información? En SQL usaríamos SELECT, en bases como MongoDB lanzaríamos un find. En CouchDB hay que usar vistas, que se programan en JavaScript y siguen un esquema MapReduce (que recordemos, funciona muy bien en paralelo). Esto se puede hacer de varias formas. Se puede usar Fauxton, una interfaz web de CouchDB, se puede usar Erica, una herramienta para facilitar la creación y mantenimiento de CouchApps o se puede hacer con la API REST, ya que las vistas se definen en documentos JSON también.

Tenemos que crear un Design Doc. Los design doc son muy útiles y permiten por ejemplo validar documentos que vayan a entrar en la base de datos (podemos definir schemas así) o definir las vistas. Un design doc simple es así. Definimos una vista llamada all con una operación map.

 

{
    "views" : {
        "all" : {
            "map" : "function(doc){ emit(null,doc); }"
        }
    }
}

Si lo tenemos guardado en un archivo JSON

curl -H "Content-Type: application/json" --data @super.json -X PUT http://127.0.0.1:5984/supermercado/_design/super

Ahora vamos a ejecutar la vista

curl -X GET http://127.0.0.1:5984/supermercado/_design/super/_view/all

Obtenemos un objeto JSON con todos los documentos que contiene la base de datos supermercado. Por supuesto es posible crear distintos documentos de diseño, con distintas vistas cada uno.

Las vistas solo las debería poder crear el administrador de la base de datos, por lo que al contrario que en otras bases de datos, el cliente no podrá realizar operaciones que no hayan sido definidas antes. En realidad, no es del todo cierto, ya que en CouchDB 2.0 se añadió Mango, un lenguaje de consulta declarativo que permite realizar cierta operativa sin usar las vistas, pero no es tan potente.

Otro ejemplo:

{
    "views" : {
        "by-price" : {
            "map" : "function(doc){ emit(doc.precio,doc); }"
        }
    }
}

Esta vista puede ser llamada con parámetros

curl -X GET http://127.0.0.1:5984/supermercado/_design/super/_view/by-price?descending=true&limit=1

CouchDB realiza la operación de ordenado por su cuenta con el valor del key que devolvemos.

La base de datos en el salvaje oeste

Ahora vamos a ver como exponer CouchDB al salvaje Internet sin comprometer seguridad. En primer lugar toca hablar de los usuarios. Como hemos dicho, CouchDB arranca en el modo Admin Party. Este modo será desactivado en cuanto creemos un usuario como admin. CouchDB soporta además usuarios tradicionales, una característica muy útil que veremos después como usar.

Para crear un usuario como admin lanzamos esta petición:

curl -X PUT http://127.0.0.1:5984/_config/admins/aarroyoc -d '"MiPassword"'

A partir de ahora ya no estamos en una admin party y si intentamos crear una base de datos veremos que CouchDB nos deniega el acceso. Sin embargo, otras tareas como subir documentos a bases de datos ya existentes todavía funcionan.

Para protegernos mejor debemos saber los tipos de autorización que soporta CouchDB. En primer lugar soporta HTTP Basic Auth, un método en el que mandamos el usuario y la contraseña en texto plano en cada petición. Esto no es para nada seguro, pero combinado con SSL no es mala opción. Sin embargo sigue sin ser la mejor solución para los expertos en seguridad. CouchDB también acepta autenticación por cookies. Las cookies duran 10 minutos por defecto y se generan haciendo una petición a _session con formato de formulario HTTP.

curl -vX POST http://127.0.0.1:5984/_session -H "Content-Type:application/x-www-form-urlencoded" -d "name=aarroyoc&password=MiPassword"

Que nos devuelve una cookie válida para operar en la base de datos

Existen plugins que permiten gestionar desde CouchDB autorizaciones más diversas, como por ejemplo OpenID o OAuth2, pero pueden complicarnos mucho el desarrollo.

Los usuarios normales se guardan en la base de datos especial _users y se les puede asignar roles, con permisos para cada base de datos. Por defecto, se pueden crear cuentas de usuario nuevas de forma tan simple como así:

curl -X PUT http://127.0.0.1:5984/_users/org.couchdb.user:USUARIO -d '{"name" : "USUARIO", "password" : "MiPassword", "type" : "user", "roles" : []}'

Es importante seguir el esquema org.couchdb.user:USUARIO para los documentos. Los roles solo los puede ajustar una cuenta de admin, así que en las peticiones anónimas deberá ir vacío.

Si en algún momento quieres saber cuántos usuarios tiene la base de datos, puedes usar la vista predefinida _all_docs.

curl -X GET http://usuarioadmin:contraseñaadmin@127.0.0.1:5984/_users/_all_docs

Estos documentos de usuarios por defecto son privados pero podemos mostrar cierta información para el resto de usuarios, sobretodo si añadimos campos extra a los documentos

curl -X PUT http://usuarioadmin:contraseñaadmin@127.0.0.1:5984/_config/couch_httpd_auth/public_fields -d '"name"'

Hará visible el campo name a todas las peticiones GET anónimas a ese documento de usuario.

Accesos a la base de datos

Ahora vamos a ver como controlar las lecturas y escrituras de una base de datos en concreto. Aquí creo que CouchDB tiene limitaciones y que debe ser algo en lo que enfocarse en futuras versiones pues es un control muy limitado. Si fuese una base de datos interna no sería mucho problema, pero teniendo en cuenta que va a estar expuesta a Internet sería necesario algo más potente. Eso no quiere decir que no se puedan hacer cosas, pero vamos a tener que tirar de funciones JavaScript internas.

Por un lado, tenemos un documento especia en cada base de datos llamado _security. Este contiene listas de admins y miembros. Las listas pueden estar vacías, contener usuarios o contener roles. En caso de que la lista de miembros este vacía se considera que todos los usuarios son miembros (incluido los anónimos). Los miembros pueden leer y escribir todos los archivos. Esto es muy insuficiente. Por poner un caso completo, yo necesitaba:

  • Que todos los usuarios pudieran leer los documentos (incluido anónimos)
  • Que todos los usuarios registrados pudieran crear documentos
  • Que solo se pudiesen modificar los documentos creados por cada usuario

Esto se puede hacer si obligamos a los documentos a que especifiquen un dueño. Todo lo podemos hacer en esta función:

function (new_doc, old_doc, userCtx){
    if(!userCtx.name)
        throw({forbidden: "Not authorized"});

    if(!new_doc.owner)
        throw({forbidden: "Plase, set an owner"});

    if(new_doc.owner != userCtx.name)
        throw({forbidden: "Owner in document should be the same as user"})

    if(old_doc!=null)
        if(old_doc.owner != userCtx.name && userCtx.roles.indexOf("_admin") < 0)
            throw({forbidden: "Not your document"});
    return;
}

Esta función puede ser subida a CouchDB con la API HTTP dentro de un design doc. El design doc quedaría así:

{
        "_rev" : "7-670f7428b5a5afb25ec61382024f0733",
        "views" : {
                "all" : {
                        "map" : "function(doc){ emit(doc.name,doc); }"
                },
                "by-price" : {
                        "map" : "function(doc){ emit(doc.precio,doc); }"
                }
        },
        "validate_doc_update":"function (new_doc, old_doc, userCtx){\n    if(!userCtx.name)\n        throw({forbidden: \"Not authorized\"});\n\n    if(!new_doc.owner)\n\tthrow({forbidden: \"Plase, set an owner\"});\n\n    if(new_doc.owner != userCtx.name)\n        throw({forbidden: \"Owner in document should be the same as user\"})\n\n    if(old_doc!=null)\n        if(old_doc.owner != userCtx.name && userCtx.roles.indexOf(\"_admin\") < 0)\n            throw({forbidden: \"Not your document\"});\n    return;\n} \n"
}

Por supuesto, en _rev irá la revisión que toque. Este sistema es más potente de lo que parece ya que podemos controlar todos los detalles de la operación. Es por ello que muchas veces la función validate_doc_update es la más compleja de los documentos de diseño. Para ayudarme un poco, estoy usando Node.js para leer archivos JavaScript y pasarlos a una cadena JSON válida.

CORS y SSL

Vamos a lanzar CouchDB a Internet. En primer lugar, un par de detalles. CouchDB idealmente vivirá en un dominio separado a la web estática. Para que las peticiones JavaScript funcionen hay que activar CORS. También vamos a proteger los datos así como los usuarios y contraseñas transmitidos. Todo esto podemos hacerlo del tirón con nginx:

 

server {
        listen 443 ssl http2;
        ssl_certificate /etc/letsencrypt/live/alejandria.adrianistan.eu/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/alejandria.adrianistan.eu/privkey.pem;
        server_name alejandria.adrianistan.eu;
        location / {
                add_header Access-Control-Allow-Origin *;
                proxy_pass http://localhost:5984;
                proxy_redirect off;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Ssl on;
        }

        location /_utils/ {
                return 404;
        }
}

¡Listo! Ya tenemos una instancia de CouchDB directamente conectada a Internet. Como podéis comprobar es una alternativa muy interesante en muchos casos. Sin embargo, ciertas cosas son un poco diferentes y más complejas de realizar. Mencionar que CouchDB también es posible usarse con un servidor de por medio en un esquema tradicional. En este caso, podemos usar la cuenta de admin para crear bases de datos personales para cada usuario. Este enfoque es necesario si necesitamos que los datos sean privados o queremos afinar más que roles tienen acceso a una base datos. CouchDB anima a crear tantas bases de datos como necesitemos, varias por usuario si es necesario, para satisfacer nuestros requerimientos. Esto no tiene gran impacto en el rendimiento tal y como está diseñado.

Puede hacerse de forma ligera, con una pequeña app que gestione el alta de usuarios y una vez creada la base de datos y ajustados los permisos se puede usar la API REST de CouchDB desde el lado del cliente con total normalidad, con protecciones de lectura y escritura más precisas.

Este modelo que hemos presentado es ideal para aplicaciones cuya base de datos principal sea pública y accesible y con unos pequeños ajustes puede adaptarse a muchas situaciones. Espero que os haya gustado el artículo. Nos hemos dejado cosas como la replicación entre nodos, los ficheros binarios o attachments ydos complementos a las vistas llamados show y lists, que permiten renderizar HTML o XML entre otras cosas, con los datos de salida de una vista si es necesario.

 

WebAssembly para tontos (usando Rust)

Una de las cosas que más me han sorprendido del mundo web en estos años fue el proyecto WebAssembly. Un proyecto que pretendía traer un bytecode unificado para navegadores. Un proyecto que permitiría compilar prácticamente cualquier lenguaje a la web sin necesidad de tocar JavaScript.

El proyecto surgía de iniciativas fracasadas de Google (PNaCl) y de Mozilla (asm.js). Pero a este proyecto se unieron Microsoft y Apple, por lo que la compatibilidad estaba asegurada.

WebAssembly es un bytecode (como el de Java o el de .NET) que puede ser ejecutado por un navegador, cada navegador implementa su máquina virtual. También es posible usarlo en otros entornos relacionados con el mundo JavaScript como Node.js. Sin embargo entre los objetivos de WebAssembly está no estar atado a JavaScript, por lo que la especificación puede ser implementada por cualquier otro tipo de entorno. Actualmente WebAssembly no tiene recolector de basura y no tiene acceso directo a las Web API. No obstante, sigue siendo un proyecto interesante. Vamos a ver como usar WebAssembly con Rust.

Instalando Rust y Emscripten

Instala Rust, la versión estable es compatible con lo que queremos. Recomiendo usar Rustup.

curl https://sh.rustup.rs -sSf | sh

El paso clave es instalar un nuevo target, el target de WASM32 (WebAssembly de 32 bits).

rustup target add wasm32-unknown-emscripten

Por supuesto también hace falta instalar Emscripten.

Descarga la versión portable de Emscripten aquí. Descomprime y ejecuta

source ./emsdk_env.sh
emsdk update
emsdk install latest
emsdk activate latest
source ./emsdk_env.sh
emcc -v (para comprobar)

Emscripten ya estará instalado junto con Clang y las piezas claves de LLVM necesarias.

Escribiendo el programa en Rust

Vamos a escribir un programa simple. Un hola mundo.

 

fn main(){
    println!("Hola mundo - WebAssembly + Rust");
}

Compilamos con rustc

 

rustc --target=wasm32-unknown-emscripten main.rs -o main.html

Esto genera diversos archivos: main.html, main.js, main.wasm y main.asm.js (para compatibilidad con navegadores que no tienen WebAssembly). El fichero .wasm contiene el bytecode, si intentas abrirlo verás que es ilegible. Sin embargo, Chrome, Firefox, Edge, Safari y Node.js entenderán ese archivo. Probamos el fichero main.html en Firefox (cuya última versión soporta WebAssembly por defecto):


Usando este sistema compilamos aplicaciones enteras. Si se ajustan ciertos parámetros de Emscripten y se usa una crate adecuada en Rust puede usarse para generar juegos 3D usando WebGL escritos 100% en Rust.

Cargas librerías en Rust desde JavaScript

En el paso anterior vimos como compilar a WASM aplicaciones enteras. Ahora vamos a compilar el código de Rust a una librería y vamos a cargarlo con JavaScript.

La librería va a ser muy simple:

#[no_mangle]
pub fn random_number() -> i32 {
    42
}

fn main() {

}

Ahora compilamos el fichero a WebAssembly

rustc --target=wasm32-unknown-emscripten random.rs

Ahora vamos a cargar el fichero random.wasm. Para ello usaremos la ayuda de random.js, que contiene el código necesario para cargar el fichero WASM así como definir los imports que el código Rust espera (variables de entorno, funciones globales, etc).

 

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8"/>
	</head>
	<body>
		<script>
		var Module = {
			wasmBinaryFile: "random.wasm",
			onRuntimeInitialized: main,
		};
		function main() {
			var random_number = Module.cwrap("random_number","number",[]);
			alert(random_number());
		}
		</script>
		<script src="random.js"></script>
	</body>
</html>

Usando este sistema, podemos ir añadiendo código WASM poco a poco en nuestras webs.

 

Conclusión

 
Como vemos, ya es posible hoy día usar WebAssembly en nuestros proyectos. Para crear el código WebAssembly podemos usar Rust. El código WASM puede interactuar con el código JavaScript existente. ¿Qué opináis de WebAssembly? ¿Creéis que puede suponer un antes y un después en el mundo web o se seguirá usando JavaScript de forma masiva?

 

Triángulo de Sierpinski en JavaScript

Un amigo me propuso esta mañana que viera un vídeo de Numberphile, concretamente uno titulado Chaos Game. El vídeo es bastante interesante y habla de como de una aparente aleatoriedad es posible sacar fractales y patrones regulares. Esta misma mañana al llegar a casa y antes de comer me he picado y me he puesto a implementar el algoritmo en JavaScript usando el Canvas de HTML5. El resultado lo tenéis aquí:

http://adrianistan.eu/sierpinski/

Y el código que lleva es el siguiente:

const COLOR_LIST = ["red","green","yellow","pink","brown","purple","cyan","blue","orange"];

function punto(x,y){
    var p = {
        x:x,
        y:y
    };
    return p;
}

function puntoMedio(p,q){
    var m = {
        x: Math.round((p.x+q.x)/2),
        y: Math.round((p.y+q.y)/2)
    };
    return m;
}

function getRandomColor(){
    return COLOR_LIST[Math.floor(COLOR_LIST.length * Math.random())];
}

function dibujarPunto(ctx,p,size){
    ctx.fillStyle = getRandomColor();
    ctx.fillRect(p.x,p.y,size,size);
}

function $(id){
    return document.getElementById(id);
}

function get(id){
    return Math.round(document.getElementById(id).value);
}

function main(){
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");

    var interval;

    $("start").addEventListener("click",function(){

        var size = get("size");
        var vertex = [punto(get("a-x"),get("a-y")),punto(get("b-x"),get("b-y")),punto(get("c-x"),get("c-y"))];

        let p = punto(get("s-x"),get("s-y"));

        dibujarPunto(ctx,p,size);

        interval = setInterval(function(){
            var q = vertex[Math.floor(3*Math.random())];
            p = puntoMedio(p,q);
            dibujarPunto(ctx,p,size);
        },get("speed"));
    });

    $("stop").addEventListener("click",function(){
        clearInterval(interval);
    });

    $("reset").addEventListener("click",function(){
        ctx.fillStyle = "white";
        ctx.fillRect(0,0,600,600);
    });
}

window.addEventListener("DOMContentLoaded",main);

Con distinto número de vértices existen otros fractales, también muy chulos. Incluso en el vídeo de Numberphile realizan un fractal con un gran parecido a una hoja de helecho, usando dos triángulos y una ligera modificación del algoritmo.

Un saludo y soñad con fractales.