El verdadero valor de un desarrollador

If you think you are worth what you know, you are very wrong. Your knowledge today does not have much value beyond a couple of years. Your value is what you can learn and how can you easily adapt to the changes this profession brings so often.

Si crees que vales lo que sabes, estás muy equivocado. Tu conocimiento no tiene mucho valor más allá de un par de años. Tu valor es lo que puedes aprender y cómo puedes adaptarte fácilmente a los cambios que esta profesión trae con tanta frecuencia.

- Jose M. Aguilar

Comparación directa de errores en Go

Como una alternativa rápida a lo que te comenté en la entrada anterior, en lugar de comprobar el tipo de error puedes usar el paquete errors para crear nuevos errores, asignarlos a una variable a nivel de paquete y, cuando fuera necesario, compararlos directamente. Este modo ahorra escribir un poco de código, y es útil para errores que se manajarán dentro del paquete de un modo ágil. Como contra, estos errores no pueden crecer en complejidad, manejando, por ejemplo, un código de error.

El ejemplo anterior quedaría de este modo con esta aproximación:

type error interface {
package main

import (
    "errors"
    "fmt"
    "log"
    "os"
    "strconv"
)

var errPizzaIsOver = errors.New("There are no more pieces of pizza")

type pizza struct {
    numberOfPieces int
}

func (p *pizza) eatAPiece() error {
    if !(p.numberOfPieces > 0) {
        return errPizzaIsOver
    } else {
        p.numberOfPieces--
        fmt.Println("Ñam, ñam...")
        return nil
    }
}

func main() {
    myPizza := &pizza{8}
    fmt.Println("There are " + strconv.FormatUint(uint64(myPizza.numberOfPieces), 10) + " pieces of pizza")
    for {
        err := myPizza.eatAPiece() // This would throw errPizzaIsOver
        if err != nil {
            switch err {
            case errPizzaIsOver:
                log.Println(err.Error())
                os.Exit(0)
            default:
                log.Println("Unknown error occurred")
                os.Exit(0)
            }
        }
        fmt.Println("Now there are " + strconv.FormatUint(uint64(myPizza.numberOfPieces), 10) + " pieces of pizza")
    }
}

Comprobación de tipo de error en Go

Es un fallo habitual, en el manejo de errores dentro de Go, realizar comparasiones de strings contra el mensaje del error que quieres reconocer, pero lo cierto es que, el modo más acertado de discernir que error estas manejando, es hacer una comparación de tipos de error dentro de una sentencia case.

¿Quiero decir entonces que puedo gestionar distintos tipos de error? Claro que sí. Fíjate que, en Go, todo lo que inplemente la interface error es un error. Además, puedes comprobar por tí mismo que es una interface muy sencilla:

type error interface {
  Error() string
}

Para darte un ejemplo voy a definir un struct, ErrPizzaIsOver, el va a contener un string llamado message:

type ErrPizzaIsOver struct {
  message string
}

Para que este struct pueda ser usado como error debe implementar la interface que te mostré más arriba, así que voy a implementarla:

func (e *ErrPizzaIsOver) Error() string {
  return e.message
}

Por último, voy a crear una función que, recibiendo como parámetro un  string, me devuelva un error del tipo ErrPizzaIsOver, y configurado con dicho string como message.

func NewErrPizzaIsOver(message string) *ErrPizzaIsOver {
  return &ErrPizzaIsOver{
    message: message,
  }
}

Todo listo, es el momento de hacer uso de nuestro nuevo tipo de error, así que vamos a ver como quedaría todo. Observa que la magia se hace con el patrón switch err.(type).

package main
import (
  "fmt"
  "os"
  "strconv"
)

type pizza struct {
  numberOfPieces int
}

type ErrPizzaIsOver struct {
  message string
}

func (e *ErrPizzaIsOver) Error() string {
  return e.message
}

func NewErrPizzaIsOver(message string) *ErrPizzaIsOver {
  return &ErrPizzaIsOver{
    message: message,
  }
}

func (p *pizza) eatAPiece() error {
  if !(p.numberOfPieces > 0) {
    return NewErrPizzaIsOver("There are no more pieces of pizza")
  } else {
    p.numberOfPieces--
    fmt.Println("Ñam, ñam...")
    return nil
  }
}

func main() {
  myPizza := &pizza{8}
  fmt.Println("There are " + strconv.FormatUint(uint64(myPizza.numberOfPieces), 10) + " pieces of pizza")
  for {
    err := myPizza.eatAPiece()
    if err != nil {
      switch err.(type) {
        case *ErrPizzaIsOver:
          fmt.Println(err.Error())
          os.Exit(0)
        default:
          fmt.Println("What the h* just happened?")
      }
    }
        fmt.Println("Now there are " + strconv.FormatUint(uint64(myPizza.numberOfPieces), 10) + " pieces of pizza")
    }
}

Canales en Go

Los canales son la característica que Go nos facilita para comunicar entre sí goroutines y poder sincronizar su ejecución.
var c chan string = make(chan string)
El tipo canal se representa con la palabra chan y, a continuación, el tipo de los datos que viajaran a través de dicho canal. En el ejemplo de arriba he declarado la variable c, que contendrá un canal para transferir datos de tipo string. En la misma línea de código he usado make para crear dicho canal que, como puedes observar, se asigna a la variable c.

Para enviar o recibir datos por el canal has de usar el signo <- justo delante o detrás del canal de la manera que sigue.
c <- "Enviando por el canal"
var mensaje := <- c
Esta operación es síncrona, por tanto, el emisor esperará hasta que el receptor está preparado para recibir el envio. Si usas un canal con buffer, la operación será asíncrona, y por tanto, éste bloqueo sólo tendrá lugar cuando el buffer esté lleno.
c := make(chan int, 1)

Go testing

Go incluye un programa llamado go test que te será de gran utilidad para poner a prueba funciones de tu código. Esto es realmente útil para asegurarte de que todo funciona como debería hacerlo después de añadir características o de realizar cambios importantes en tu desarrollo. Para poder fabricar un ejemplo con el uso de go test, supongamos que has escrito un archivo llamado math.go, y que dicho archivo contiene una función que sirve para calcular el promedio de un conjunto de números:

package math

func Average(numbers []float64) float64 {
  total := float64(0)
  for _, x := range numbers {
  total += x
}
return total / float64(len(numbers))

Para preparar pruebas para esta función, sólo tienes que escribir un archivo con el mismo nombre que el archivo que contiene la función a probar, pero en lugar de finalizar en .go lo hará en _test.go. El compilador de Go ignora los archivos que finalizan de este modo cuando usas go install o go build. Como queremos fabricar pruebas para la función Average, en nuestro nuevo archivo vamos a crear una función llamada TestAverage. La función probadora debe empezar con la palabra Test para que go test la ejecute. Te muestro el código que habría en math_test.go y te sigo explicando...

package math

import "testing"

func TestAverage(t *testing.T) {
  result := Average([]float64{2,4})
  if result != 3 {
    t.Error("Expected 3, got ", result)
  }
}

Después de definir el paquete, que es el mismo que el del archivo que contiene la función que estamos probando, has de importa el paquete testing, porque las funciones probadoras usan el tipo *testing.T como parámetro. Dentro de la función probadora, TestAverage, tienes que usar la función que va a ser probada, que en este caso es la función Average. La idea es sencilla, se trata de que le pases a la función Average unos parámetros que generen unos valores de salida que conozcas de antemano, y así podrás comparar los valores devueltos por la función que estás probando con los que tú ya sabes que debería devolver. En el ejemplo se pasan los valores 2 y 4, cuyo resultado previsto, su promedio, es 3. En el caso de que la función Average no devolviera 3 se usa t.Error, que es similar a fmt.Println, con la finalidad de indicarle al programa go test que se ha detectado un error.

Todo listo, ahora sólo te resta ejecutar go test, el cual te debería mostrar un resultado similar al que sigue.
$ go test
PASS
ok    goWorkspace/src/Repository/Project/math    0.029s

State en React

Ya has visto como configurar componentes con props en React, pero ¿cómo puedes definir comportamientos o interacciones con dichos componentes? Para ello necesitas que los componentes puedan definir su estado, y con las props es imposible, pues no pueden ser modificadas por el componente, sólo por su componente padre. Para esto existe this.state, donde los componentes pueden disponer de datos privados modificables a traves de la función this.setState().

Ya sabes que función usar para ajustar this.state, pero ¿Cómo podrías definir un estado inicial del componente? Bien, pues para eso disponemos del constructor de clase. En la función render comprobamos el estado del componente para decidir como va a ser mostrado, y ahora viene la magia: cuando modificamos el estado de un componente, automáticamente se volverán a renderizar todos sus componentes hijos y el propio componente. Esto, además, es bastante ágil gracias al DOM virtual de React.

class Dashboard extends Component {
  constructor() {
    super(...arguments);
    this.state = {
        showAdvancedFeatures: false
    }
  }

  render(){
    let advancedFeatures;
    if(this.state.showAdvancedFeatures){
      advancedFeatures = (
        <AvancedFetaures/>
      )
    }
    return (
      <div className="dashboard">
        <BasicFeatures/>
        {advancedFeatures}
      </div>
    );
  }
}
 

Props en React

React es una biblioteca de JavaScript que se basa en la fabricación y combinación de componentes para construir interfaces de usuario. Cada componente se define por separado en un archivo javascript, y tiene un aspecto similar a este que sigue, el cual se encontraría en el archivo listaCompra.js:

import React, { Component } from 'react';
import Articulo from './Articulo';

class ListaCompra extends Component {
  render() {
    return (
      <ul>
        <Articulo cantidad="1">Pan</Articulo>
        <Articulo cantidad="6">Leche</Articulo>
        <Articulo cantidad="2">Huevos</Articulo>
      </ul>
    );
  }
}

Las propiedades, que veremos en el código escritas como props, se usan para configurar componentes, y son pasadas desde los componentes padres a los componentes hijos. Es importante recordar que el componente hijo no puede modificarlas, el propietario es el componente padre.

En el ejemplo anterior, el componente ListaCompra usa otro componente que aún no hemos escrito, el componente Articulo. En el JSX que devuelve su método render las props son pasadas al componente hijo como etiquetas de atributos similares a las de HTML. En nuestro caso se está pasando la propiedad cantidad. El contenido entre las etiquetas de apertura y cierre también se puede usar como propiedad del componente hijo. En ListaCompra se trata del nombre del artículo.

import React, { Component } from 'react';

class Articulo extends Component {
  render() {
    return (
      <li>
        {this.props.cantidad}× {this.props.children}
      </li>
    );
  }
}

Buscador de texto en Neovim

Otro recurso importante que vas a necesitar en el editor es un buscador de textos. Ante el hecho de que el proyecto que permitía el uso de El surfero de plata en vim, ag.vim, ha quedado obsoleto, te podrías decantar por ack.vim. Con ack.vim puedes hacer uso de ack, que no es ni más ni menos que un grep optimizado para programadores.

Instalar ack es bastante sencillo en distribuciones Linux deribadas de Debian.
$ sudo apt-get install ack-grep
La pega es que, como el paquete ack ya existe, ack es empaquetado como ack-grep. Esto supone teclear más al usar el comando en consola, pero lo puedes solucionar usando las desviaciones de la siguiente manera:
$ sudo dpkg-divert --local --divert /usr/bin/ack --rename --add /usr/bin/ack-grep
Listo, ya dispones de la herramienta ack. Ahora, para poder usarla en tu editor Neovim, sólo añádela como un Plugin de un modo similar a como hicíste con fzf aquí.
" ¡Cualquier URL de git es admitida por vim-plug!
Plug 'https://github.com/mileszs/ack.vim'
Luego en Neovim, ejecuata el comando PlugInstall ack.vim. Está bien familiarizarse con los comandos de vim-plug, así que échales un vistazo aquí.

¿Cómo se usa ack en Neovim? Sencillo...
:Ack [options] {pattern} [{directories}]
Y aquí están los atajos disponibles con esta herramienta.

Buqueda de archivos en Neovim

fzf se define a sí mismo como un buscador difuso de línea de comandos para propósitos generales. Más detalladamente, se trata de un filtro para líneas de comandos Unix, y que puedes aplicar a listas, archivos, procesos, históricos de comandos, etcétera.

Lo puedes instalar en tu carpeta home clonando su repositorio en GitHub de la siguiente manera:
$ git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf

Luego sólo tienes que ejecutar el script de instalación:
$ ~/.fzf/install

Ahora viene lo mejor, y es que fzf puede ser usado como un plugin de Neovim. Si usas el gestor de plugings vim-plug, que aprendimos a instalar aquí, es muy sencillo preparar nuestro buscador de archivos, sólo habría que añadir las siguientes líneas al documento de configuración ~/.config/nvim/init.vim:
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
Plug 'junegunn/fzf.vim'

Para ejecutar fzf cómodamente en neovim puedes asignarle una combinación de teclas, por ejemplo Ctrl + p.
nnoremap <c-p> :FZF<cr>

Cómo instalar vim-plug para Neovim

vim-plug es un gestor de paquetes para Neovim con características muy interesantes. Lo tienes disponible en https://github.com/junegunn/vim-plug, donde puedes encontrar toda la información necesaria para usarlo. Su instalación en sistemas Unix se puede realizar con un sencilo comando:
$ curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vi

Una vez instalado sólo tienes que preparar un archivo de configuración en la dirección ~/.config/nvim/init.vim. Aqui abajo tienes un ejemplo con alguna información:
" Inicia la configuración usando el comando call plug#begin(),
" con el cual puedes especificar el directorio donde se colocarán
" los plugins que queremos usar en nuestro editor.
" En el caso concreto de Neovim puedes usar ~/.local/share/nvim/plugged.

plug#begin('~/.local/share/nvim/plugged')


" A continuación se solicitarán los plugins necesarios. Hay muchos modos de
" hacerlo, por ejemplo, usando una dirección URL de Github.

Plug 'https://github.com/junegunn/vim-github-dashboard.git'


" Finalmente cierra el archivo de configuración con el
" comando call plug#end().

call plug#end()

Para probarlo abre Neovim y ejecuta el comando PlugInstall. Verás un panel lateral donde se muestra el estado de instalación de los plugins indicados en el archivo de configuración de vim-plug.