Skip to content

Instantly share code, notes, and snippets.

@rgs1
Created October 28, 2022 17:58
Show Gist options
  • Save rgs1/163732ef1277f408cdcfa749a7e11a6b to your computer and use it in GitHub Desktop.
Save rgs1/163732ef1277f408cdcfa749a7e11a6b to your computer and use it in GitHub Desktop.
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"net/mail"
"os"
)
var (
logPath = "emails.log"
serverAddress = "127.0.0.1:8080"
)
// definicion de estructura
type Message struct {
Email string
}
// asignando un metodo a una estructura (OOP, pero sin mucha burocracia)
func (m *Message) valid() bool {
_, err := mail.ParseAddress(m.Email)
return err == nil
}
// defino una interface...
type Logger interface {
Save(m Message)
}
type LoggerImpl struct {
c chan Message
}
// Implementar todos los metodos de una interface nos hace adherir a la misma
func (l *LoggerImpl) Save(m Message) {
l.c <- m
}
func NewLoggerImpl() *LoggerImpl {
// Toma de punter explicita --> golang ahora sabe que este objeto tiene que vivir en el heap
return &LoggerImpl{
c: make(chan Message),
}
}
func (l *LoggerImpl) run() {
f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Println(err)
return
}
// Al salir de este metodo, cerrar el archivo (no tengo que preocuparme de cada return individual)
defer f.Close()
for {
// recibir un mensaje a traves del canal c
msg := <-l.c
fmt.Println(msg.Email)
// guardar
if _, err := f.WriteString(msg.Email + "\n"); err != nil {
fmt.Println(err)
}
}
}
func handler(w http.ResponseWriter, r *http.Request, l Logger) {
switch r.Method {
case "POST":
var msg Message
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&msg)
if err != nil {
http.Error(w, "decoding error", http.StatusBadRequest)
return
}
if !msg.valid() {
http.Error(w, "invalid email", http.StatusBadRequest)
return
}
if len(msg.Email) > 100 {
http.Error(w, "email too long", http.StatusBadRequest)
return
}
// Utilizar el canal para notificar al otro hilo o goroutine sobre el trabajo pendiente
// Esto es practicamente inmediato (depende del tamaño del canal), asi que no bloquea al usuario
l.Save(msg)
fmt.Fprintf(w, "ok\n")
default:
http.Error(w, "only POST is supported", http.StatusNotFound)
}
}
func main() {
l := NewLoggerImpl() // <-- inferencia de tipo
go l.run() // <-- concurrencia en una sola linea!!
// funciones definidas dentro de otra funcion
http.HandleFunc("/add-email", func(w http.ResponseWriter, r *http.Request) {
handler(w, r, l)
})
// asignar y chequear en la misma linea
if err := http.ListenAndServe(serverAddress, nil); err != nil {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment