Artículos

CRUD en memoria con Golang

Recientemente he estado trasteando con el lenguaje Go, y como siempre he dicho: la mejor manera de aprender es haciendo, por lo cual he realizado un pequeño proyecto de consola para poder practicar. El código final del proyecto lo puedes encontrar en este enlace.

Si ya has programado en Go con anterioridad debes saber que en Go no existen las clases, y si vienes de un lenguaje de programación completamente orientado a objetos lo primero que buscar es como programar una clase que represente tu modelo de datos.

Entonces, si en Go no existen las clases; como se hace para representar tu modelo de datos?, bueno pues en Go puedes crear nuevos tipos de datos muy facilmente con algo llamado struct, una vez creado el nuevo tipo de datos se pueden crear nuevas ‘instancias’ de estos tipos de datos y asignarlos a las variables al igual que como hacemos cuando instanciamos una clase, a continuacion de muestro el tipo de datos que he creado para el CRUD que he programado:

package models

// Usuario represent the user model
type Usuario struct {
	ID       int
	Nombre   string
	Apellido string
}

Luego de crear este paquete llamado models también he creado el paquete controllers para poder programar las acciones del CRUD y separar un poco la lógica que hace render de la interfaz de usuario:

En este paquete he escrito dos archivos uno para el menu y otro para las acciones CRUD MenuController:

package controllers

import (
	"fmt"
	"strings"
)

// PrintMenu imprime el menu en pantalla
func PrintMenu() {
	fmt.Println(strings.ToUpper("1- Lista de usuarios"))
	fmt.Println(strings.ToUpper("2- Ver un usuario"))
	fmt.Println(strings.ToUpper("3- Crear usuario"))
	fmt.Println(strings.ToUpper("4- Actualizar usuario"))
	fmt.Println(strings.ToUpper("5- Salir del programa"))
	doTask()
}

func doTask() {
	var opcion string
	fmt.Scanln(&opcion)

	switch opcion {
	case "1":
		listUsers()
		PrintMenu()
	case "2":
		getUserByIndex()
		PrintMenu()

	case "3":
		createUser()
		PrintMenu()
	case "4":
		updateUser()
		PrintMenu()
	case "5":
		break
	default:
		fmt.Println("Selecciona una opcion de la lista")
		PrintMenu()
	}
}

// PrintHeader imprime la cabecera del programa
func PrintHeader() {
	decorador := "*"
	titulo := "* crud de usuario en memoria *"
	fmt.Println(strings.Repeat(decorador, len(titulo)))
	fmt.Println(strings.ToUpper(titulo))
	fmt.Println(strings.Repeat(decorador, len(titulo)))
}

MainController:

package controllers

import (
	"crud-memoria/models"
	"fmt"
	"strconv"
	"strings"

	"syreclabs.com/go/faker"
)

var usuarios []models.Usuario

func listUsers() {
	for _, item := range usuarios {
		fmt.Println(item.ID, item.Nombre, item.Apellido)
	}
}

func getUserByIndex() (models.Usuario, int) {
	var index string
	fmt.Scanln(&index)
	i, _ := strconv.Atoi(index)
	if i > len(usuarios) {
		fmt.Println("El usuario no existe")
		return models.Usuario{}, i
	}
	usuario := usuarios[i]

	decorador := "*"
	titulo := "* Usuario del indice " + index + " *"
	fmt.Println(strings.Repeat(decorador, len(titulo)))
	fmt.Println(strings.ToUpper(titulo))
	fmt.Println(strings.Repeat(decorador, len(titulo)))
	fmt.Println(usuario.ID, usuario.Nombre, usuario.Apellido)
	fmt.Println()
	fmt.Println()
	fmt.Println()
	return usuario, i

}

func createUser() {
	var nombre, apellido string
	fmt.Println("Escriba el nombre:")
	fmt.Scanln(&nombre)

	fmt.Println("Escriba el apellido:")
	fmt.Scanln(&apellido)

	usuario := models.Usuario{faker.Number().NumberInt(5), nombre, apellido}
	usuarios = append(usuarios, usuario)
	fmt.Println("usuario creado exitosamente")

}

func updateUser() {
	_, indice := getUserByIndex()
	var nombre, apellido string
	fmt.Println("Escriba el nombre:")
	fmt.Scanln(&nombre)

	fmt.Println("Escriba el apellido:")
	fmt.Scanln(&apellido)

	usuarios[indice].Nombre = nombre
	usuarios[indice].Apellido = apellido

	fmt.Println("Usuario actualizado exitosamente")

}

// FillInitialData rellena la base de datos con datos fake
func FillInitialData(cantidad uint) {
	for i := 0; i <= int(cantidad); i++ {
		usuario := models.Usuario{faker.Number().NumberInt(5), faker.Name().FirstName(), faker.Name().LastName()}
		usuarios = append(usuarios, usuario)
	}
}

En los controladores solamente he utilizado paquetes de la biblioteca standar, con la única exception de un paquete para la generación de datos fake syreclabs.com/go/faker para poder hacer pruebas.

Una de las cosas que mas me llamó la atención acerca de Go es la posibilidad de que un método pueda retornar mas de un valor simultaneamente.

Otra de las cosas un poco extrañas es la forma en como Go maneja el acceso a las funciones declaradas en otros paquetes:

  • Todos las variables y métodos que comienzan con letra mayúsculas pueden ser accedidos desde otros paquetes, de lo contrario solo pueden ser accedidos del paquete actual, esto también aplica para los structs y para los datos internos que alberga.

Para construir/compilar el codigo debes ejecutar el siguiente comando en terminal:

go build main.go

Para ejecutar el proyecto:

./main

El resultado debe ser algo como esto: protipo-con-vue