Formulario de registro en Elm

Recientemente he estado experimentando con Elm, un lenguaje funcional puro y a la vez framework de backend. De momento he programado ya he visto unas cuántas cosas y puedo decir que me gusta bastante, que es un soplo de aire fresco en el mundo web y que sinceramente no creo que vaya a triunfar algún día. Sin embargo para proyectos personales, lo veo como alternativa superior a Angular, React y Vue.

Uno de los ejercicios que puede venir bien para entender Elm es implementar un formulario de registro con normas para la contraseña. Según vayamos escribiendo se nos informa sobre qué errores tiene la contraseña para ser segura y cuando lo sea mostramos el botón de registro.

Es un código un poco tonto pero me apetecía compartirlo.

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput)
import Char
import String
import List

main : Program Never Model Msg
main = Html.program {init = init, view = view, update = update, subscriptions = subscriptions}

type alias Model =
{
    name : String,
    password : String,
    passwordAgain : String
}

init : (Model, Cmd Msg)
init = (Model "" "" "", Cmd.none)

subscriptions : Model -> Sub Msg
subscriptions model = Sub.none

type Msg = Name String | Password String | PasswordAgain String

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        Name name -> ({ model | name = name},Cmd.none)
        Password pass -> ({model | password = pass},Cmd.none)
        PasswordAgain pass -> ({model | passwordAgain = pass},Cmd.none)

view : Model -> Html Msg
view model =
    div []
        [
            input[type_ "text", placeholder "Name", onInput Name][],
            input[type_ "password", placeholder "Password", onInput Password][],
            input[type_ "password", placeholder "Confirm", onInput PasswordAgain][],
            viewValidate model
        ]

viewValidate : Model -> Html Msg
viewValidate model =
    let list = checkMinLength model :: checkPassword model :: checkUpper model :: checkLower model :: checkDigit model :: []
    wrongList = List.filter (\(x,y) -> not x) list
    in
    if List.length wrongList == 0 then
        div [] [button[]]
    else
        div [] (List.map showError wrongList )

showError : (Bool,String) -> Html Msg
showError (_,error) =
    div [style[("color","red")]] 

checkPassword : Model -> (Bool, String)
checkPassword model =
    if model.password == model.passwordAgain then
        (True,"")
    else
        (False,"Passwords do not match")

checkMinLength : Model -> (Bool,String)
checkMinLength model =
    if String.length model.password > 7 then
        (True,"OK")
    else
        (False,"Password must have a minimum length of 8 characters")

checkUpper : Model -> (Bool,String)
checkUpper model =
    let isUp = String.any Char.isUpper model.password
    in
    if isUp then
        (True,"")
    else
        (False,"The password must contain an upper letter")

checkLower : Model -> (Bool,String)
checkLower model =
    let isDown = String.any Char.isLower model.password
    in
    if isDown then
        (True,"")
    else
        (False,"The password must contain a lower letter")

checkDigit : Model -> (Bool,String)
checkDigit model =
    let isDig = String.any Char.isDigit model.password
    in
    if isDig then
        (True,"")
    else
        (False,"The password must contain a digit")

 

loading...

¿Qué hacer si pierdes o te roban el móvil?

Nunca pensamos que pueda pasar y sin embargo, constantemente se dan situaciones donde esto pueda pasar. Me refiero a que perdamos o nos roben el móvil. Hoy en día los móviles son una extensión nuestra y son vitales para nuestro funcionamiento. Por tanto podemos pasarlo bastante mal si te roban el móvil o lo pierdes.

Afortunadamente, los móviles actuales cuentan con sistemas que permiten localizar el móvil (siempre que el teléfono siga encendido claro está).

Si tu dispositivo tiene Android, la opción más común es usar el Android Device Manager. Se trata de una aplicación integrada de serie en los dispositivos Android con Google Play. Adicionalmente existe en Google Play y en la página web una aplicación para localizar el dispositivo.

Tiene varias opciones, entre ellas es muy interesante la de reproducir sonido si estamos cerca del teléfono y no lo vemos y la de borrar los datos, si vemos difícil recuperar el teléfono, que al menos no lleguen a cualquier mano.

Para usuarios de iOS existe una opción similar llamada Find my iPhone. Se activa desde la web de iCloud. Su funcionamiento es muy similar a Android Device Manager aunque se integra también con Apple Pay, la plataforma de pagos de Apple, para bloquear los pagos.

Estos métodos son los oficiales de los fabricantes para cada sistema operativo. No obstante, existen más aplicaciones de terceros. En dispositivos iOS tenemos la aplicación Life360 GPS Tracking, que nos permite conocer la ubicación GPS de familiares y amigos si ellos nos dan su autorización expresa. Puede ser interesante dejar configurado este servicio con un amigo para así obtener la ubicación precisa en caso de pérdida o sustracción. Una aplicación similar pero compatible con Android e iOS es Instamapper.

Pero existen todavía más métodos, quizá no tengas más opciones ya que no configuraste tu teléfono anteriormente. En ese caso te preguntarás cómo rastrear un celular por número. Existen webs gratuitas, que afirman ser capaces de localizar los dispositivos. Algunas de ellas son: www.sat-gps-locate.com y www.themobiletracker.com. En estas webs solo hay que introducir el número completo de teléfono (con prefijo de país) y localizan el teléfono. Según las propias webs la precisión es de 10 metros en Europa y de 25 metros en Latinoamérica. Usan una combinación de tecnología GSM (que disponen todos los teléfonos) y GPS.

Existen muchas aplicaciones, vamos a mencionar otra conocida, llamada SeekDroid. De funcionamiento a Android Device Manager, tiene algunas opciones extra que pueden interesarnos. Podemos enviar mensajes al dispositivo que tiene el ladrón y de ese modo advertirle de qué te has dado cuenta del robo. El inconveniente que tiene es que necesita conexión a Internet para operar. Si tu teléfono tiene contratado datos no es inconveniente, pero si no lo tiene solo podrás localizarlo cuando el ladrón se conecte por Wi-Fi.

En todo caso recuerda usar estos métodos sabiamente y sin abusar. Rastrear a una persona mayor de edad sin su consentimiento puede ser un delito. Usa estos métodos solo para rastrear tu propio teléfono y el de familiares y amigos que te dejen.

 

 

Documentación con rustdoc

La documentación es una parte importante y muchas veces olvidada de un proyecto de software. Rust cuenta desde el principio con una herramienta destinada a que escribir documentación sea poco doloroso, se trata de rustdoc.

Comentarios en Markdown

Los comentarios de rustdoc empiezan con 3 barras y pueden llevar Markdown. Se escriben encima de la función, estructura, etc que describamos.

/// Perfil almacena los datos del perfil de un usuario en nuestra webapp
struct Perfil{
    username: String,
    password: String,
    url: Option<String>
}

impl Perfil{
    /// Genera un nuevo Perfil
    /// # Ejemplo
    /// ```
    /// let user = Perfil::new("The42","1234");
    /// ```
    pub fn new(u: &str, p: &str) -> Perfil{
        Perfil {username: String::from(u), password: String::from(p), url: None}
    }
}

Mencionar que el código del comentario es sometido a tests también por cargo test. Para generar la documentación basta con escribir cargo doc y Cargo generará la documentación en formato HTML.

Consultando documentación

El mejor sitio para leer la documentación de Rust es Docs.rs. Docs.rs ejecuta cargo doc a todas las crates de Crates.io y las expone al público sin costo.

 

Tests en Rust

Mientras los programas no puedan verificarse de forma matemática de forma sencilla, la única manera de asegurarse que un programa más o menos funciona es con tests. Rust soporta tests de forma nativa. Gracias a la directiva #[test].

Definiendo una función de test

Una función de test es cualquiera que lleve la directiva #[test]. Normalmente, estos tests se dejan dentro de un módulo con la directiva #[cfg(test)].

fn suma(a: i32,b: i32) -> i32{
    a+b
}

#[cfg(test)]
mod test{

    #[test]
    fn suma(){
        assert_eq!(super::suma(4,5),9);
    }
}

La macro assert_eq! provocará un panic! si sus argumentos no coinciden. También es posible hacer fallar los tests llamando a panic! manualmente. ¿Cómo se ejecutan estos test te preguntarás? Sencillo, con cargo test. Automáticamente Cargo selecciona las funciones de test y las ejecuta todas, generando un informe de tests existosos y tests que han fallado.

Obviamente, existen más opciones dentro de los tests. assert! comprobará que una expresión sea true. #[should_panic] se deberá indicar en aquellas funciones de test en lo que lo correcto sea que ocurra un panic!. Por otro lado, la trait Debug es interesante.

Es posible ejecutar tests de forma manual, con cargo test NOMBRE_TEST.

 

Cargo y módulos en Rust

El software va creciendo poco a poco en un proyecto y es vital que esté bien organizado. Rust incluye un potente sistema de módulos para manejar estas situaciones y Cargo nos ayuda a gestionar las dependencias del proyecto.

Módulos

Los módulos se definen con la palabra reservada mod y por defecto todo es privado. Es necesario indicar pub para hacer esos campos públicos. Con use podemos acortar la manera con la que hacemos referencia a los componentes del módulo. Así pues, use es similar a using namespace de C++.

mod network{
    pub fn version() -> String{
        String::from("Network 1.0.0")
    }
    pub mod server{
        fn private_thing(){

        }
        pub fn public_thing(){

        }
    }
}

fn main() {
    println!("{}",network::version());
    {
        use network::server::public_thing;
        public_thing();
    }
}

Los módulos resultan una opción más interesante si tenemos múltiples archivos. Por norma general, si un módulo se define en el fichero de entrada del compilador, este se busca en dos sitios:

  • Un fichero nombre_del_modulo.rs
  • Un fichero nombre_del_modulo/mod.rs (opción recomendada para módulos de varios archivos)

Por lo que el código anterior puede sustituirse por esto en el fichero principal:

mod network;

fn main() {
    println!("{}",network::version());
    {
        use network::server::public_thing;
        public_thing();
    }
}

Y esto en un fichero network.rs

pub fn version() -> String{
    String::from("Network 1.0.0")
}
pub mod server{
    fn private_thing(){

    }
    pub fn public_thing(){

    }
}

Podemos usar use-as para modificar el nombre con el que se importa una función.

mod network;

fn main() {
    println!("{}",network::version());
    {
        use network::server::public_thing as super_thing;
        super_thing();
    }
}

 

Crates

El código en Rust se organiza en crates, que son colecciones de módulos que se distribuyen. Piensa en ello como si fuesen gemas de Ruby o librerías de C++. Las crates se pueden compartir con la gente. El mayor repositorio de crates de la comunidad Rust es crates.io.

Las crates se pueden generar con rustc o con cargo, aunque es habitual usar la segunda opción. Vamos a ver primero como usaríamos una crate externa. En este caso voy a usar regex. Sé que en esta parte no vas a saber como descargar regex y usarla, pero voy a explicar el código.

extern crate regex;

use regex::Regex;

fn main() {
    let r = Regex::new(r"([A-Za-z0-9])([.]|[_]|[A-Za-z0-9])+@gmail.com").unwrap();
    if r.is_match("pucela_segunda@gmail.com") {
        println!("Correo válido de Gmail");
    }else{
        println!("Correo no válido de Gmail");
    }
}

Básicamente se usa extern crate para importar una crate externa, en este caso regex. Con use lo único que hacemos es acortar la manera de llamar a las funciones. Si no estuviese podríamos poner perfectamente regex::Regex::new en vez de Regex::new y funcionaría.

Cargo

Cargo es una herramienta que cumple dos funciones dentro del ecosistema Rust. Por un lado es un gestor de dependencias y por otro lado gestiona también las compilaciones.

En su parte de gestor de dependencias nos permite especificar que crates necesita el proyecto para compilar, su versión y si la crate lo soporta, con qué características activadas.

En su parte de gestor de compilaciones, realiza la compilación con rustc y añade las flags pertinentes al compilador. También se le pueden especificar cosas más complejas, aunque en Rust no suele ser habitual tener configuraciones de compilación excesivamente complejas.

Todo lo relativo a Cargo se define en el archivo Cargo.toml. Que tiene una estructura similar a esta:

[package]
name = "super_app"
version = "0.1.0"
authors = ["Adrián Arroyo Calle"]

[dependencies]
regex = "0.2.2"

En la sección dependencies puedes añadir línea por línea la crate que te haga falta. Consulta la documentación de cada crate para esto, pues puede haber variaciones.

Comandos básicos de Cargo

Crear un proyecto con Cargo

cargo new –bin mi_proyecto # si queremos que sea ejecutable

cargo new mi_crate # si queremos que sea una crate

Compilar y ejecutar

cargo run

cargo build # solo compilar

cargo build –release # compilar en modo Release

Cargo automáticamente se encarga de obtener las dependencias en la fase de compilación.

Instalar aplicaciones

cargo install APLICACION

Muchas herramientas de pueden distribuir a través de Cargo. Por ejemplo, Racer, un programa que sirve de autocompletado para IDEs como Eclipse o Visual Studio.

Ejecutar tests

cargo test

Generar documentación

cargo doc

Plugins de Cargo

Cargo es extensible gracias a plugins. Algunos interesantes son Clippy, cargo-audit, rustfmt,