├── .github
└── ISSUE_TEMPLATE
│ ├── error-en-los-ejemplos.md
│ └── problemas-con-los-slides.md
├── .gitignore
├── CONTRIBUTORS.md
├── LICENSE
├── README.md
├── README.md.org
├── TODO.org
├── day-01
├── 01-installation
│ └── README.md.org
├── 02-hello-world
│ ├── README.md.org
│ └── main.go
├── 03-variables
│ ├── README.md.org
│ └── main.go
├── 04-types
│ ├── README.md.org
│ └── main.go
├── 05-functions
│ ├── README.md.org
│ └── main.go
├── 06-control-statments
│ ├── README.md.org
│ ├── for.go
│ ├── if.go
│ └── switch.go
├── 07-arrays
│ ├── README.md.org
│ └── main.go
├── 08-maps
│ ├── README.md.org
│ └── main.go
├── 09-packages-modules
│ ├── README.md.org
│ ├── geometry
│ │ └── square.go
│ ├── go.mod
│ └── main.go
└── 10-cmdline
│ ├── README.md.org
│ ├── go.mod
│ └── main.go
├── day-02
├── 11-errors-defer-panic-recover
│ ├── README.md.org
│ ├── defer-recover
│ │ └── main.go
│ └── errors
│ │ └── main.go
├── 12-testing
│ ├── README.md.org
│ ├── functions.go
│ ├── functions_test.go
│ ├── go.mod
│ └── testdata
│ │ └── hello_world
├── 13-structs-pointers
│ ├── README.md.org
│ └── main.go
├── 14-interfaces
│ ├── README.md.org
│ ├── empty-interface.go
│ ├── interface-values.go
│ ├── interfaces-struct-composition.go
│ ├── interfaces.go
│ └── type-assertions.go
└── 15-concurrency
│ ├── README.md.org
│ └── concurrency
│ ├── go.mod
│ └── main.go
├── day-03
├── 16-json
│ ├── README.md.org
│ └── json-ser-de
│ │ ├── go.mod
│ │ └── main.go
├── 17-rest-api
│ ├── README.md.org
│ ├── hello-world-api
│ │ └── main.go
│ ├── helloworld-api-gorilla
│ │ └── main.go
│ ├── rest-api-gorilla
│ │ └── main.go
│ └── rest-api
│ │ ├── go.mod
│ │ └── main.go
└── 18-http-client
│ ├── README.md.org
│ ├── go.mod
│ └── main.go
├── day-04
├── challenge1
│ ├── 1.1
│ │ ├── README.md.org
│ │ └── main.go
│ ├── 1.2
│ │ ├── README.md.org
│ │ └── main.go
│ ├── 1.3
│ │ ├── README.md.org
│ │ ├── reverseapp
│ │ │ └── main.go
│ │ └── stringutil
│ │ │ └── reverse.go
│ ├── 1.4
│ │ ├── README.md.org
│ │ ├── reverseapp
│ │ │ └── main.go
│ │ └── stringutil
│ │ │ └── reverse.go
│ └── 1.5
│ │ ├── README.md.org
│ │ ├── reverseapp
│ │ └── main.go
│ │ └── stringutil
│ │ ├── reverse.go
│ │ └── reverse_test.go
└── challenge2
│ ├── 2.1
│ ├── README.md.org
│ └── main.go
│ ├── 2.2
│ ├── README.md.org
│ ├── main.go
│ └── main_test.go
│ └── 2.3
│ ├── README.md.org
│ ├── main.go
│ └── main_test.go
├── day-05
├── challenge3
│ ├── README.md.org
│ ├── math.go
│ └── math_test.go
└── challenge4
│ ├── 4.1
│ ├── README.md.org
│ └── main.go
│ ├── 4.2
│ ├── README.md.org
│ ├── inmemoryrepository.go
│ ├── main.go
│ ├── mongorepository.go
│ └── todorepository.go
│ └── 4.3
│ ├── README.md.org
│ ├── go.mod
│ ├── go.sum
│ ├── main.go
│ ├── main_test.go
│ ├── memoryrepository.go
│ ├── mongorepository.go
│ ├── todorepository.go
│ └── views
│ └── index.html
├── day-06
├── 19-dbaccess
│ ├── README.md.org
│ ├── mongo
│ │ ├── dbaccess.go
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── main.go
│ └── sqlite
│ │ ├── data.db
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── main.go
└── 20-echo
│ ├── README.md.org
│ └── echo
│ ├── cmd
│ └── litter
│ │ └── litter.go
│ ├── go.mod
│ ├── go.sum
│ └── internal
│ ├── handler
│ ├── handler.go
│ ├── post.go
│ └── user.go
│ └── model
│ ├── post.go
│ └── user.go
├── day-07
├── 21-sarama
│ ├── README.md.org
│ ├── consumergroup
│ │ ├── README.md
│ │ ├── go.mod
│ │ └── main.go
│ ├── http_server
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── go.mod
│ │ ├── http_server.go
│ │ └── http_server_test.go
│ ├── interceptors
│ │ ├── README.md
│ │ ├── go.mod
│ │ ├── main.go
│ │ └── trace_interceptor.go
│ └── producer-consumer
│ │ ├── cmd
│ │ ├── consumer-groups
│ │ │ └── consumer-groups.go
│ │ ├── consumer
│ │ │ └── consumer.go
│ │ └── producer
│ │ │ └── producer.go
│ │ ├── go.mod
│ │ └── go.sum
└── 22-concurrency-patterns
│ └── README.md.org
├── index.html
└── reveal
├── css
├── layout.scss
├── print
│ ├── paper.scss
│ └── pdf.scss
├── reveal.scss
└── theme
│ ├── README.md
│ ├── source
│ ├── beige.scss
│ ├── black.scss
│ ├── blood.scss
│ ├── league.scss
│ ├── moon.scss
│ ├── night.scss
│ ├── serif.scss
│ ├── simple.scss
│ ├── sky.scss
│ ├── solarized.scss
│ └── white.scss
│ └── template
│ ├── exposer.scss
│ ├── mixins.scss
│ ├── settings.scss
│ └── theme.scss
├── dist
├── reset.css
├── reveal.css
├── reveal.esm.js
├── reveal.js
└── theme
│ ├── beige.css
│ ├── black.css
│ ├── blood.css
│ ├── fonts
│ ├── league-gothic
│ │ ├── LICENSE
│ │ ├── league-gothic.css
│ │ ├── league-gothic.eot
│ │ ├── league-gothic.ttf
│ │ └── league-gothic.woff
│ └── source-sans-pro
│ │ ├── LICENSE
│ │ ├── source-sans-pro-italic.eot
│ │ ├── source-sans-pro-italic.ttf
│ │ ├── source-sans-pro-italic.woff
│ │ ├── source-sans-pro-regular.eot
│ │ ├── source-sans-pro-regular.ttf
│ │ ├── source-sans-pro-regular.woff
│ │ ├── source-sans-pro-semibold.eot
│ │ ├── source-sans-pro-semibold.ttf
│ │ ├── source-sans-pro-semibold.woff
│ │ ├── source-sans-pro-semibolditalic.eot
│ │ ├── source-sans-pro-semibolditalic.ttf
│ │ ├── source-sans-pro-semibolditalic.woff
│ │ └── source-sans-pro.css
│ ├── league.css
│ ├── moon.css
│ ├── night.css
│ ├── serif.css
│ ├── simple.css
│ ├── sky.css
│ ├── solarized.css
│ └── white.css
└── plugin
├── highlight
├── highlight.esm.js
├── highlight.js
├── monokai.css
├── plugin.js
└── zenburn.css
├── markdown
├── markdown.esm.js
├── markdown.js
└── plugin.js
├── math
├── math.esm.js
├── math.js
└── plugin.js
├── notes
├── notes.esm.js
├── notes.js
├── plugin.js
└── speaker-view.html
├── search
├── plugin.js
├── search.esm.js
└── search.js
└── zoom
├── plugin.js
├── zoom.esm.js
└── zoom.js
/.github/ISSUE_TEMPLATE/error-en-los-ejemplos.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Error en los ejemplos
3 | about: Reporar errores o mejoras con los ejemplos
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: yorodm
7 |
8 | ---
9 |
10 | **Describa el error**
11 | Descripción pequeña del error.
12 | Ejemplo (camino hasta el proyecto):
13 | Error:
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/problemas-con-los-slides.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Problemas con los slides
3 | about: Reportar problemas con los slides
4 | title: "[SLIDE]"
5 | labels: documentation
6 | assignees: yorodm
7 |
8 | ---
9 |
10 | Número de sección:
11 | Problema:
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
--------------------------------------------------------------------------------
/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 | # Colaboradores
2 |
3 | Agradecimientos a los siguientes usuarios que han colaborado con el
4 | _workshop_ en general:
5 |
6 | 1. [leiserfg](https://github.com/leiserfg)
7 | 2. [oneohthree](https://github.com/oneohthree)
8 | 3. [cccaballero](https://github.com/cccaballero)
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2013 Shopify
4 | Copyright (c) 2017 LabStack
5 | Copyright (c) 2019 Rodolfo Finochietti
6 | Copyright (c) 2021 Yoandy Rodriguez (https://github.com/yorodm)
7 |
8 |
9 | Permission is hereby granted, free of charge, to any person obtaining a copy
10 | of this software and associated documentation files (the "Software"), to deal
11 | in the Software without restriction, including without limitation the rights
12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | copies of the Software, and to permit persons to whom the Software is
14 | furnished to do so, subject to the following conditions:
15 |
16 | The above copyright notice and this permission notice shall be included in all
17 | copies or substantial portions of the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | SOFTWARE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Golang desde 0 hasta API
2 |
3 | Slides y ejemplos para un workshop de Golang para principiantes.
4 |
5 | ## Tabla de contenidos
6 |
7 | 1. Primeros pasos
8 | 2. Hello World
9 | 3. Variables
10 | 4. Tipos
11 | 5. Funciones
12 | 6. Instrucciones de control
13 | 7. Arreglos
14 | 8. Maps
15 | 9. Paquetes y módulos
16 | 10. Linea de comandos
17 | 11. Manejo de errores: error, defer, recover, panic
18 | 12. Pruebas unitarias
19 | 13. Structs y punteros
20 | 14. Interfaces
21 | 15. Concurrencia en Go.
22 | 16. Formatos de datos: JSON y XML
23 | 17. Creando APIs REST
24 | 18. HTTP Client
25 | 19. Challenge 1.1
26 | 20. Challenge 1.2
27 | 21. Challenge 1.3
28 | 22. Challenge 1.4
29 | 23. Challenge 1.5
30 | 24. Challenge 2.1
31 | 25. Challenge 2.2
32 | 26. Challenge 2.3
33 | 27. Challenge 3
34 | 28. Challenge 4.1
35 | 29. Challenge 4.2
36 | 30. Challenge 4.3
37 | 31. Acceso a bases de datos
38 | 32. Labstack Echo
39 | 33. Golang y Kafka [en progreso]
40 |
--------------------------------------------------------------------------------
/README.md.org:
--------------------------------------------------------------------------------
1 | #+EXPORT_FILE_NAME: index.html
2 | #+REVEAL_ROOT: ./reveal/
3 | #+Title: Golang de 0 a API.
4 | #+Author: Yoandy Rodríguez Martínez
5 | #+Email: yoandy.rmartinez@gmail.com
6 |
7 | #+OPTIONS: date:nil toc:nil timestamp:nil reveal_width:1200, reveal_height:800
8 | #+REVEAL_MARGIN: 0.1
9 | #+REVEAL_MIN_SCALE: 0.2
10 | #+REVEAL_MAX_SCALE: 2.5
11 | #+REVEAL_THEME: moon
12 | #+REVEAL_TRANS: none
13 | #+REVEAL_HLEVEL: 2
14 | #+REVEAL_HEAD_PREAMBLE:
15 |
16 | #+INCLUDE: day-01/01-installation/README.md.org
17 | #+INCLUDE: day-01/02-hello-world/README.md.org
18 | #+INCLUDE: day-01/03-variables/README.md.org
19 | #+INCLUDE: day-01/04-types/README.md.org
20 | #+INCLUDE: day-01/05-functions/README.md.org
21 | #+INCLUDE: day-01/06-control-statments/README.md.org
22 | #+INCLUDE: day-01/07-arrays/README.md.org
23 | #+INCLUDE: day-01/08-maps/README.md.org
24 | #+INCLUDE: day-01/09-packages-modules/README.md.org
25 | #+INCLUDE: day-01/10-cmdline/README.md.org
26 | #+INCLUDE: day-02/11-errors-defer-panic-recover/README.md.org
27 | #+INCLUDE: day-02/12-testing/README.md.org
28 | #+INCLUDE: day-02/13-structs-pointers/README.md.org
29 | #+INCLUDE: day-02/14-interfaces/README.md.org
30 | #+INCLUDE: day-02/15-concurrency/README.md.org
31 | #+INCLUDE: day-03/16-json/README.md.org
32 | #+INCLUDE: day-03/17-rest-api/README.md.org
33 | #+INCLUDE: day-03/18-http-client/README.md.org
34 | #+INCLUDE: day-04/challenge1/1.1/README.md.org
35 | #+INCLUDE: day-04/challenge1/1.2/README.md.org
36 | #+INCLUDE: day-04/challenge1/1.3/README.md.org
37 | #+INCLUDE: day-04/challenge1/1.4/README.md.org
38 | #+INCLUDE: day-04/challenge1/1.5/README.md.org
39 | #+INCLUDE: day-04/challenge2/2.1/README.md.org
40 | #+INCLUDE: day-04/challenge2/2.2/README.md.org
41 | #+INCLUDE: day-04/challenge2/2.3/README.md.org
42 | #+INCLUDE: day-05/challenge3/README.md.org
43 | #+INCLUDE: day-05/challenge4/4.1/README.md.org
44 | #+INCLUDE: day-05/challenge4/4.2/README.md.org
45 | #+INCLUDE: day-05/challenge4/4.3/README.md.org
46 | #+INCLUDE: day-06/19-dbaccess/README.md.org
47 | #+INCLUDE: day-06/20-echo/README.md.org
48 | #+INCLUDE: day-07/21-sarama/README.md.org
49 | #+INCLUDE: day-07/22-concurrency-patterns/README.md.org
50 |
--------------------------------------------------------------------------------
/TODO.org:
--------------------------------------------------------------------------------
1 | * Cosas que quedan por terminar
2 |
3 | ** Semana 1 [1/3]
4 |
5 | - [X] Dia 1. [10/10]
6 | - [X] 01-installation
7 | - [X] 02-hello-world
8 | - [X] 03-variables
9 | - [X] 04-types
10 | - [X] 05-functions
11 | - [X] 06-control-statments
12 | - [X] 07-arrays
13 | - [X] 08-maps
14 | - [X] 09-packages-modules
15 | - [X] 10-cmdline
16 | - [-] Dia 2. [4/5]
17 | - [X] 11-errors-defer-panic-recover
18 | - [X] 12-testing
19 | - [X] 13-structs-pointers
20 | - [X] 14-interfaces
21 | - [ ] 15-concurrency
22 | - [ ] Dia 3. [0/3]
23 | - [ ] 16-json-xml
24 | - [ ] 17-rest-api
25 | - [ ] 18-http-client
26 |
27 |
28 | ** Semana 2 [0/3]
29 |
30 | - [ ] Dia 6.
31 | - [ ] 19-dbaccess
32 | - [ ] 20-echo
33 | - [ ] Dia 7
34 | - [ ] 21-concurrency-patterns
35 | - [ ] 22-context
36 | - [ ] 23 cancellation
37 |
--------------------------------------------------------------------------------
/day-01/01-installation/README.md.org:
--------------------------------------------------------------------------------
1 |
2 | * Primeros pasos
3 | :PROPERTIES:
4 | :CUSTOM_ID: first-steps
5 | :END:
6 |
7 | ** Instalación
8 | :PROPERTIES:
9 | :CUSTOM_ID: install
10 | :END:
11 |
12 | 1. Descargar la versión para tu OS desde [[https://golang.org/dl]]
13 | (algunas distribuciones de Linux incluyen el paquete en sus
14 | repositorios).
15 |
16 | 2. Abrir el instalador y seguir las
17 | [[https://golang.org/doc/install][instrucciones]].
18 |
19 | #+begin_example
20 | (Unix| Linux): /usr/local/go (varía según el sistema)
21 | Windows: C:\Go
22 | #+end_example
23 |
24 | 3. Para chequear que la instalación está correcta escribir en la
25 | consola
26 |
27 | #+begin_example
28 | $ go version
29 | #+end_example
30 |
31 | ** Configuración inicial
32 | :PROPERTIES:
33 | :CUSTOM_ID: config-initial
34 | :END:
35 |
36 | 1. Activar variables de entorno para el trabajo con módulos
37 |
38 | #+begin_example
39 | $ go env -w GO111MODULE=on GOPROXY=https://goproxy.io,direct
40 | #+end_example
41 |
42 | 2. Para chequear que todas las variables de entorno están correctas
43 | escribir en la consola
44 |
45 | #+begin_example
46 | $ go env
47 | GOROOT = [Directorio de instalación]
48 | GOPROXY=https://goproxy.io,direct
49 | GO111MODULE=on
50 | #+end_example
51 |
52 | ** Completamiento en editores
53 | :PROPERTIES:
54 | :CUSTOM_ID: gpls
55 | :END:
56 |
57 | 1. Instalar la herramienta =gopls=.
58 |
59 | #+begin_example
60 | $ go get golang.org/x/tools/gopls@latest
61 | #+end_example
62 |
63 | ** Otras Herramientas
64 | :PROPERTIES:
65 | :CUSTOM_ID: otras-herramientas
66 | :END:
67 |
68 | - [[https://code.visualstudio.com/download][Visual Studio Code]]
69 | - [[https://code.visualstudio.com/docs/languages/go][Visual Studio Code Go extension]]
70 | - [[https://www.getpostman.com][Postman]]
71 | - [[https://www.mongodb.com][MongoDB]]
72 |
--------------------------------------------------------------------------------
/day-01/02-hello-world/README.md.org:
--------------------------------------------------------------------------------
1 | * Hello World
2 | :PROPERTIES:
3 | :CUSTOM_ID: hello-world
4 | :END:
5 | Vamos a programar el tradicional =Hello World=.
6 |
7 | ** Pasos
8 | :PROPERTIES:
9 | :CUSTOM_ID: steps
10 | :END:
11 |
12 | 1. Crear un directorio llamado =hello-world=.
13 | 2. Crear un archivo de texto llamado =main.go= dentro del directorio
14 | creado en el paso 1.
15 | 3. Agregar el siguiente código al archivo =main.go=
16 |
17 | #+begin_src go
18 | package main
19 |
20 | import "fmt"
21 |
22 | func main() {
23 | fmt.Println("Hello World")
24 | }
25 | #+end_src
26 |
27 | - Ejecutar el programa con el comando =go run main.go=
28 |
29 | ** ¿Qué sucede exactamente?
30 | :PROPERTIES:
31 | :CUSTOM_ID: deep-dive
32 | :END:
33 |
34 | En esta sección vamos a revisar cada línea del programa =Hello World=.
35 |
36 | *** Packages e imports
37 |
38 | Linea 1 => =package main=
39 |
40 | Los *packages* son la forma de agrupar funcionalidad común en Golang. El
41 | package default es =main= y usualmente significa que el archivo que lo
42 | contiene es el punto de entrada.
43 |
44 | Linea 3 => =import "fmt"=
45 |
46 | En esta línea especificamos que el package =fmt= (que se incluye como
47 | parte de Golang), es requerido para ejecutar este programa.
48 |
49 | *** Funciones
50 |
51 | Linea 5 => =func main() {=
52 |
53 | Las funciones en Golang se definen con el keyword =func=. En esta línea
54 | estamos definiendo una función llamada =main= sin argumentos. El código
55 | de la función se escribe entre ={ }=.
56 |
57 | Para crear un ejecutable en Golang tiene que existir una funcion llamada
58 | =main= dentro de un package también llamado =main=. Solo puede haber una
59 | funcion =main=, incluso si tenemos muchos archivos =.go= (solo puede
60 | existir un entrypoint por ejecutable)
61 |
62 | *** Bibliotecas
63 |
64 | Línea 6 => =fmt.Println("Hello World")=
65 |
66 | Esta línea imprime =Hello World= en la consola. =Println= es una función
67 | dentro del package =fmt= que recibe como argumento el string
68 | ="Hello World"=.
69 |
70 | La función =Println= hace dos cosas::
71 |
72 | - Imprime la entrada que el usuario específica en el argumento.
73 | - Agrega una nueva línea después de imprimir la entrada especificada.
74 |
--------------------------------------------------------------------------------
/day-01/02-hello-world/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | fmt.Println("Hello World")
7 | }
8 |
--------------------------------------------------------------------------------
/day-01/03-variables/README.md.org:
--------------------------------------------------------------------------------
1 | * Variables
2 | :PROPERTIES:
3 | :CUSTOM_ID: variables
4 | :END:
5 |
6 | ** Definiendo variables
7 |
8 | Una variable es un nombre que se le asigna a una porción de memoria para
9 | almacenar un valor de un determinado tipo.
10 |
11 | El keyword =var= se usa para declarar variables. La sintaxis es
12 | =var = seguido (opcionalmente) por un valor inicial.
13 |
14 | *** Ejemplos de variables
15 |
16 | #+begin_src go
17 | var a int // Inicializado por defecto
18 |
19 | var b = 10 // Se infiere el tipo
20 |
21 | var c, d = 20, 30 // Asignando valores múltiples
22 |
23 | e, f := 40, 50 // Crear y asignar
24 | #+end_src
25 |
26 | *** Valor inicial
27 |
28 | Si no se especifica un valor inicial Golang asigna uno por defecto
29 | dependiento del tipo de dato de la variable.
30 |
31 | Para imprimir el valor de una variable se puede usar =Println=.
32 |
33 | =fmt.Println("a = ", a)=
34 |
--------------------------------------------------------------------------------
/day-01/03-variables/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func init() {
6 | fmt.Println("Se ejecuta init")
7 | }
8 |
9 | func main() {
10 | var a int // Default initialization
11 | fmt.Println("a: ", a)
12 |
13 | var b = 10 // Automatic type inference
14 | fmt.Println("b: ", b)
15 |
16 | var c, d = 20, 30 // Multiple value assignment
17 | fmt.Println("c: ", c, " d: ", d)
18 |
19 | e, f := 40, 50 // Shorthand assignment
20 | fmt.Println("e: ", e, " f: ", f)
21 | }
22 |
--------------------------------------------------------------------------------
/day-01/04-types/README.md.org:
--------------------------------------------------------------------------------
1 | * Tipos
2 | :PROPERTIES:
3 | :CUSTOM_ID: tipos
4 | :END:
5 |
6 | ** Tipos simples
7 |
8 | Los siguientes tipos de datos están disponibles en Golang:
9 |
10 | - bool
11 | - Tipos numéricos:
12 |
13 | - int8, int16, int32, int64, int
14 | - uint8, uint16, uint32, uint64, uint
15 | - float32, float64
16 | - complex64, complex128
17 | - byte
18 | - rune
19 | - string
20 | - error
21 | - chan
22 |
23 | *** Booleanos
24 |
25 | =bool= representa valores boleanos =true= o =false=.
26 |
27 | *** Tipos numéricos
28 |
29 | Para tipos numéricos, el número que sigue al tipo indica la cantidad de
30 | bits que se usa para representar el valor en memoria. El número de bits
31 | determina qué tan grande el número puede ser.
32 |
33 | =int8=: 8 bit signed integers
34 |
35 | =int16=: 16 bit entero con signo
36 |
37 | =int32=: 32 bit entero con signo
38 |
39 | =int=: 32 or 64 bit entero con signo (dependiende de la plataforma).
40 |
41 | =uint= : 32 or 64 bit entero sin signo (dependiende de la plataforma).
42 |
43 | =float32=: 32 bit número flotante.
44 |
45 | =float64=: 64 bit número flotante.
46 |
47 | =byte= alias de =uint8=
48 |
49 | =rune= alias de =int32=
50 |
51 | *** Números complejos
52 |
53 | =complex64=: números complejos con parte real e imaginaria =float32=
54 |
55 | =complex128=: números complejos con parte real e imaginaria =float64=
56 |
57 | La función =complex= se utiliza para construir un número complejo con
58 | partes reales e imaginarias:
59 |
60 | #+begin_src go
61 | func complex(r, i FloatType) ComplexType
62 | #+end_src
63 |
64 | *** El tipo string
65 |
66 | =string= es una colección de caracteres encerrados entre comillas.
67 |
68 | #+begin_src go
69 | first := "Allen"
70 | last := "Varghese"
71 | name := first +" "+ last
72 | fmt.Println("My name is",name)
73 | #+end_src
74 |
75 | *** Errores y canales.
76 |
77 | =error= es un tipo especial para indicar condiciones de error.
78 |
79 | =chan= es un tipo especial que representa un canal de comunicación.
80 |
81 | ** Conversion de tipos
82 | :PROPERTIES:
83 | :CUSTOM_ID: conversion-de-tipos
84 | :END:
85 |
86 | No hay conversión automática de tipos en Golang. Se utilizan funciones
87 | de tipo para hacer una conversión.
88 |
89 | #+begin_src go
90 | int()
91 | float64()
92 | #+end_src
93 |
94 | ** Constantes
95 | :PROPERTIES:
96 | :CUSTOM_ID: constantes
97 | :END:
98 |
99 | Las constantes no cambian sus valores una vez asignadas. Se usa el keyword
100 | =const= en vez de =var= para declarar una constante.
101 |
102 | #+begin_src go
103 | const a bool = true
104 | const b int32 = 32890
105 | const c string = "Something"
106 | #+end_src
107 |
--------------------------------------------------------------------------------
/day-01/04-types/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "math"
6 | )
7 |
8 | func main() {
9 | var i int16 = 55 // int16
10 | var j float32 = 67.8 // float32
11 | sum := i + int16(j) // j is converted to int
12 | fmt.Println(sum)
13 |
14 | const aa int = 48
15 | const bb bool = true
16 | fmt.Println(aa, bb)
17 |
18 | // Type conversion
19 | var x, y int = 3, 4
20 | var f float64 = math.Sqrt(float64(x*x + y*y))
21 | var z uint = uint(f)
22 | fmt.Println(x, y, z)
23 | }
24 |
--------------------------------------------------------------------------------
/day-01/05-functions/README.md.org:
--------------------------------------------------------------------------------
1 | * Funciones
2 | :PROPERTIES:
3 | :CUSTOM_ID: funciones
4 | :END:
5 |
6 | ** Funciones en Golang
7 | :PROPERTIES:
8 | :CUSTOM_ID: golang-funcs
9 | :END:
10 |
11 | Una función es un grupo de instrucciones que se ejecutan todas juntas
12 | como un bloque. Una función puede o no tener argumentos de entrada, y
13 | retornar valores.
14 |
15 | En Golang una función se define con el keyword =func=. El siguiente es
16 | un ejemplo de una función que suma dos enteros:
17 |
18 | #+begin_src go
19 | func AddIntegers(a int, b int) int {
20 | return a + b
21 | }
22 | #+end_src
23 |
24 | ** Retornando valores
25 | :PROPERTIES:
26 | :CUSTOM_ID: golang-returns
27 | :END:
28 |
29 | El keyword =return= es usado para indicar qué valor va a retornar la
30 | función.
31 |
32 | Golang soporta que una función devuelva múltiples valores:
33 |
34 | #+begin_src go
35 | func SumDifference(a int, b int) (int, int) {
36 | return a + b, a - b
37 | }
38 | #+end_src
39 |
40 | ** Identificador en blanco
41 | :PROPERTIES:
42 | :CUSTOM_ID: blank-identifier
43 | :END:
44 |
45 | Se utiliza el *identificador en blanco* en el lugar de un valor que se
46 | quiere descartar al llamar a una función:
47 |
48 | #+begin_src go
49 | var _, diff = SumDifference(10, 20)
50 |
51 | fmt.Println("Difference is ", diff)
52 | #+end_src
53 |
54 | ** Valores de retorno con nombres
55 | :PROPERTIES:
56 | :CUSTOM_ID: named-return-values
57 | :END:
58 |
59 | Cuando se define una función se le puede asignar un nombre al tipo de
60 | dato de retorno para luego referenciarlo en el código de la función.
61 |
62 | #+begin_src go
63 | func Product(a int, b int) (prod int) {
64 | prod = a * b
65 | return
66 | }
67 | #+end_src
68 |
69 | Al asignar un valor de retorno con nombre no hace falta incluirlo en
70 | la sentencia =return=.
71 |
72 | ** Funciones anónimas y clausuras
73 | :PROPERTIES:
74 | :CUSTOM_ID: funciones-anónimas-y-clausuras
75 | :END:
76 |
77 | Golang soporta funciones anónimas y de segundo orden. Tomemos por
78 | ejemplo la función =sort.Slice=
79 |
80 | #+begin_src go
81 | func Slice(slice interface{}, less func(i, j int) bool)
82 | #+end_src
83 |
84 | El parámetro =less= describe una función que toma dos enteros y
85 | retorna un valor =bool=
86 |
87 | *** Funciones anónimas.
88 |
89 | Podemos asignar una función a una variable o definirla en el momento
90 | de su uso. Las funciones anónimas forman *clausuras*
91 |
92 | #+begin_src go
93 | c := []int{20, 11, 12, 1, 5}
94 | less := func(i int, j int) bool {
95 | return c[i] < c[j]
96 | }
97 | sort.Slice(c, less)
98 | fmt.Println("Despues del sort", c)
99 | //Output: [1 5 11 12 20]
100 | c := []int{20, 11, 12, 1, 5}
101 | sort.Slice(c, func(i int, j int) bool {
102 | return c[i] < c[j]
103 | })
104 | fmt.Println("Despues del sort", c)
105 | //Output: [1 5 11 12 20]
106 | #+end_src
107 |
--------------------------------------------------------------------------------
/day-01/05-functions/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "sort"
6 | )
7 |
8 | func main() {
9 | var sum = AddIntegers(10, 20)
10 | fmt.Println("Sum of 10 and 20 is", sum)
11 |
12 | var sum2, diff = SumDifference(10, 20)
13 | fmt.Println("Sum & Difference of 10 and 20 are (", sum2, ",", diff, ")")
14 |
15 | var _, diff1 = SumDifference(10, 20)
16 | fmt.Println("Difference of 10 and 20 is", diff1)
17 | c := []int{20, 11, 12, 1, 5}
18 | fmt.Println("Antes del sort", c)
19 | less := func(i int, j int) bool {
20 | return c[i] < c[j]
21 | }
22 | sort.Slice(c, less)
23 | fmt.Println("Despues del sort", c)
24 | }
25 |
26 | // AddIntegers adds two integers
27 | func AddIntegers(a int, b int) int {
28 | return a + b
29 | }
30 |
31 | // SumDifference gives the sum and difference of two numbers
32 | func SumDifference(a int, b int) (int, int) {
33 | return a + b, a - b
34 | }
35 |
--------------------------------------------------------------------------------
/day-01/06-control-statments/README.md.org:
--------------------------------------------------------------------------------
1 | * Instrucciones de control
2 | :PROPERTIES:
3 | :CUSTOM_ID: instrucciones-de-control
4 | :END:
5 |
6 | Golang soporta múltiples instrucciones de control e iteradores.
7 |
8 | ** =if=/=else=
9 | :PROPERTIES:
10 | :CUSTOM_ID: if---else-if---else
11 | :END:
12 |
13 | Se pueden tener muchas instrucciones =else if=. El bloque =else= es
14 | opcional:
15 |
16 | #+begin_src go
17 | if condition {
18 | // codigo a ejecutar
19 | } else if condition {
20 | // codigo a ejecutar
21 | } else {
22 | // codigo a ejecutar
23 | }
24 | #+end_src
25 |
26 | #+begin_quote
27 | El bloque =else= debe estar en la misma línea que la llave de cierre
28 | (=}=).
29 | #+end_quote
30 |
31 | *** Inicializar la condición.
32 |
33 | La instruccion =if= soporta indicar una instrucción opcional que se
34 | ejecuta antes de la condición:
35 |
36 | #+begin_src go
37 | if statement; condition {
38 | // código a ejecutar
39 | }
40 | #+end_src
41 |
42 | ** =for=
43 | :PROPERTIES:
44 | :CUSTOM_ID: for
45 | :END:
46 |
47 | Hay una sola instrucción de iteración en Golang, el iterador =for=.
48 |
49 | #+begin_src go
50 | for initialization; condition; post {
51 | // codigo a ejecutar
52 | }
53 | #+end_src
54 |
55 | Los tres componentes =initialization=, =condition= y =post= son
56 | opcionales en Golang.
57 |
58 | *** =for= en detalle
59 |
60 | 1. La inicialización se ejecuta una sola vez.
61 | 2. La condición se prueba antes de ejecutar el ciclo.
62 | 3. El post se ejecuta después de cada iteración del ciclo.
63 |
64 | *** Ejemplo de =for=
65 |
66 | El siguiente ejemplo imprime números del 1 al 10 usando un iterador
67 | =for=:
68 |
69 | #+begin_src go
70 | for i := 1; i <= 10; i = i + 1 {
71 | fmt.Println(i)
72 | }
73 | #+end_src
74 |
75 | ** =switch=
76 | :PROPERTIES:
77 | :CUSTOM_ID: switch
78 | :END:
79 |
80 | La instrucción de control =switch= evalúa una expresión y la compara
81 | contra una lista de posibles valores o expresiones que puedan coincidir.
82 | Es una forma abreviada de escribir muchas cláusulas =if else=.
83 |
84 | #+begin_src go
85 | switch expression {
86 | case expression or value | (, expression or value)*:
87 | // código a ejecutar
88 | (break | fallthrough)
89 | case expression or value | (, expression or value)*:
90 | // código a ejecutar
91 | default:
92 | // código a ejecutar
93 | }
94 | #+end_src
95 |
96 | *** =break=, =fallthrough= y =default=
97 |
98 | 1. =break= detiene el flujo y sale del =switch=
99 | 2. =fallthrough= continúa al próximo =case=.
100 | 2. Si no hay coincidencias, se ejecuta el código en el bloque
101 | =default=.
102 |
--------------------------------------------------------------------------------
/day-01/06-control-statments/for.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | fmt.Println("1 .. 10 =>")
7 | simple()
8 |
9 | fmt.Println("\n\n1 .. 7 =>")
10 | simpleBreak()
11 |
12 | fmt.Println("\n\nOdd numbers =>")
13 | simpleContinue()
14 |
15 | fmt.Println("\n\nNested loops =>")
16 | printTower()
17 | }
18 |
19 | func simple() {
20 | for i := 1; i <= 10; i = i + 1 {
21 | fmt.Print(i, " ")
22 | }
23 | }
24 |
25 | // simpleBreak will print numbers from 1 to 7
26 | func simpleBreak() {
27 | for i := 1; i <= 10; i = i + 1 {
28 | if i == 8 {
29 | break
30 | }
31 | fmt.Print(i, " ")
32 | }
33 | }
34 |
35 | // simpleContinue will print only odd numbers
36 | func simpleContinue() {
37 | for i := 1; i <= 10; i = i + 1 {
38 | if i%2 == 0 {
39 | continue
40 | }
41 | fmt.Print(i, " ")
42 | }
43 | }
44 |
45 | func printTower() {
46 | const ROWS = 5
47 | for i := 0; i < ROWS; i++ {
48 | for j := 0; j <= i; j++ {
49 | fmt.Print(" * ")
50 | }
51 | fmt.Println("")
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/day-01/06-control-statments/if.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | var aa = 10
7 | if aa == 10 {
8 | fmt.Println("The number is 10!")
9 | } else if aa%2 == 0 {
10 | fmt.Println("Even number!")
11 | } else {
12 | fmt.Println("Odd number")
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/day-01/06-control-statments/switch.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "runtime"
6 | )
7 |
8 | func main() {
9 | const finger = 3
10 | // const finger = 6 // Should go to the default section
11 |
12 | switch finger {
13 | case 1:
14 | fmt.Println("Thumb")
15 | case 2:
16 | fmt.Println("Index")
17 | case 3:
18 | fmt.Println("Middle")
19 | case 4:
20 | fmt.Println("Ring")
21 | case 5:
22 | fmt.Println("Pinky")
23 | default:
24 | fmt.Println("incorrect finger number")
25 | }
26 |
27 | fmt.Print("Go runs on ")
28 |
29 | switch os := runtime.GOOS; os {
30 | case "darwin":
31 | fmt.Println("OS X.")
32 | case "linux":
33 | fmt.Println("Linux.")
34 | default:
35 | fmt.Printf("%s.", os)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/day-01/07-arrays/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | copyArray()
7 | fmt.Println("")
8 | sumOfArray()
9 | fmt.Println("")
10 | printArray()
11 | fmt.Println("")
12 | printSlice()
13 | fmt.Println("")
14 | updateSlice()
15 | fmt.Println("")
16 | appendSlice()
17 | }
18 |
19 | func copyArray() {
20 | a := [...]string{"IRL", "IND", "US", "CAN"}
21 | b := a
22 |
23 | b[1] = "CHN"
24 |
25 | fmt.Println("Original :", a)
26 | fmt.Println("Copy :", b)
27 | }
28 |
29 | func sumOfArray() {
30 | a := [...]int{1, 2, 3, 4, 5}
31 | sum := 0
32 | for i, v := range a {
33 | fmt.Println("Index:", i, " Value is:", v)
34 | sum += v
35 | }
36 | fmt.Println("Sum:", sum)
37 | }
38 |
39 | func printArray() {
40 | a := [3][2]string{
41 | {"lion", "tiger"},
42 | {"cat", "dog"},
43 | {"pigeon", "peacock"},
44 | }
45 | for _, v1 := range a {
46 | for _, v2 := range v1 {
47 | fmt.Printf("%s ", v2)
48 | }
49 | fmt.Println("")
50 | }
51 | }
52 |
53 | func printSlice() {
54 | a := [...]int{1, 2, 3, 4, 5}
55 | sa := a[1:4]
56 | fmt.Println(sa)
57 | }
58 |
59 | func updateSlice() {
60 | a := [...]int{1, 2, 3, 4, 5}
61 | sa := a[1:4]
62 |
63 | fmt.Println("Before:", a)
64 | sa[0] = 22
65 |
66 | fmt.Println("After:", a)
67 | }
68 |
69 | func appendSlice() {
70 | sa := []int{1, 2, 3}
71 | newSa := append([]int{}, sa...)
72 |
73 | fmt.Println(newSa)
74 | }
75 |
--------------------------------------------------------------------------------
/day-01/08-maps/README.md.org:
--------------------------------------------------------------------------------
1 | * Maps
2 | :PROPERTIES:
3 | :CUSTOM_ID: maps
4 | :END:
5 |
6 | ** Tipos asociativos, =map=
7 | :PROPERTIES:
8 | :CUSTOM_ID: maps-inicial
9 | :END:
10 |
11 | Un =map= es un tipo de dato incluido en Golang que asocia una clave con un
12 | valor. El valor puede ser recuperado a partir de la clave.
13 |
14 | Un =map= vacío se crea con la función =make=:
15 |
16 | #+begin_src go
17 | make(map[type of key]type of value)
18 |
19 | eg: make(map[string]int)
20 | #+end_src
21 |
22 | ** Agregando valores
23 | :PROPERTIES:
24 | :CUSTOM_ID: agregando-valores
25 | :END:
26 |
27 | Los valores en un =map= se referencian igual que en un arreglo. Un valor
28 | se agrega a un =map= asignándole una clave, si la clave ya existe el valor
29 | para esa clave se sobrescribe.
30 |
31 | #+begin_src go
32 | m := make(map[string]int)
33 | m["a"] = 1
34 | m["b"] = 2
35 |
36 | fmt.Println(m)
37 |
38 | // Output: map[a:1 b:2]
39 | #+end_src
40 |
41 | *** Inicializando un =map=
42 |
43 | Un =map= se puede inicializar con valores en su declaración igual que un
44 | arreglo:
45 |
46 | #+begin_src go
47 | m := map[string]int {
48 | "a": 1,
49 | "b": 2,
50 | }
51 | #+end_src
52 |
53 | ** Obteniendo valores
54 | :PROPERTIES:
55 | :CUSTOM_ID: retrieving-values
56 | :END:
57 |
58 | Una manera de chequear si una clave existe es la siguiente:
59 |
60 | #+begin_src go
61 | data, ok := m["some key"]
62 | #+end_src
63 |
64 | Si =ok= es =true=, la clave existe y la variable =data= contendrá la
65 | información recuperada, si =ok= es =false= la clave no existe.
66 |
67 | Acceder a una clave inexistente en un =map= causa un =panic=.
68 |
69 | ** Iteraciones
70 | :PROPERTIES:
71 | :CUSTOM_ID: iteraciones
72 | :END:
73 |
74 | El iterador =for= se utiliza para recorrer las claves y valores de un
75 | =map=
76 |
77 | #+begin_src go
78 | m := map[string]int {
79 | "a": 1,
80 | "b": 2,
81 | "c": 3,
82 | }
83 | for key, value := range m {
84 | fmt.Println("Key:", key, " Value:", value)
85 | }
86 | // Output: Key: a Value 1
87 | // Key: b Value 2
88 | // Key: c Value 3
89 | #+end_src
90 |
91 | #+REVEAL: split
92 |
93 | Se puede utilizar el identificador en blanco (=_=) para descartar
94 | cualquiera de los elementos del par.
95 |
96 | #+begin_quote
97 | El orden en que se recorre el =map= no esta garantizado, y cada ejecución puede tener un order diferente.
98 | #+end_quote
99 |
100 | ** Eliminacion
101 | :PROPERTIES:
102 | :CUSTOM_ID: eliminacion
103 | :END:
104 |
105 | Los valores se eliminan de un =map= con la función =delete=. La función no
106 | retorna nada. La sintaxis es la siguiente:
107 |
108 | #+begin_src go
109 | delete(m, key)
110 | #+end_src
111 |
112 | ** Tamaño
113 | :PROPERTIES:
114 | :CUSTOM_ID: tamaño
115 | :END:
116 |
117 | El tamaño de un =map= es retornado por la funcion =len=. Esta funcion
118 | retorna la cantidad de claves que hay en un map. La sintaxis es la
119 | siguiente:
120 |
121 | #+begin_src go
122 | len(m)
123 | #+end_src
124 |
125 | ** Tipo
126 | :PROPERTIES:
127 | :CUSTOM_ID: tipo
128 | :END:
129 |
130 | Un =map= es un /tipo referencia/. Por lo tanto si un =map= se asigna a
131 | otra variable, ambas variable apuntan a los mismos pares (clave,
132 | valor). Lo mismo sucede si se pasa como argumento en una funcion.
133 |
--------------------------------------------------------------------------------
/day-01/08-maps/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | m := map[string]int{
7 | "a": 1,
8 | "b": 2,
9 | "c": 3,
10 | }
11 | for key, value := range m {
12 | fmt.Println("Key:", key, " Value:", value)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/day-01/09-packages-modules/geometry/square.go:
--------------------------------------------------------------------------------
1 | package geometry
2 |
3 | import "fmt"
4 |
5 | func init() {
6 | fmt.Println("This is geometry.init")
7 | }
8 |
9 | type square struct {
10 | a int
11 | b int
12 | }
13 |
14 | func NewSquare(a, b int) *square {
15 | return &square{a, b}
16 | }
17 |
18 | // Area of a square
19 | func (s square) Area() int {
20 | return s.a * s.b
21 | }
22 |
23 | // Perimeter of a square
24 | func (s square) Perimeter() int {
25 | return 2 * (s.a + s.b)
26 | }
27 |
--------------------------------------------------------------------------------
/day-01/09-packages-modules/go.mod:
--------------------------------------------------------------------------------
1 | module sample10
2 |
3 | go 1.15
4 |
--------------------------------------------------------------------------------
/day-01/09-packages-modules/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "sample10/geometry"
6 | )
7 |
8 | func main() {
9 | var length, breadth = 10, 20
10 | s := geometry.NewSquare(length, breadth)
11 | fmt.Println("Area is", s.Area())
12 | fmt.Println("Perimeter is", s.Perimeter())
13 | }
14 |
--------------------------------------------------------------------------------
/day-01/10-cmdline/README.md.org:
--------------------------------------------------------------------------------
1 | * Línea de comandos
2 | :PROPERTIES:
3 | :CUSTOM_ID: linea-de-comandos
4 | :END:
5 |
6 | ** Argumentos
7 | :PROPERTIES:
8 | :CUSTOM_ID: argumentos
9 | :END:
10 |
11 | Interactuar con la línea de comandos es muy útil y muy común al ejecutar
12 | scripts y programas que no tienen GUI. El paquete =os= se usa para tener
13 | acceso a los argumentos de la línea de comandos al ejecutar un programa
14 | de Golang.
15 |
16 | #+REVEAL: split
17 |
18 | Los argumentos de la línea de comandos están disponibles en el arreglo
19 | =os.Args=:
20 |
21 | #+begin_src go
22 | import "os"
23 |
24 | os.Args // Lista completa incluyendo el nombre del programa
25 | os.Args[1:] // Solo los argumentos
26 | #+end_src
27 |
28 | ** Flags
29 | :PROPERTIES:
30 | :CUSTOM_ID: flags
31 | :END:
32 |
33 | Especificar los argumentos como valores separados por espacios en la
34 | terminal es muy básico y difícil de extender. El uso de =flags= provee
35 | mas flexibilidad en cómo los argumentos se especifican, sobre todo los
36 | opcionales. Golang cuenta con un paquete llamado =flag= para
37 | soportarlos.
38 |
39 | #+begin_src go
40 | import "flag"
41 |
42 | flag.(nombre del flag, valor default, descripcion)
43 | #+end_src
44 |
45 | #+REVEAL: split
46 |
47 | Un flag se define con la sintaxis anterior. El == puede ser
48 | =string=, =int= o =bool=.
49 |
50 | Un flag opcional se puede indicar controlando si el valor es el mismo
51 | que el valor especificado por defecto.
52 |
53 | Un flag se puede leer independientemente de la posición en que el
54 | usuario los hubiera especificado.
55 |
56 | ** Environment Variables
57 | :PROPERTIES:
58 | :CUSTOM_ID: environment-variables
59 | :END:
60 |
61 | Las variables de entorno se usan para configurar aspectos de sistema en
62 | la mayoría de los *NIX y también en Windows. Son pares de clave-valor
63 | usualmente disponibles para todos los programas que corren en la
64 | terminal/shell. Se pueden definir variables de entorno custom solo
65 | disponibles durante la ejecución de un script de shell.
66 |
67 | #+REVEAL: split
68 |
69 | El paquete =os= proporciona dos funciones, =Setenv= y =Getenv=, para
70 | escribir y leer variables de entorno. Un string vacío se retorna si la
71 | clave no existe.
72 |
73 | #+begin_src go
74 | import "os"
75 |
76 | os.Setenv("FOO", "1")
77 | fmt.Println("FOO:", os.Getenv("FOO"))
78 | fmt.Println("BAR:", os.Getenv("BAR"))
79 |
80 | //Output: FOO: 1
81 | // BAR:
82 | #+end_src
83 |
84 | #+REVEAL: split
85 |
86 | La función =Environ= retorna la lista completa de todas las variables de
87 | entorno existentes.
88 |
89 | #+begin_src go
90 | import "strings"
91 |
92 | for _, e := range os.Environ() {
93 | pair := strings.Split(e, "=")
94 | fmt.Println(pair[0])
95 | }
96 | #+end_src
97 |
--------------------------------------------------------------------------------
/day-01/10-cmdline/go.mod:
--------------------------------------------------------------------------------
1 | module sample11
2 |
3 | go 1.15
4 |
--------------------------------------------------------------------------------
/day-01/10-cmdline/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 | "strings"
8 | )
9 |
10 | func main() {
11 | // Run only one of the below functions
12 | cmdArgs()
13 | cmdFlags()
14 | cmdEnv()
15 | }
16 |
17 | func cmdArgs() {
18 | argsWithProg := os.Args
19 | argsWithoutProg := os.Args[1:]
20 |
21 | arg := os.Args[3]
22 | fmt.Println("With program name: ", argsWithProg)
23 | fmt.Println("Just args:", argsWithoutProg)
24 | fmt.Println("3rd Argument:", arg)
25 | }
26 |
27 | func cmdFlags() {
28 | wordPtr := flag.String("word", "foo", "a string")
29 | numbPtr := flag.Int("numb", 42, "an int")
30 | boolPtr := flag.Bool("fork", false, "a bool")
31 | flag.Parse()
32 |
33 | fmt.Println("word:", *wordPtr)
34 | fmt.Println("numb:", *numbPtr)
35 | fmt.Println("fork:", *boolPtr)
36 | fmt.Println("tail:", flag.Args())
37 | }
38 |
39 | func cmdEnv() {
40 | os.Setenv("FOO", "1")
41 | fmt.Println("FOO:", os.Getenv("FOO"))
42 | fmt.Println("BAR:", os.Getenv("BAR"))
43 |
44 | for _, e := range os.Environ() {
45 | pair := strings.Split(e, "=")
46 | fmt.Println(pair[0], "->", pair[1])
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/day-02/11-errors-defer-panic-recover/README.md.org:
--------------------------------------------------------------------------------
1 | * Manejo de errores: error, defer, recover, panic
2 | :PROPERTIES:
3 | :CUSTOM_ID: manejo-de-errores-error-defer-recover-panic
4 | :END:
5 |
6 | ** Manejo de errores en Golang
7 | :PROPERTIES:
8 | :CUSTOM_ID: manejo-de-errores-en-golang
9 | :END:
10 |
11 | En lugar de excepciones como Python, Java o C#, Golang sigue un enfoque
12 | más cercano a los lenguajes funcionales donde el estado de error es
13 | representado por un tipo de datos,
14 |
15 | #+begin_src go
16 | var DivisionByZero = errors.New("División por cero")
17 |
18 | func safedivide(a int, b int) (int, error) {
19 | if b == 0 {
20 | return 0, DivisionByZero
21 | }
22 | return a / b, nil
23 | }
24 | #+end_src
25 |
26 | ** El tipo error
27 | :PROPERTIES:
28 | :CUSTOM_ID: error-type
29 | :END:
30 |
31 | El tipo =error= es una interfaz /built-in/ del lenguaje
32 |
33 | #+begin_src go
34 | type error interface {
35 | Error() string
36 | }
37 | #+end_src
38 |
39 | *** Errores personalizados
40 |
41 | Esto nos permite crear nuestros propios tipos de errores
42 |
43 | #+begin_src go
44 | type MyError struct {
45 | message: string
46 | status: int
47 | }
48 |
49 | func (m *MyError) Error() string {
50 | return fmt.Sprintf("Error - %s. Status %d", m.message, m.status)
51 | }
52 |
53 | func returnMyError() error {
54 | return &MyError{
55 | message: "None",
56 | status : -1,
57 | }
58 | }
59 | #+end_src
60 |
61 | *** Operando con errores
62 |
63 | El paquete =errors= contiene funciones dedicadas a manejar tipos de
64 | error. Podemos usar la función =errors.Is= para verificar si un error
65 | es de un tipo específico.
66 |
67 | #+begin_src go
68 | if _, err := os.Open("non-existing"); err != nil {
69 | if errors.Is(err, os.ErrNotExist) {
70 | fmt.Println("file does not exist")
71 | } else {
72 | fmt.Println(err)
73 | }
74 | }
75 | #+end_src
76 |
77 | ** Defer
78 | :PROPERTIES:
79 | :CUSTOM_ID: defer
80 | :END:
81 |
82 | La sentencia =defer= se utiliza para indicar que la función a
83 | continuación se debe ejecutar a la salida de la función actual
84 |
85 | Es muy usual en Golang usar =defer= para destruir o liberar cualquier
86 | tipo de recurso temporal que se obtenga en la función.
87 |
88 | #+begin_src go
89 | func doSomething() {
90 | a := getExternalResource();
91 | defer a.Release()
92 | /// Resto de la función
93 | }
94 | #+end_src
95 |
96 | =defer= se ejecuta sin importar las causas por las que la función
97 | actual haya terminado, lo que garantiza en el ejemplo anterior que
98 | =a.Release()= siempre se ejecute.
99 |
100 | ** Panic y recover
101 | :PROPERTIES:
102 | :CUSTOM_ID: panic-y-recover
103 | :END:
104 |
105 | Golang incluye una función especial =panic= para indicar un error que no
106 | puede ser manejado de forma correcta.
107 |
108 | La contraparte de =panic= es la función =recover=, que verifica si
109 | ocurrió una llamada a =panic= en el contexto de la función actual.
110 |
111 | Si una llamada a =panic= no es seguida por un =recover=, la función
112 | termina y el contexto pasa al invocador. Esto continúa hasta que se
113 | encuentre un recover o se llegue a la función =main= en cuyo caso el
114 | programa se detendrá con un mensaje de error.
115 |
116 | #+REVEAL: split
117 |
118 | #+begin_src go
119 | func f() {
120 | defer func() {
121 | if r := recover(); r != nil {
122 | fmt.Println("Recovered in f", r)
123 | }
124 | }()
125 | fmt.Println("Calling g.")
126 | g(0)
127 | fmt.Println("Returned normally from g.")
128 | }
129 |
130 | func g(i int) {
131 | if i > 3 {
132 | fmt.Println("Panicking!")
133 | panic(fmt.Sprintf("%v", i))
134 | }
135 | defer fmt.Println("Defer in g", i)
136 | fmt.Println("Printing in g", i)
137 | g(i + 1)
138 | }
139 | #+end_src
140 |
141 | ** Panic y recover no son try y catch
142 | :PROPERTIES:
143 | :CUSTOM_ID: panic-y-recover-no-son-try-y-catch
144 | :END:
145 |
146 | Uno de los errores más comunes para los que se inician en Golang es
147 | pensar en =panic= y =recover= como una alternativa a los bloques
148 | =try-catch= que aparecen en otros lenguajes. Esto es considerado una
149 | mala práctica.
150 |
151 | La función =panic= se debe utilizar solo para indicar estados en el
152 | flujo de una aplicación para los cuales no hay solución efectiva. Por
153 | otro lado =recover= debería utilizarse para liberar o destruir recursos
154 | adquiridos por la aplicación antes de hacer una salida forzosa.
155 |
--------------------------------------------------------------------------------
/day-02/11-errors-defer-panic-recover/defer-recover/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | f()
7 | fmt.Println("Returned normally from f.")
8 | }
9 |
10 | func f() {
11 | defer func() {
12 | if r := recover(); r != nil {
13 | fmt.Println("Recovered in f", r)
14 | }
15 | }()
16 | fmt.Println("Calling g.")
17 | g(0)
18 | fmt.Println("Returned normally from g.")
19 | }
20 |
21 | func g(i int) {
22 | if i > 3 {
23 | fmt.Println("Panicking!")
24 | panic(fmt.Sprintf("%v", i))
25 | }
26 | defer fmt.Println("Defer in g", i)
27 | fmt.Println("Printing in g", i)
28 | g(i + 1)
29 | }
30 |
--------------------------------------------------------------------------------
/day-02/11-errors-defer-panic-recover/errors/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | )
7 |
8 | var DivisionByZero = errors.New("Division por cero")
9 |
10 | func safedivide(a int, b int) (int, error) {
11 | if b == 0 {
12 | return 0, DivisionByZero
13 | }
14 | return a / b, nil
15 | }
16 |
17 | func main() {
18 | a, b := 1, 5
19 | c, err := safedivide(a, b)
20 | if err != nil {
21 | fmt.Println(err)
22 | }
23 | fmt.Printf("%d entre %d es %d \n", a, b, c)
24 | a, b = 10, 0
25 | c, err = safedivide(a, b)
26 | if err != nil {
27 | fmt.Println(err)
28 | }
29 | fmt.Printf("%d entre %d es %d \n", a, b, c)
30 | }
31 |
--------------------------------------------------------------------------------
/day-02/12-testing/README.md.org:
--------------------------------------------------------------------------------
1 | * Pruebas unitarias
2 | :PROPERTIES:
3 | :CUSTOM_ID: pruebas-unitarias
4 | :END:
5 |
6 | Golang incluye en el paquete [[https://pkg.go.dev/testing][testing]] las herramientas necesarias para
7 | hacer pruebas unitarias. Por convención las pruebas unitarias se
8 | incluyen en el mismo paquete de la funcionalidad a probar, adicionando
9 | a los archivos el sufijo =_test=.
10 |
11 | Además de pruebas unitarias, Golang nos permite escribir pruebas de
12 | rendimiento o /benchmarks/ y ejemplos de prueba.
13 |
14 | ** Nuestra primera prueba unitaria.
15 | :PROPERTIES:
16 | :CUSTOM_ID: nuestra-primera-prueba-unitaria.
17 | :END:
18 |
19 | Si tenemos una función para calcular los números de fibonacci.
20 |
21 | #+begin_src go
22 | func Fibonacci(n int) int {
23 | if n < 2 {
24 | return n
25 | }
26 | return Fibonacci(n-1) + Fibonacci(n-2)
27 | }
28 | #+end_src
29 |
30 | *** Pruebas unitarias
31 |
32 | #+begin_src go
33 | func TestFibonacci(t *testing.T) {
34 | var in int = 10
35 | var want int = 55
36 |
37 | got := Fibonacci(in)
38 | if got != want {
39 | t.Errorf("Fibonacci(%d) == %d, want %d", in, got, want)
40 | }
41 | }
42 | #+end_src
43 |
44 | *** Pruebas de ejemplo
45 |
46 |
47 | Los ejemplos además de ejecutar pruebas, son una forma de documentar el
48 | uso de funcionalidades.
49 |
50 | #+begin_src go
51 | func ExampleFibonacci() {
52 | fmt.Println(Fibonacci(10))
53 | // Output: 55
54 | }
55 | #+end_src
56 |
57 | #+REVEAL: split
58 |
59 | Tanto las pruebas unitarias como los ejemplos se ejecutan utilizando el
60 | comando =go test=.
61 |
62 | ** Benchmarks
63 | :PROPERTIES:
64 | :CUSTOM_ID: benchmarks
65 | :END:
66 |
67 | Un /benchmark/ o prueba de rendimiento nos permite examinar el desempeño
68 | de nuestras funcionalidades.
69 |
70 | #+begin_src go
71 | func BenchmarkFibonacci(b *testing.B) {
72 | for n := 0; n < b.N; n++ {
73 | Fibonacci(10)
74 | }
75 | }
76 | #+end_src
77 |
78 | ** Datos de pruebas o fixtures
79 | :PROPERTIES:
80 | :CUSTOM_ID: datos-de-pruebas-o-fixtures
81 | :END:
82 |
83 | Por convención, Golang ignora cualquier directorio que empiece con =.=,
84 | =_= o se llame =testdata=. Dado que las pruebas siempre se ejecutan en
85 | el directorio donde se encuentra el archivo =_test= podemos usar la
86 | función =Join= del paquete =path/filepath= para acceder a los datos de
87 | prueba.
88 |
89 | #+REVEAL: split
90 |
91 | #+begin_src go
92 | func TestBytesInFile(t *testing.T) {
93 | l, err := BytesInFile(filepath.Join("testdata", "hello_world"))
94 | if err != nil {
95 | t.Errorf("Error %s", err)
96 | t.FailNow()
97 | }
98 | if l != 12 { // New line at end of file
99 | t.Errorf("Got wrong number of bytes %d", l)
100 | }
101 | }
102 | #+end_src
103 |
--------------------------------------------------------------------------------
/day-02/12-testing/functions.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import (
4 | "io/ioutil"
5 | "os"
6 | )
7 |
8 | // Calculate a Fibonacci number
9 | func Fibonacci(n int) int {
10 | if n < 2 {
11 | return n
12 | }
13 | return Fibonacci(n-1) + Fibonacci(n-2)
14 | }
15 |
16 | func BytesInFile(filename string) (int, error) {
17 | f, err := os.Open(filename)
18 | defer f.Close()
19 | if err != nil {
20 | return 0, err
21 | }
22 | s, err := ioutil.ReadAll(f)
23 | if err != nil {
24 | return 0, err
25 | }
26 | return len(s), nil
27 | }
28 |
--------------------------------------------------------------------------------
/day-02/12-testing/functions_test.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import (
4 | "fmt"
5 | "path/filepath"
6 | "testing"
7 | )
8 |
9 | func TestFibonacci(t *testing.T) {
10 | var in int = 10
11 | var want int = 55
12 |
13 | got := Fibonacci(in)
14 | if got != want {
15 | t.Errorf("Fibonacci(%d) == %d, want %d", in, got, want)
16 | }
17 | }
18 |
19 | func BenchmarkFibonacci(b *testing.B) {
20 | for n := 0; n < b.N; n++ {
21 | Fibonacci(10)
22 | }
23 | }
24 |
25 | func ExampleFibonacci() {
26 | fmt.Println(Fibonacci(10))
27 | // Output: 55
28 | }
29 |
30 | func TestBytesInFile(t *testing.T) {
31 | l, err := BytesInFile(filepath.Join("testdata", "hello_world"))
32 | if err != nil {
33 | t.Errorf("Error %s", err)
34 | t.FailNow()
35 | }
36 | if l != 12 { // New line at end of file
37 | t.Errorf("Got wrong number of bytes %d", l)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/day-02/12-testing/go.mod:
--------------------------------------------------------------------------------
1 | module sample13
2 |
3 | go 1.15
4 |
--------------------------------------------------------------------------------
/day-02/12-testing/testdata/hello_world:
--------------------------------------------------------------------------------
1 | Hola Mundo
2 |
--------------------------------------------------------------------------------
/day-02/13-structs-pointers/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | // Address of an employee
6 | type Address struct {
7 | city, state string
8 | }
9 |
10 | func (a *Address) Print() string {
11 | return fmt.Sprintf("%s %s", a.city, a.state)
12 | }
13 |
14 | // Employee refers to an employee record
15 | type Employee struct {
16 | firstName string "el nombre"
17 | lastName string "el apellido"
18 | age int "la edad"
19 | Address "dirección embebida"
20 | }
21 |
22 | // Print will display a formatted employee record
23 | func (e *Employee) Print() {
24 | fmt.Println("Employee Record:")
25 | fmt.Println("Name:", e.firstName, e.lastName)
26 | // Call "inherited" method with same name
27 | fmt.Println("Address:", e.Address.Print())
28 | }
29 |
30 | // updateAge will change age to the new value
31 | func (e *Employee) updateAge(newAge int) {
32 | e.age = newAge
33 | }
34 |
35 | func main() {
36 | emp := Employee{
37 | firstName: "Peter",
38 | lastName: "Parker",
39 | age: 22,
40 | Address: Address{
41 | city: "New York",
42 | state: "New York",
43 | },
44 | }
45 | fmt.Println(emp)
46 | fmt.Println("City:", emp.city) //Printing nested field
47 |
48 | // Using struct method
49 | emp.Print()
50 |
51 | // Using a pointer receiver
52 | fmt.Println("Before:", emp.age)
53 | emp.updateAge(34)
54 | fmt.Println("After:", emp.age)
55 | }
56 |
--------------------------------------------------------------------------------
/day-02/14-interfaces/README.md.org:
--------------------------------------------------------------------------------
1 | * Interfaces
2 | :PROPERTIES:
3 | :CUSTOM_ID: interfaces
4 | :END:
5 |
6 | ** Interfaces en Golang
7 | :PROPERTIES:
8 | :CUSTOM_ID: interfaces-en-golang
9 | :END:
10 |
11 | En Golang, una interfaz es un conjunto de firmas de métodos. Si un
12 | tipo tiene una definición para esos métodos, se dice que implementa la
13 | interfaz. A diferencia de otros lenguajes, /la asociación de un tipo
14 | con una interfaz es implicita/.
15 |
16 | Un tipo puede implementar más de una interfaz.
17 |
18 | #+begin_src go
19 | type Permanent struct {
20 | empID int
21 | basicPay int
22 | pension int
23 | }
24 |
25 | type Contract struct {
26 | empID int
27 | basicPay int
28 | }
29 |
30 | func (p Permanent) calculateSalary() int {
31 | return p.basicPay + p.pension
32 | }
33 |
34 | func (p Contract) calculateSalary() int {
35 | return p.basicPay
36 | }
37 |
38 | type SalaryCalculator interface {
39 | calculateSalary() int
40 | }
41 |
42 | // In main
43 | sc := [...]SalaryCalculator{Permanent{1, 5000, 50}, Contract{3, 7000}}
44 | totalPayout := 0
45 | for _, v := range sc {
46 | totalPayout += v.calculateSalary()
47 | }
48 | fmt.Println(totalPayout)
49 | #+end_src
50 |
51 | ** Interfaces anidadas.
52 | :PROPERTIES:
53 | :CUSTOM_ID: interfaces-anidadas.
54 | :END:
55 |
56 | Las interfaces al igual que las estructuras pueden anidarse.
57 |
58 | #+begin_src go
59 | type Reader interface {
60 | Read(p []byte) (n int, err error)
61 | }
62 |
63 | type Seeker interface {
64 | Seek(offset int64, whence int) (int64, error)
65 | }
66 |
67 | type ReadSeeker interface {
68 | Reader
69 | Seeker
70 | }
71 | #+end_src
72 |
73 | Para que un tipo implemente la interfaz =ReadSeeker= tiene que
74 | implementar =Read= y =Seeker= a la vez.
75 |
76 | ** Interfaces y composición.
77 | :PROPERTIES:
78 | :CUSTOM_ID: interfaces-y-composición.
79 | :END:
80 |
81 | De la misma forma que una estructura "hereda" los métodos de otra en
82 | la composición, implementa las interfaces de aquellas que la componen.
83 |
84 | #+begin_src go
85 |
86 | type Animal interface {
87 | Name() string
88 | }
89 |
90 | type Dog struct{}
91 |
92 | func (d *Dog) Name() string {
93 | return "Dog"
94 | }
95 |
96 | func Bark(d *Dog) {
97 | fmt.Println("Woof!")
98 | }
99 |
100 | type GuideDog struct {
101 | *Dog
102 | }
103 | #+end_src
104 |
105 | En el ejemplo anterior el tipo =GuidedDog= implementa la interfaz
106 | =Animal=
107 |
108 | ** La interfaz vacía
109 | :PROPERTIES:
110 | :CUSTOM_ID: la-interfaz-vacía
111 | :END:
112 |
113 | El tipo =interface{}= representa un valor comodín al estilo del tipo
114 | =object= de C# o =void *= en C (técnicamente todos los tipos
115 | implementan una interfaz sin métodos)
116 |
117 | #+begin_src go
118 | func describe(i interface{}) {
119 | fmt.Printf("(%v, %T)\n", i, i)
120 | }
121 | #+end_src
122 |
123 | ** Type Assertions.
124 | :PROPERTIES:
125 | :CUSTOM_ID: type-assertions
126 | :END:
127 |
128 | A veces nos es necesario saber de qué tipo es el valor guardado en la
129 | interfaz.
130 |
131 | #+begin_src go
132 | var i interface{} = "hello"
133 | s := i.(string)
134 | fmt.Println(s)
135 |
136 | s, ok := i.(string)
137 | fmt.Println(s, ok)
138 |
139 | f, ok := i.(float64)
140 | fmt.Println(f, ok)
141 |
142 | f = i.(float64) // panic
143 | fmt.Println(f)
144 |
145 | #+end_src
146 |
147 | La sintaxis =variable.(Tipo)= es un *type assertion*, concepto muy
148 | parecido al *type casting* de otros lenguajes.
149 |
150 | *** Type assertions seguras e inseguras
151 |
152 | Los type assertions se ejecutan con alguna de las siguientes variantes
153 |
154 | #+begin_src go
155 | // variante segura
156 | v, ok := i.(Tipo)
157 | // variante insegura
158 | v := i.(Tipo)
159 | #+end_src
160 |
161 | La variante segura retorna un par =(Tipo,bool)= donde el segundo
162 | valor representa el estado de la operación. Un estado de =true=
163 | significa que se pudo efectuar la conversión, =false= que la
164 | conversión no es posible y el primer valor de la tupla estará con el
165 | valor nulo.
166 |
167 | En la variante insegura si no se puede efectuar la conversión el
168 | /runtime/ de Golang lanzará un =panic=.
169 |
170 | ** Type switches
171 | :PROPERTIES:
172 | :CUSTOM_ID: type-switches
173 | :END:
174 |
175 | Los *type switches* son una construcción especial que nos permite
176 | determinar el tipo de una variable y actuar en consecuencia
177 |
178 | #+begin_src go
179 | switch v := v.(type) {
180 | case string:
181 | fmt.Printf("%v is a string\n", v)
182 | case int:
183 | fmt.Printf("%v is an int\n", v)
184 | default:
185 | fmt.Printf("The type of v is unknown\n")
186 | }
187 | #+end_src
188 |
189 | En lugar de usar la sintaxis =v.(Tipo)= para la conversión se utiliza
190 | =v.(type)=.
191 |
--------------------------------------------------------------------------------
/day-02/14-interfaces/empty-interface.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | var i interface{}
7 | describe(i)
8 |
9 | i = 42
10 | describe(i)
11 |
12 | i = "hello"
13 | describe(i)
14 | }
15 |
16 | func describe(i interface{}) {
17 | fmt.Printf("(%v, %T)\n", i, i)
18 | }
19 |
--------------------------------------------------------------------------------
/day-02/14-interfaces/interface-values.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "math"
6 | )
7 |
8 | type I interface {
9 | M()
10 | }
11 |
12 | type T struct {
13 | S string
14 | }
15 |
16 | func (t *T) M() {
17 | fmt.Println(t.S)
18 | }
19 |
20 | type F float64
21 |
22 | func (f F) M() {
23 | fmt.Println(f)
24 | }
25 |
26 | func main() {
27 | var i I
28 |
29 | i = &T{"Hello"}
30 | describe(i)
31 | i.M()
32 |
33 | i = F(math.Pi)
34 | describe(i)
35 | i.M()
36 | }
37 |
38 | func describe(i I) {
39 | fmt.Printf("(%v, %T)\n", i, i)
40 | }
41 |
--------------------------------------------------------------------------------
/day-02/14-interfaces/interfaces-struct-composition.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type Human struct{}
6 |
7 | type Animal interface {
8 | Name() string
9 | }
10 |
11 | type Dog struct{}
12 |
13 | func (d *Dog) Name() string {
14 | return "Dog"
15 | }
16 |
17 | func Bark(d *Dog) {
18 | fmt.Println("Woof!")
19 | }
20 |
21 | type GuideDog struct {
22 | *Dog
23 | }
24 |
25 | func (gd *GuideDog) Help(h *Human) {
26 | fmt.Printf("Hey human, grab %s’s leash!\n", gd.Name())
27 | }
28 |
29 | type Cat struct{}
30 |
31 | func (c *Cat) Name() string {
32 | return "Cat"
33 | }
34 |
35 | type CatDog struct {
36 | *Cat
37 | *Dog
38 | }
39 |
40 | func (cd *CatDog) Name() string {
41 | return fmt.Sprintf("%s%s", cd.Cat.Name(), cd.Dog.Name())
42 | }
43 |
44 | func main() {
45 | //1 - Interfaces
46 | var animal Animal
47 | animal = &Dog{} // returns a pointer to a new Dog
48 | fmt.Println(animal.Name()) // Dog
49 |
50 | //2 - Struct embedding
51 | gd := &GuideDog{}
52 | gd.Help(nil) // prints “Hey human, grab Dog’s leash!”
53 |
54 | //3 - Multi Struct embedding
55 | cd := &CatDog{}
56 | fmt.Printf("My favorite animal is the %s!\n", cd.Name())
57 | }
58 |
--------------------------------------------------------------------------------
/day-02/14-interfaces/interfaces.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | // Permanent represent an employee with a permanent position
6 | type Permanent struct {
7 | empID int
8 | basicPay int
9 | pension int
10 | }
11 |
12 | // Contract represent an employee with a contractual position
13 | type Contract struct {
14 | empID int
15 | basicPay int
16 | }
17 |
18 | func (p Permanent) calculateSalary() int {
19 | return p.basicPay + p.pension
20 | }
21 |
22 | func (p Contract) calculateSalary() int {
23 | return p.basicPay
24 | }
25 |
26 | // SalaryCalculator calculates salary
27 | type SalaryCalculator interface {
28 | calculateSalary() int
29 | }
30 |
31 | func main() {
32 | sc := [...]SalaryCalculator{Permanent{1, 5000, 50}, Contract{3, 7000}}
33 | totalPayout := 0
34 | for _, v := range sc {
35 | totalPayout += v.calculateSalary()
36 | }
37 | fmt.Println("Total Salary:", totalPayout)
38 | }
39 |
--------------------------------------------------------------------------------
/day-02/14-interfaces/type-assertions.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | var i interface{} = "hello"
7 |
8 | s := i.(string)
9 | fmt.Println(s)
10 |
11 | s, ok := i.(string)
12 | fmt.Println(s, ok)
13 |
14 | f, ok := i.(float64)
15 | fmt.Println(f, ok)
16 |
17 | f = i.(float64) // panic
18 | fmt.Println(f)
19 | }
20 |
--------------------------------------------------------------------------------
/day-02/15-concurrency/README.md.org:
--------------------------------------------------------------------------------
1 | * Concurrencia en Go.
2 | :PROPERTIES:
3 | :CUSTOM_ID: concurrencia-en-go.
4 | :END:
5 |
6 | ** Gorutinas
7 | :PROPERTIES:
8 | :CUSTOM_ID: gorutinas
9 | :END:
10 |
11 | Las gorutinas son funciones o métodos que se ejecutan de modo
12 | concurrente. Para los efectos se pueden considerar como hilos o
13 | threads ligeros.
14 |
15 | Las gorutinas se crean usando la palabre clave =go= antes de la
16 | llamada a una función.
17 |
18 | #+begin_src go
19 | // llamar name como una gorutina
20 | go name()
21 |
22 | // gorutina anónima como
23 | go func() {
24 | // código
25 | }()
26 | #+end_src
27 |
28 | *** Sincronizando gorutinas
29 |
30 | Las gorutinas se ejecutan de modo *concurrente*. Para sincronizar
31 | gorutinas es común utilizar elementos de sincronización como
32 | =sync.WaitGroup=.
33 |
34 | #+begin_src go
35 | func main() {
36 | wg := &sync.WaitGroup{}
37 | wg.Add(4)
38 | for i:=0; i < 4; i++ {
39 | go worker(wg)
40 | }
41 | // Sin esto es posible que main termine
42 | // antes de las gorutinas.
43 | wg.Wait()
44 | }
45 |
46 | func worker(w *sync.WaitGroup) {
47 | defer wg.Done()
48 | DoWork()
49 | }
50 | #+end_src
51 |
52 | ** Canales
53 | :PROPERTIES:
54 | :CUSTOM_ID: canales
55 | :END:
56 |
57 | Los canales son un mecanismo para sincronización y comunicación entre
58 | gorutinas. Los usuarios de sistemas basados en Unix pueden pensar en
59 | canales como /pipes/.
60 |
61 | Para crear un canal usamos la función =make=.
62 |
63 | #+begin_src go
64 | c := make(chan string) // canal sin buffer
65 | cb := make(chan string, 200) // canal con buffer de 200
66 | #+end_src
67 |
68 | *** Tipos de canales
69 |
70 | La comunicación vía canales en síncrona. Cuando se envían datos a un
71 | canal la gorutina se bloquea hasta que estos datos sean recibidos. Lo
72 | mismo ocurre si se intenta recibir sin haber datos disponibles.
73 |
74 | Los canales con buffer permiten acumular datos enviados y solo bloquean si
75 |
76 | 1. Se han enviado =n= datos, donde =n= es el tamaño del buffer.
77 | 2. Se intenta recibir desde el canal y el buffer no contiene datos.
78 |
79 | *** Terminando la comunicación.
80 |
81 | Para cerrar un canal se utiliza =close=. Un canal cerrado no puede
82 | usarse para enviar datos. Recibir datos de un canal cerrado siempre
83 | retornará el /valor cero/ del tipo de datos del canal.
84 |
85 | #+begin_src go
86 | // Enviar
87 | c <- "Hola"
88 | // Recibir
89 | v := <- c
90 | // Recibir mientras el canal esté abierto
91 | for v := range c {
92 | processValue(v)
93 | }
94 | #+end_src
95 |
96 | *** Multiplexing
97 |
98 | La sentencia =select= nos permite esperar por el resultado de más de
99 | un canal.
100 |
101 | #+begin_src go
102 | // Esperamos por mensaje o timeout
103 | for {
104 | select {
105 | case v := <-input:
106 | doOperation(v)
107 | // Canal de tipo <- chan Time
108 | case <- time.After(time.Duration(80) * time.Millisecond):
109 | fmt.Println("Timeout!")
110 | return results
111 | }
112 | }
113 | #+end_src
114 |
115 | ** Referencias
116 | :PROPERTIES:
117 | :CUSTOM_ID: referencias
118 | :END:
119 |
120 | 1. [[https://blog.golang.org/waza-talk][Concurrency is Not Parallelism]]
121 | 2. [[https://tour.golang.org/concurrency/1][Gorutines - A Tour of Go]]
122 | 3. [[https://tour.golang.org/concurrency/2][Channels - A Tour of Go]]
123 | 4. [[https://tour.golang.org/concurrency/3][Buffered Channels - A Tour of Go]]
124 |
--------------------------------------------------------------------------------
/day-02/15-concurrency/concurrency/go.mod:
--------------------------------------------------------------------------------
1 | module sample15
2 |
3 | go 1.15
4 |
--------------------------------------------------------------------------------
/day-02/15-concurrency/concurrency/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | "time"
7 | )
8 |
9 | func main() {
10 | numPiecesOfWork := 20
11 | numWorkers := 5
12 | workCh := make(chan int)
13 | wg := &sync.WaitGroup{}
14 | wg.Add(numWorkers)
15 | for i := 0; i < numWorkers; i++ {
16 | go worker(workCh, wg)
17 | }
18 | for i := 0; i < numPiecesOfWork; i++ {
19 | work := i % 10
20 | workCh <- work
21 | }
22 | close(workCh)
23 | v := <-workCh
24 | fmt.Println(v)
25 | wg.Wait()
26 | fmt.Println("done")
27 | }
28 |
29 | func worker(workCh <-chan int, wg *sync.WaitGroup) {
30 | defer wg.Done()
31 | for work := range workCh {
32 | doWork(work)
33 | }
34 | }
35 |
36 | func doWork(work int) {
37 | time.Sleep(time.Duration(work) * time.Millisecond)
38 | fmt.Println("slept for", work, "milliseconds")
39 | }
40 |
--------------------------------------------------------------------------------
/day-03/16-json/README.md.org:
--------------------------------------------------------------------------------
1 | * Formatos de datos: JSON y XML
2 | :PROPERTIES:
3 | :CUSTOM_ID: formatos-de-datos-json-y-xml
4 | :END:
5 |
6 | ** El paquete =encoding=.
7 | :PROPERTIES:
8 | :CUSTOM_ID: encoding
9 | :END:
10 |
11 | El paquete =encoding= define interfaces para convertir datos
12 | desde/hacia bytes o representaciones de texto.
13 |
14 | - =ascii85= Formato utilizado por Adobe PDF y Postcript.
15 | - =asn1= Estructuras ASN.1 en formato DER.
16 | - =base32= Codificación Base32, RFC 4648.
17 | - =base64= Codificación Base64, RFC 4648.
18 | - =binary= Traducción entre formatos numéricos y codificación a bytes de los mismos.
19 | - =csv= Archivos con valores separados por coma (CSV).
20 | - =gob= Transmisión de flujos de datos entre emisor y receptor ()
21 | - =hex= Valores hexadecimales.
22 | - =json= JSON como se define en la RFC 7159.
23 | - =pem= Formato PEM usado actualmente para representar llaves TLS y certificados.
24 | - =xml= Parser XML 1.0 con soporte para espacios de nombres.
25 |
26 | ** Serializando a JSON (Marshal).
27 | :PROPERTIES:
28 | :CUSTOM_ID: serializacion
29 | :END:
30 |
31 | =json.Marshal= codifica tipos Golang en JSON
32 |
33 | - =bool= como JSON Boolean.
34 | - valores numéricos como JSON Number.
35 | - =string= como JSON String, eliminando etiquetas HTML.
36 | - arreglos y /slices/ como JSON Array.
37 | - =struct= como JSON Object.
38 | - =nil= como =null=
39 | - =chan=, complex y literales de funciones provocan error.
40 |
41 | *** Serializando estructuras
42 |
43 | Las etiquetas definen como se serializan las estructuras.
44 |
45 | #+begin_src go
46 | type Address struct {
47 | // omitempty hace que los campos vacíos no
48 | // no se serialice.
49 | City string `json:"ciudad,omitempty"`
50 | State string `json:"estado,omitempty"`
51 | // Campos marcados con - no se serializan
52 | Zip strung `json:"-"`
53 | }
54 |
55 | type Employee struct {
56 | FirstName string `json:"nombre"`
57 | LastName string `json:"apellido"`
58 | Age int `json:"edad"`
59 | // no se serializa porque no es exportado
60 | id string
61 | Address
62 | }
63 | #+end_src
64 |
65 | #+REVEAL: split
66 |
67 | #+begin_src go
68 | emp1 := Employee{"Peter", "Parker", 22, Address{"Manhattan", "New York"}}
69 | d, _ := json.Marshal(&emp1)
70 | fmt.Println(string(d))
71 | #+end_src
72 |
73 | ** Deserializando desde JSON (Unmarshal).
74 | :PROPERTIES:
75 | :CUSTOM_ID: deserializacion
76 | :END:
77 |
78 | =json.Unmarshal= es la contraparte de =json.Marshal=.
79 |
80 | #+begin_src go
81 | tony := `{"nombre":"Tony","apellido":"Stark","edad":44}`
82 | var emp3 Employee
83 | err = json.Unmarshal([]byte(tony), &emp3)
84 | if err != nil {
85 | log.Fatal("No se pudo deserializar")
86 | }
87 | #+end_src
88 |
89 | *** Consideraciones.
90 |
91 | 1. =Unmarshal= a un slice elimina los valores del slice.
92 | 2. =Unmarshal= a un array descarta los valores extra si el array
93 | destino es muy pequeño.
94 | 3. =Unmarshal= a un =map= conserva las llaves existentes.
95 | 4. =null= se traduce a =nil= o al valor que tenga el tipo sin
96 | inicializar
97 |
--------------------------------------------------------------------------------
/day-03/16-json/json-ser-de/go.mod:
--------------------------------------------------------------------------------
1 | module sample16
2 |
3 | go 1.15
4 |
--------------------------------------------------------------------------------
/day-03/16-json/json-ser-de/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "log"
7 | )
8 |
9 | type Address struct {
10 | City string `json:"ciudad,omitempty"`
11 | State string `json:"estado,omitempty"`
12 | }
13 |
14 | type Employee struct {
15 | FirstName string `json:"nombre"`
16 | LastName string `json:"apellido"`
17 | Age int `json:"edad"`
18 | Address
19 | }
20 |
21 | func main() {
22 | emp1 := Employee{"Peter", "Parker", 22, Address{"Manhattan", "New York"}}
23 | emp2 := Employee{"Hulk", "Banner", 22, Address{}}
24 | d, err := json.Marshal(&emp1)
25 | if err != nil {
26 | log.Fatal("No se pudo serializar")
27 | }
28 | fmt.Println(string(d))
29 | d, err = json.Marshal(&emp2)
30 | if err != nil {
31 | log.Fatal("No se pudo serializar")
32 | }
33 | fmt.Println(string(d))
34 | tony := `{"nombre":"Tony","apellido":"Stark","edad":44}`
35 | var emp3 Employee
36 | err = json.Unmarshal([]byte(tony), &emp3)
37 | if err != nil {
38 | log.Fatal("No se pudo deserializar")
39 | }
40 | fmt.Println(emp3)
41 | }
42 |
--------------------------------------------------------------------------------
/day-03/17-rest-api/README.md.org:
--------------------------------------------------------------------------------
1 | * Creando APIs REST
2 | :PROPERTIES:
3 | :CUSTOM_ID: creando-apis-rest
4 | :END:
5 |
6 | ** Servidor HTTP.
7 | :PROPERTIES:
8 | :CUSTOM_ID: servidor-http.
9 | :END:
10 |
11 | El paquete =net/http= contiene implementaciones de servidor y cliente
12 | para este protocolo. Crear un programa que responda a peticiones HTTP es
13 | una tarea sencilla.
14 |
15 | #+begin_src go
16 | func main() {
17 | http.HandleFunc("/hello", func(w http.ResponseWWriter, r *http.Request) {
18 | fmt.Fprintf(w, "Hello world")
19 | })
20 | log.Fatal(http.ListenAndServe(":8080", nil))
21 | }
22 | #+end_src
23 |
24 | Las estructuras =Server= y =ServeMux= son las encargadas de operar el
25 | servidor y el ciclo de petición y respuesta. La variable
26 | =DefaultServeMux= es utilizada por el servidor por defecto
27 |
28 | ** Creando nuestro propio ServeMux
29 | :PROPERTIES:
30 | :CUSTOM_ID: creando-nuestro-propio-servemux
31 | :END:
32 |
33 | Usar =DefaultServeMux= tiene sentido para proyectos pequeños, pero lo
34 | usual es que utilicemos nuestra propio =ServeMux=
35 |
36 | #+begin_src go
37 | mux := http.NewServeMux()
38 | mux.HandleFunc("/hello", func(w http.ResponseWriter, req *http.Request) {
39 | fmt.Fprintf(w, "Hello world")
40 | })
41 | log.Fatal(http.ListenAndServe(":8080", mux))
42 | #+end_src
43 |
44 | Alternativamente podemos crear nuestro propio =Server=.
45 |
46 | #+begin_src go
47 | // Create a server listening on port 8000
48 | s := &http.Server{
49 | Addr: ":8000",
50 | Handler: mux,
51 | }
52 |
53 | // Continue to process new requests until an error occurs
54 | log.Fatal(s.ListenAndServe())
55 | #+end_src
56 |
57 | ** APIS REST con ServeMux.
58 | :PROPERTIES:
59 | :CUSTOM_ID: apis-rest-con-servemux.
60 | :END:
61 |
62 | Tanto =Server= como =ServeMux= proveen el nivel mínimo de abstracción
63 | necesario para manejar peticiones HTTP.
64 |
65 | Tomemos por ejemplo el código para hacer un API REST sencilla
66 |
67 | #+begin_src go
68 | type H = func(w http.ResponseWriter, r *http.Request)
69 |
70 | func dispatchResource(get, post, put, delete H) H {
71 | return func(w http.ResponseWriter, r *http.Request) {
72 | w.Header().Set("Content-Type", "application/json")
73 | switch r.Method {
74 | case "GET":
75 | get(w, r)
76 | case "POST":
77 | post(w, r)
78 | case "PUT":
79 | put(w, r)
80 | case "DELETE":
81 | delete(w, r)
82 | default:
83 | w.WriteHeader(http.StatusNotFound)
84 | w.Write([]byte(`{"message": "not found"}`))
85 | }
86 | }
87 | }
88 |
89 | func main() {
90 | // initialize mux
91 | getFunc := func(w http.ResponseWriter, r *http.Request) {
92 | w.WriteHeader(http.StatusOK)
93 | w.Write([]byte(`{"message": "get called"}`))
94 | }
95 | postFunc := func(w http.ResponseWriter, r *http.Request) {
96 | w.WriteHeader(http.StatusCreated)
97 | w.Write([]byte(`{"message": "post called"}`))
98 | }
99 | m.HandleFunc("/resource", dispatchResource(getFunc, postFunc, nil, nil))
100 | // use mux
101 | }
102 | #+end_src
103 |
104 | ** Referencias
105 | :PROPERTIES:
106 | :CUSTOM_ID: referencias
107 | :END:
108 |
109 | - [[https://pkg.go.dev/net/http][Documentación de net/http]]
110 | - [[https://github.com/gorilla/mux][Gorilla Mux]]
111 | - [[https://github.com/gin-gonic/gin][Gin]]
112 |
--------------------------------------------------------------------------------
/day-03/17-rest-api/hello-world-api/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | )
7 |
8 | func main() {
9 | http.HandleFunc("/hello", func(w http.ResponseWWriter, r *http.Request) {
10 | fmt.Fprintf(w, "Hello world")
11 | })
12 | log.Fatal(http.ListenAndServe(":8080", nil))
13 | }
14 |
--------------------------------------------------------------------------------
/day-03/17-rest-api/helloworld-api-gorilla/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 |
8 | "github.com/gorilla/mux"
9 | )
10 |
11 | func helloWorld(w http.ResponseWriter, r *http.Request) {
12 | fmt.Fprintf(w, "Hello World")
13 | }
14 |
15 | func main() {
16 | router := mux.NewRouter().StrictSlash(true)
17 | router.HandleFunc("/", helloWorld)
18 | log.Fatal(http.ListenAndServe(":8080", router))
19 | }
20 |
--------------------------------------------------------------------------------
/day-03/17-rest-api/rest-api-gorilla/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "log"
8 | "net/http"
9 |
10 | "github.com/gorilla/mux"
11 | )
12 |
13 | type data struct {
14 | Num int `json:"Num"`
15 | Text string `json:"Text"`
16 | }
17 |
18 | func create(w http.ResponseWriter, r *http.Request) {
19 | var newData data
20 | reqBody, err := ioutil.ReadAll(r.Body)
21 | if err != nil {
22 | fmt.Fprintf(w, err.Error())
23 | }
24 |
25 | json.Unmarshal(reqBody, &newData)
26 |
27 | w.WriteHeader(http.StatusCreated)
28 |
29 | json.NewEncoder(w).Encode(newData)
30 | }
31 |
32 | func getOne(w http.ResponseWriter, r *http.Request) {
33 | fmt.Fprintf(w, "The item with %v has been requested", mux.Vars(r)["num"])
34 | }
35 |
36 | func getAll(w http.ResponseWriter, r *http.Request) {
37 | fmt.Fprintf(w, "The all items has been requested")
38 | }
39 |
40 | func update(w http.ResponseWriter, r *http.Request) {
41 | var updatedData data
42 | reqBody, err := ioutil.ReadAll(r.Body)
43 | if err != nil {
44 | fmt.Fprintf(w, err.Error())
45 | }
46 |
47 | json.Unmarshal(reqBody, &updatedData)
48 |
49 | json.NewEncoder(w).Encode(updatedData)
50 | }
51 |
52 | func delete(w http.ResponseWriter, r *http.Request) {
53 | fmt.Fprintf(w, "The item with %v has been deleted successfully", mux.Vars(r)["num"])
54 | }
55 |
56 | func main() {
57 | router := mux.NewRouter().StrictSlash(true)
58 |
59 | router.HandleFunc("/api", create).Methods("POST")
60 | router.HandleFunc("/api", getAll).Methods("GET")
61 | router.HandleFunc("/api/{num}", getOne).Methods("GET")
62 | router.HandleFunc("/api/{num}", update).Methods("PATCH")
63 | router.HandleFunc("/api/{num}", delete).Methods("DELETE")
64 |
65 | log.Fatal(http.ListenAndServe(":8080", router))
66 | }
67 |
--------------------------------------------------------------------------------
/day-03/17-rest-api/rest-api/go.mod:
--------------------------------------------------------------------------------
1 | module sample17
2 |
3 | go 1.15
4 |
--------------------------------------------------------------------------------
/day-03/17-rest-api/rest-api/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "net/http"
6 | )
7 |
8 | type H = func(w http.ResponseWriter, r *http.Request)
9 |
10 | func dispatchResource(get, post, put, delete H) H {
11 | return func(w http.ResponseWriter, r *http.Request) {
12 | w.Header().Set("Content-Type", "application/json")
13 | switch r.Method {
14 | case "GET":
15 | get(w, r)
16 | case "POST":
17 | post(w, r)
18 | case "PUT":
19 | put(w, r)
20 | case "DELETE":
21 | delete(w, r)
22 | default:
23 | w.WriteHeader(http.StatusNotFound)
24 | w.Write([]byte(`{"message": "not found"}`))
25 | }
26 | }
27 | }
28 |
29 | func main() {
30 |
31 | // Create a mux for routing incoming requests
32 | m := http.NewServeMux()
33 |
34 | getFunc := func(w http.ResponseWriter, r *http.Request) {
35 | w.WriteHeader(http.StatusOK)
36 | w.Write([]byte(`{"message": "get called"}`))
37 | }
38 | postFunc := func(w http.ResponseWriter, r *http.Request) {
39 | w.WriteHeader(http.StatusCreated)
40 | w.Write([]byte(`{"message": "post called"}`))
41 | }
42 | m.HandleFunc("/resource", dispatchResource(getFunc, postFunc, nil, nil))
43 |
44 | // Create a server listening on port 8000
45 | s := &http.Server{
46 | Addr: ":8000",
47 | Handler: m,
48 | }
49 |
50 | // Continue to process new requests until an error occurs
51 | log.Fatal(s.ListenAndServe())
52 | }
53 |
--------------------------------------------------------------------------------
/day-03/18-http-client/README.md.org:
--------------------------------------------------------------------------------
1 | * HTTP Client
2 | :PROPERTIES:
3 | :CUSTOM_ID: http-client
4 | :END:
5 |
6 | El paquete [[https://golang.org/pkg/net/http/][http]] contiene una implementacion de un cliente http que
7 | permite emular las acciones que realiza un web browser.
8 |
9 | Para este ejercicio se utiliza el tester HTTP https://httpbin.org
10 | ([[https://github.com/postmanlabs/httpbin][codigo fuente]], [[https://hub.docker.com/r/kennethreitz/httpbin/][imagen de Docker para uso local]]).
11 |
12 | ** Ejecutando un GET
13 | :PROPERTIES:
14 | :CUSTOM_ID: ejecutando-un-get
15 | :END:
16 |
17 | #+begin_src go
18 | resp, _ := http.Get("https://httpbin.org/get")
19 | defer resp.Body.Close()
20 |
21 | data, _ := ioutil.ReadAll(resp.Body)
22 | fmt.Println(string(data))
23 | #+end_src
24 |
25 | ** Ejecutando un POST
26 | :PROPERTIES:
27 | :CUSTOM_ID: ejecutando-un-post
28 | :END:
29 |
30 | #+begin_src go
31 | payload := "Hello world!"
32 | resp, _ := http.Post("https://httpbin.org/post", "text/plain", strings.NewReader(payload))
33 | defer resp.Body.Close()
34 |
35 | data, _ := ioutil.ReadAll(resp.Body)
36 | fmt.Println(string(data))
37 | #+end_src
38 |
39 | Notar que si obtenemos un error de =http.Get= o =http.Post= debemos
40 | cerrar el body (utilizando =defer=).
41 |
42 | ** HTTP request timeouts
43 | :PROPERTIES:
44 | :CUSTOM_ID: http-request-timeouts
45 | :END:
46 |
47 | Podemos especificar un tiempo maximo de espera para que un request que
48 | tarda mucho termine automaticamente.
49 |
50 | Crear un nuevo objeto request:
51 |
52 | #+begin_src go
53 | req, _ := http.NewRequest(http.MethodGet, url, nil)
54 | #+end_src
55 |
56 | Crear un =context= con timeout:
57 |
58 | #+begin_src go
59 | ctx, cancel := context.WithTimeout(context.Background(), timeout)
60 | defer cancel()
61 | #+end_src
62 |
63 | Ejecutar el request pasando por parametro el =context= con el timeout:
64 |
65 | #+begin_src go
66 | resp, _ := http.DefaultClient.Do(req.WithContext(ctx))
67 | defer resp.Body.Close()
68 | #+end_src
69 |
--------------------------------------------------------------------------------
/day-03/18-http-client/go.mod:
--------------------------------------------------------------------------------
1 | module sample18
2 |
3 | go 1.15
4 |
--------------------------------------------------------------------------------
/day-03/18-http-client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "io/ioutil"
8 | "net/http"
9 | "strings"
10 | "time"
11 | )
12 |
13 | func httpGet(url string) (string, error) {
14 | resp, err := http.Get(url)
15 | if err != nil {
16 | return "", fmt.Errorf("HTTP error: %s", err)
17 | }
18 | defer resp.Body.Close()
19 |
20 | if resp.StatusCode != http.StatusOK {
21 | return "", fmt.Errorf("expected status OK but got %q instead", resp.Status)
22 | }
23 |
24 | data, err := ioutil.ReadAll(resp.Body)
25 | if err != nil {
26 | return "", fmt.Errorf("read error: %s\n", err)
27 | }
28 |
29 | return string(data), nil
30 | }
31 |
32 | func httpPost(url string) (string, error) {
33 | payload := "Hello world!"
34 | resp, err := http.Post(url, "text/plain", strings.NewReader(payload))
35 | if err != nil {
36 | return "", fmt.Errorf("HTTP error: %s", err)
37 | }
38 | defer resp.Body.Close()
39 |
40 | if resp.StatusCode != http.StatusOK {
41 | return "", fmt.Errorf("expected status OK but got %q instead", resp.Status)
42 | }
43 |
44 | data, err := ioutil.ReadAll(resp.Body)
45 | if err != nil {
46 | return "", fmt.Errorf("read error: %s\n", err)
47 | }
48 |
49 | return string(data), nil
50 | }
51 |
52 | func getUserAgent(jsonData string) (string, error) {
53 | type response struct {
54 | Headers struct {
55 | UserAgent string `json:"User-Agent"`
56 | } `json:"headers"`
57 | }
58 |
59 | var resp response
60 | err := json.Unmarshal([]byte(jsonData), &resp)
61 | if err != nil {
62 | return "", fmt.Errorf("json parsing error: %s\n", err)
63 | }
64 |
65 | return resp.Headers.UserAgent, nil
66 | }
67 |
68 | func getWithTimeout(url string, timeout time.Duration) (string, error) {
69 | req, err := http.NewRequest(http.MethodGet, url, nil)
70 | if err != nil {
71 | return "", fmt.Errorf("envalid URL: %s", err)
72 | }
73 |
74 | ctx, cancel := context.WithTimeout(context.Background(), timeout)
75 | defer cancel()
76 |
77 | resp, err := http.DefaultClient.Do(req.WithContext(ctx))
78 | if err != nil {
79 | return "", fmt.Errorf("HTTP error: %s", err)
80 | }
81 | defer resp.Body.Close()
82 |
83 | if resp.StatusCode != http.StatusOK {
84 | return "", fmt.Errorf("expected status OK but got %q instead", resp.Status)
85 | }
86 |
87 | data, err := ioutil.ReadAll(resp.Body)
88 | if err != nil {
89 | return "", fmt.Errorf("read error: %s\n", err)
90 | }
91 |
92 | return string(data), nil
93 | }
94 |
95 | func main() {
96 | //////////////
97 | // HTTP GET //
98 | //////////////
99 |
100 | payload, err := httpGet("https://httpbin.org/get")
101 | if err != nil {
102 | fmt.Printf("GET error: %s\n", err)
103 | }
104 |
105 | fmt.Println("GET response:")
106 | fmt.Println(payload)
107 |
108 | ///////////////
109 | // HTTP POST //
110 | ///////////////
111 |
112 | payload, err = httpPost("https://httpbin.org/post")
113 | if err != nil {
114 | fmt.Printf("POST error: %s\n", err)
115 | }
116 |
117 | fmt.Println("POST response:")
118 | fmt.Println(payload)
119 |
120 | ////////////////////
121 | // Get User Agent //
122 | ////////////////////
123 |
124 | payload, err = httpGet("https://httpbin.org/headers")
125 | if err != nil {
126 | fmt.Printf("GET error: %s\n", err)
127 | }
128 |
129 | userAgent, err := getUserAgent(payload)
130 | if err != nil {
131 | fmt.Printf("Failed to extract user agent: %s\n", err)
132 | }
133 |
134 | fmt.Printf("Request user agent: %s\n\n", userAgent)
135 |
136 | //////////////////////
137 | // Get with timeout //
138 | //////////////////////
139 |
140 | httpbinTimeout := 5
141 | httpTimeout := 10 * time.Second
142 | payload, err = getWithTimeout(
143 | fmt.Sprintf("https://httpbin.org/delay/%d", httpbinTimeout),
144 | httpTimeout,
145 | )
146 | if err != nil {
147 | fmt.Printf("GET with timeout error: %s\n", err)
148 | }
149 |
150 | fmt.Println("GET with timeout response:")
151 | fmt.Println(payload)
152 | }
153 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.1/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 1.1
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-1.1
4 | :END:
5 | Crear una funcion /Reverse/ que devuelva un string pasado por parametro.
6 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.1/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | fmt.Printf(Reverse("!oG ,olleH"))
7 | }
8 |
9 | func Reverse(s string) string {
10 | r := []rune(s)
11 | for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
12 | r[i], r[j] = r[j], r[i]
13 | }
14 | return string(r)
15 | }
16 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.2/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 1.2
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-1.2
4 | :END:
5 | Modificar la funcion /Reverse/ para invertir el /casing/ de las vocales
6 | (solamente).
7 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.2/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | fmt.Printf(Reverse("!oG ,olleH"))
7 | }
8 |
9 | func Reverse(s string) string {
10 | r := []rune(s)
11 | for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
12 | r[i], r[j] = ChangeVowelCase(r[j]), ChangeVowelCase(r[i])
13 | }
14 | return string(r)
15 | }
16 |
17 | func ChangeVowelCase(s rune) rune {
18 | var ret string = string(s)
19 |
20 | switch ret {
21 | case "A":
22 | ret = "a"
23 | case "E":
24 | ret = "e"
25 | case "I":
26 | ret = "i"
27 | case "O":
28 | ret = "o"
29 | case "U":
30 | ret = "u"
31 | case "a":
32 | ret = "A"
33 | case "e":
34 | ret = "E"
35 | case "i":
36 | ret = "I"
37 | case "o":
38 | ret = "O"
39 | case "u":
40 | ret = "U"
41 | }
42 |
43 | return []rune(ret)[0]
44 | }
45 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.3/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 1.3
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-1.3
4 | :END:
5 | Crear un paquete /stringutil/ que contega la funcion /Reverse/ y se
6 | utilice en funcion la /main/.
7 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.3/reverseapp/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "stringutil"
6 | )
7 |
8 | func main() {
9 | fmt.Printf(stringutil.Reverse("!oG ,olleH"))
10 | }
11 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.3/stringutil/reverse.go:
--------------------------------------------------------------------------------
1 | package stringutil
2 |
3 | func Reverse(s string) string {
4 | r := []rune(s)
5 | for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
6 | r[i], r[j] = r[j], r[i]
7 | }
8 | return string(r)
9 | }
10 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.4/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 1.4
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-1.4
4 | :END:
5 | Cambiar el programa para que el parametro string de la funcion /Reverse/
6 | se obtenga desde la linea de comandos con un flag llamado /text/.
7 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.4/reverseapp/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "stringutil"
7 | )
8 |
9 | func main() {
10 | in := flag.String("text", "!oG ,olleH", "a string")
11 | flag.Parse()
12 |
13 | fmt.Printf(stringutil.Reverse(*in))
14 | }
15 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.4/stringutil/reverse.go:
--------------------------------------------------------------------------------
1 | package stringutil
2 |
3 | func Reverse(s string) string {
4 | r := []rune(s)
5 | for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
6 | r[i], r[j] = r[j], r[i]
7 | }
8 | return string(r)
9 | }
10 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.5/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 1.5
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-1.5
4 | :END:
5 | Crear un /test/, un /benchmark/, y un /example/ para la funcion
6 | /Reverse/.
7 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.5/reverseapp/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "stringutil"
6 | )
7 |
8 | func main() {
9 | fmt.Printf(stringutil.Reverse("!oG ,olleH"))
10 | }
11 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.5/stringutil/reverse.go:
--------------------------------------------------------------------------------
1 | // Additional string funcions
2 | package stringutil
3 |
4 | // Return the reverse of the input parameter string
5 | func Reverse(s string) string {
6 | r := []rune(s)
7 | for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
8 | r[i], r[j] = r[j], r[i]
9 | }
10 | return string(r)
11 | }
12 |
--------------------------------------------------------------------------------
/day-04/challenge1/1.5/stringutil/reverse_test.go:
--------------------------------------------------------------------------------
1 | package stringutil
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestReverse(t *testing.T) {
9 | cases := []struct {
10 | in, want string
11 | }{
12 | {"Hello, world", "dlrow ,olleH"},
13 | {"Hello, 世界", "界世 ,olleH"},
14 | {"", ""},
15 | }
16 | for _, c := range cases {
17 | got := Reverse(c.in)
18 | if got != c.want {
19 | t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
20 | }
21 | }
22 | }
23 |
24 | func BenchmarkReverse(b *testing.B) {
25 | for n := 0; n < b.N; n++ {
26 | Reverse("Hello, world")
27 | }
28 | }
29 |
30 | func ExampleReverse() {
31 | fmt.Println(Reverse("Hello, world"))
32 | // Output: dlrow ,olleH
33 | }
34 |
--------------------------------------------------------------------------------
/day-04/challenge2/2.1/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 2.1
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-2.1
4 | :END:
5 | Modelar la funcionalidad de un sistema de precios para una aerolinea que
6 | calcule los ingresos netos de un vuelo en base a los pasajeros y el
7 | precio base del ticket.
8 |
9 | El precio base del ticket es el mismo para todos los pasajeros.
10 |
11 | Existen 3 tipos de pasajeros: * Base: Paga el 100% del precio base. * De
12 | ultimo minuto: Paga el 50% del precio base. * Empleado de la aerolinea:
13 | No paga el ticket.
14 |
--------------------------------------------------------------------------------
/day-04/challenge2/2.1/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type Passenger interface {
6 | GetDiscount() float32
7 | }
8 |
9 | type BasePassenger struct {
10 | }
11 |
12 | func (*BasePassenger) GetDiscount() float32 {
13 | return 0
14 | }
15 |
16 | type LastMinutePassenger struct {
17 | }
18 |
19 | func (*LastMinutePassenger) GetDiscount() float32 {
20 | return 0.5
21 | }
22 |
23 | type EmployeeAirlinePassenger struct {
24 | }
25 |
26 | func (*EmployeeAirlinePassenger) GetDiscount() float32 {
27 | return 1
28 | }
29 |
30 | func NetIncome(passengers []Passenger, baseTicketPrice float32) float32 {
31 | var total float32
32 | for _, p := range passengers {
33 | total += baseTicketPrice - p.GetDiscount()*baseTicketPrice
34 | }
35 | return total
36 | }
37 |
38 | func main() {
39 | passengers := [...]Passenger{&BasePassenger{}, &LastMinutePassenger{}, &EmployeeAirlinePassenger{}}
40 | fmt.Println(NetIncome(passengers[:], 1000))
41 | }
42 |
--------------------------------------------------------------------------------
/day-04/challenge2/2.2/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 2.2
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-2.2
4 | :END:
5 | Crear un test para verificar el codigo creado en el challenge 2.1
6 | anterior.
7 |
--------------------------------------------------------------------------------
/day-04/challenge2/2.2/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type Passenger interface {
6 | GetDiscount() float32
7 | }
8 |
9 | type BasePassenger struct {
10 | }
11 |
12 | func (*BasePassenger) GetDiscount() float32 {
13 | return 0
14 | }
15 |
16 | type LastMinutePassenger struct {
17 | }
18 |
19 | func (*LastMinutePassenger) GetDiscount() float32 {
20 | return 0.5
21 | }
22 |
23 | type EmployeeAirlinePassenger struct {
24 | }
25 |
26 | func (*EmployeeAirlinePassenger) GetDiscount() float32 {
27 | return 1
28 | }
29 |
30 | func NetIncome(passengers []Passenger, baseTicketPrice float32) float32 {
31 | var total float32
32 | for _, p := range passengers {
33 | total += baseTicketPrice - p.GetDiscount()*baseTicketPrice
34 | }
35 | return total
36 | }
37 |
38 | func main() {
39 | passengers := [...]Passenger{&BasePassenger{}, &LastMinutePassenger{}, &EmployeeAirlinePassenger{}}
40 | fmt.Println(NetIncome(passengers[:], 1000))
41 | }
42 |
--------------------------------------------------------------------------------
/day-04/challenge2/2.2/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "testing"
4 |
5 | func TestNetIncome(t *testing.T) {
6 | passengers := [...]Passenger{&BasePassenger{}, &LastMinutePassenger{}, &EmployeeAirlinePassenger{}}
7 | var in float32 = 1000
8 | var want float32 = 1500
9 |
10 | got := NetIncome(passengers[:], in)
11 |
12 | if got != want {
13 | t.Errorf("netIncome(%f) == %f, want %f", in, got, want)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/day-04/challenge2/2.3/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 2.3
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-2.3
4 | :END:
5 | Agregar un nuevo tipo de pasajero 'empleado de aerolinea de ultimo
6 | minuto' cuyo descuento sea la suma de los descuentos de los tipos de
7 | pasajero 'empleado de aerolinea' y 'ultimo minuto'.
8 |
--------------------------------------------------------------------------------
/day-04/challenge2/2.3/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | type Passenger interface {
6 | GetDiscount() float32
7 | }
8 |
9 | type BasePassenger struct {
10 | }
11 |
12 | func (*BasePassenger) GetDiscount() float32 {
13 | return 0
14 | }
15 |
16 | type LastMinutePassenger struct {
17 | }
18 |
19 | func (*LastMinutePassenger) GetDiscount() float32 {
20 | return 0.5
21 | }
22 |
23 | type EmployeeAirlinePassenger struct {
24 | }
25 |
26 | func (*EmployeeAirlinePassenger) GetDiscount() float32 {
27 | return 1
28 | }
29 |
30 | type EmployeeAirlineLastMinutePassenger struct {
31 | LastMinutePassenger
32 | EmployeeAirlinePassenger
33 | }
34 |
35 | func (elm *EmployeeAirlineLastMinutePassenger) GetDiscount() float32 {
36 | return elm.LastMinutePassenger.GetDiscount() + elm.EmployeeAirlinePassenger.GetDiscount()
37 | }
38 |
39 | func NetIncome(passengers []Passenger, baseTicketPrice float32) float32 {
40 | var total float32
41 | for _, p := range passengers {
42 | total += baseTicketPrice - p.GetDiscount()*baseTicketPrice
43 | }
44 | return total
45 | }
46 |
47 | func main() {
48 | passengers := [...]Passenger{&BasePassenger{}, &LastMinutePassenger{}, &EmployeeAirlinePassenger{}, &EmployeeAirlineLastMinutePassenger{}}
49 | fmt.Println(NetIncome(passengers[:], 1000))
50 | }
51 |
--------------------------------------------------------------------------------
/day-04/challenge2/2.3/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "testing"
4 |
5 | func TestNetIncome(t *testing.T) {
6 | passengers := [...]Passenger{&BasePassenger{}, &LastMinutePassenger{}, &EmployeeAirlinePassenger{}, &EmployeeAirlineLastMinutePassenger{}}
7 | var in float32 = 1000
8 | var want float32 = 2000
9 |
10 | got := NetIncome(passengers[:], in)
11 |
12 | if got != want {
13 | t.Errorf("netIncome(%f) == %f, want %f", in, got, want)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/day-05/challenge3/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 3
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-3
4 | :END:
5 |
6 | - Convertir las siguientes funciones para que utilicen /gorutines/ y
7 | /channels/.
8 | - Crear dos /benchmarks/ para comparar la velocidad de ejecucion entre
9 | las versiones con y sin uso de /gorutines/.
10 |
11 | #+begin_src go
12 | func Squares(number int) (sum int) {
13 | sum = 0
14 | for number != 0 {
15 | digit := number % 10
16 | sum += digit * digit
17 | number /= 10
18 | }
19 | return
20 | }
21 |
22 | func Cubes(number int) (sum int) {
23 | sum = 0
24 | for number != 0 {
25 | digit := number % 10
26 | sum += digit * digit * digit
27 | number /= 10
28 | }
29 | return
30 | }
31 |
32 | func SquaresPlusCubes(number int) int {
33 | return Squares(number) + Cubes(number)
34 | }
35 | #+end_src
36 |
--------------------------------------------------------------------------------
/day-05/challenge3/math.go:
--------------------------------------------------------------------------------
1 | package math
2 |
3 | func Squares(number int) (sum int) {
4 | sum = 0
5 | for number != 0 {
6 | digit := number % 10
7 | sum += digit * digit
8 | number /= 10
9 | }
10 | return
11 | }
12 |
13 | func Cubes(number int) (sum int) {
14 | sum = 0
15 | for number != 0 {
16 | digit := number % 10
17 | sum += digit * digit * digit
18 | number /= 10
19 | }
20 | return
21 | }
22 |
23 | func SquaresPlusCubes(number int) int {
24 | return Squares(number) + Cubes(number)
25 | }
26 |
27 | func SquaresMT(number int, c chan int) {
28 | sum := 0
29 | for number != 0 {
30 | digit := number % 10
31 | sum += digit * digit
32 | number /= 10
33 | }
34 | c <- sum
35 | }
36 |
37 | func CubesMT(number int, c chan int) {
38 | sum := 0
39 | for number != 0 {
40 | digit := number % 10
41 | sum += digit * digit * digit
42 | number /= 10
43 | }
44 | c <- sum
45 | }
46 |
47 | func SquaresPlusCubesMT(number int, c chan int) {
48 | sc := make(chan int)
49 | cc := make(chan int)
50 | go SquaresMT(number, sc)
51 | go CubesMT(number, cc)
52 | squares, cubes := <-sc, <-cc
53 | c <- squares + cubes
54 | }
55 |
--------------------------------------------------------------------------------
/day-05/challenge3/math_test.go:
--------------------------------------------------------------------------------
1 | package math
2 |
3 | import "testing"
4 |
5 | const in int = 12345
6 | const want int = 280
7 |
8 | func TestSquaresPlusCubes(t *testing.T) {
9 | got := SquaresPlusCubes(in)
10 | if got != want {
11 | t.Errorf("SquaresPlusCubes(%d) == %d, want %d", in, got, want)
12 | }
13 | }
14 |
15 | func BenchmarkSquaresPlusCubes(b *testing.B) {
16 | for n := 0; n < b.N; n++ {
17 | SquaresPlusCubes(in)
18 | }
19 | }
20 |
21 | func TestSquaresPlusCubesMT(t *testing.T) {
22 | c := make(chan int)
23 | go SquaresPlusCubesMT(in, c)
24 | got := <-c
25 |
26 | if got != want {
27 | t.Errorf("SquaresPlusCubesMT(%d) == %d, want %d", in, got, want)
28 | }
29 | }
30 |
31 | func BenchmarkSquaresPlusCubesMT(b *testing.B) {
32 | for n := 0; n < b.N; n++ {
33 | c := make(chan int)
34 | go SquaresPlusCubesMT(in, c)
35 | got := <-c
36 |
37 | if got != want {
38 | b.Errorf("SquaresPlusCubesMT(%d) == %d, want %d", in, got, want)
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.1/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 4.1
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-4.1
4 | :END:
5 | Crear una API REST para una aplicacion de recordatorios (ToDo) que
6 | contenga todas las funciones
7 | [[https://en.wikipedia.org/wiki/Create,_read,_update_and_delete][CRUD]]
8 | para la siguiente entidad:
9 |
10 | #+begin_src go
11 | ID int
12 | Title string
13 | IsDone bool
14 | #+end_src
15 |
16 | Hints:
17 |
18 | - Los datos se pueden almacenar en un arreglo.
19 | - La funcion /strconv.Atoi/ del paquete
20 | /[[https://golang.org/pkg/strconv][strconv]]/ permite convertir un
21 | string a un integer.
22 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.1/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "log"
8 | "net/http"
9 | "strconv"
10 |
11 | "github.com/gorilla/mux"
12 | )
13 |
14 | type item struct {
15 | ID int `json:"id,omitempty"`
16 | Title string `json:"title,omitempty"`
17 | IsDone bool `json:"isdone,omitempty"`
18 | }
19 |
20 | var items = []item {}
21 |
22 | func homeEndpoint(w http.ResponseWriter, r *http.Request) {
23 | fmt.Fprintf(w, "Welcome to the To-Do Web API")
24 | }
25 |
26 | func createItemEndpoint(w http.ResponseWriter, r *http.Request) {
27 | var newItem item
28 | reqBody, err := ioutil.ReadAll(r.Body)
29 | if err != nil {
30 | fmt.Fprintf(w, err.Error())
31 | }
32 |
33 | json.Unmarshal(reqBody, &newItem)
34 | items = append(items, newItem)
35 | w.WriteHeader(http.StatusCreated)
36 |
37 | json.NewEncoder(w).Encode(newItem)
38 | }
39 |
40 | func getItemEndpoint(w http.ResponseWriter, r *http.Request) {
41 | itemID, _ := strconv.Atoi(mux.Vars(r)["id"])
42 |
43 | for _, singleItem := range items {
44 | if singleItem.ID == itemID {
45 | json.NewEncoder(w).Encode(singleItem)
46 | }
47 | }
48 | }
49 |
50 | func getItemsEndpoint(w http.ResponseWriter, r *http.Request) {
51 | json.NewEncoder(w).Encode(items)
52 | }
53 |
54 | func updateItemEndpoint(w http.ResponseWriter, r *http.Request) {
55 | itemID, _ := strconv.Atoi(mux.Vars(r)["id"])
56 | var updatedItem item
57 |
58 | reqBody, err := ioutil.ReadAll(r.Body)
59 | if err != nil {
60 | fmt.Fprintf(w, err.Error())
61 | }
62 | json.Unmarshal(reqBody, &updatedItem)
63 |
64 | for i, singleItem := range items {
65 | if singleItem.ID == itemID {
66 | singleItem.Title = updatedItem.Title
67 | singleItem.IsDone = updatedItem.IsDone
68 | items = append(items[:i], singleItem)
69 | json.NewEncoder(w).Encode(singleItem)
70 | }
71 | }
72 | }
73 |
74 | func deleteItemEndpoint(w http.ResponseWriter, r *http.Request) {
75 | itemID, _ := strconv.Atoi(mux.Vars(r)["id"])
76 |
77 | for i, singleItem := range items {
78 | if singleItem.ID == itemID {
79 | items = append(items[:i], items[i+1:]...)
80 | fmt.Fprintf(w, "The item with ID %v has been deleted successfully", itemID)
81 | break
82 | }
83 | }
84 | }
85 |
86 | func main() {
87 | router := mux.NewRouter().StrictSlash(true)
88 |
89 | router.HandleFunc("/", homeEndpoint)
90 | router.HandleFunc("/api", createItemEndpoint).Methods("POST")
91 | router.HandleFunc("/api", getItemsEndpoint).Methods("GET")
92 | router.HandleFunc("/api/{id}", getItemEndpoint).Methods("GET")
93 | router.HandleFunc("/api/{id}", updateItemEndpoint).Methods("PATCH")
94 | router.HandleFunc("/api/{id}", deleteItemEndpoint).Methods("DELETE")
95 |
96 | log.Fatal(http.ListenAndServe(":8080", router))
97 | }
98 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.2/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 4.2
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-4.2
4 | :END:
5 | Modificar la API Rest del challenge 4.1 para que pueda acceder a
6 | diferentes fuentes de datos (en memoria y MongoDB) usando el patron
7 | /[[https://medium.com/@pererikbergman/repository-design-pattern-e28c0f3e4a30][Repository]]/.
8 |
9 | Hints:
10 |
11 | - Se pueden copiar las funciones necesarias para operar con MongoDB
12 | desde el
13 | [[https://github.com/rfinochi/golang-workshop-src/tree/master/19-dbaccess][Ejercicio
14 | 19]].
15 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.2/inmemoryrepository.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | var items = []Item{}
4 |
5 | type InMemoryRepository struct {
6 | }
7 |
8 | func (InMemoryRepository) CreateItem(newItem Item) {
9 | items = append(items, newItem)
10 | }
11 |
12 | func (InMemoryRepository) UpdateItem(updatedItem Item) {
13 | for i, item := range items {
14 | if item.ID == updatedItem.ID {
15 | item.Title = updatedItem.Title
16 | item.IsDone = updatedItem.IsDone
17 | items = append(items[:i], item)
18 | }
19 | }
20 | }
21 |
22 | func (InMemoryRepository) GetItems() []Item {
23 | return items
24 | }
25 |
26 | func (InMemoryRepository) GetItem(id int) Item {
27 | var result Item
28 |
29 | for _, item := range items {
30 | if item.ID == id {
31 | result = item
32 | break
33 | }
34 | }
35 |
36 | return result
37 | }
38 |
39 | func (InMemoryRepository) DeleteItem(id int) {
40 | for i, item := range items {
41 | if item.ID == id {
42 | items = append(items[:i], items[i+1:]...)
43 | break
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.2/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "log"
8 | "net/http"
9 | "strconv"
10 |
11 | "github.com/gorilla/mux"
12 | )
13 |
14 | var repositoryType string
15 |
16 | func homeEndpoint(w http.ResponseWriter, r *http.Request) {
17 | fmt.Fprintf(w, "Welcome to the To-Do Web API")
18 | }
19 |
20 | func createItemEndpoint(w http.ResponseWriter, r *http.Request) {
21 | var newItem Item
22 | reqBody, err := ioutil.ReadAll(r.Body)
23 | if err != nil {
24 | fmt.Fprintf(w, err.Error())
25 | }
26 |
27 | json.Unmarshal(reqBody, &newItem)
28 |
29 | repo := createRepository()
30 | repo.CreateItem(newItem)
31 |
32 | w.WriteHeader(http.StatusCreated)
33 | fmt.Fprintf(w, "OK")
34 | }
35 |
36 | func getItemEndpoint(w http.ResponseWriter, r *http.Request) {
37 | id, _ := strconv.Atoi(mux.Vars(r)["id"])
38 |
39 | repo := createRepository()
40 | json.NewEncoder(w).Encode(repo.GetItem(id))
41 | }
42 |
43 | func getItemsEndpoint(w http.ResponseWriter, r *http.Request) {
44 | repo := createRepository()
45 | json.NewEncoder(w).Encode(repo.GetItems())
46 | }
47 |
48 | func updateItemEndpoint(w http.ResponseWriter, r *http.Request) {
49 | id, _ := strconv.Atoi(mux.Vars(r)["id"])
50 | var updatedItem Item
51 |
52 | reqBody, err := ioutil.ReadAll(r.Body)
53 | if err != nil {
54 | fmt.Fprintf(w, err.Error())
55 | }
56 | json.Unmarshal(reqBody, &updatedItem)
57 |
58 | updatedItem.ID = id
59 |
60 | repo := createRepository()
61 | repo.UpdateItem(updatedItem)
62 |
63 | fmt.Fprintf(w, "OK")
64 | }
65 |
66 | func deleteItemEndpoint(w http.ResponseWriter, r *http.Request) {
67 | id, _ := strconv.Atoi(mux.Vars(r)["id"])
68 |
69 | repo := createRepository()
70 | repo.DeleteItem(id)
71 |
72 | fmt.Fprintf(w, "OK")
73 | }
74 |
75 | func createRepository() TodoRepository {
76 | if repositoryType == "Mongo" {
77 | return &MongoRepository{}
78 | } else {
79 | return &InMemoryRepository{}
80 | }
81 | }
82 |
83 | func main() {
84 | repositoryType = ""
85 |
86 | router := mux.NewRouter().StrictSlash(true)
87 |
88 | router.HandleFunc("/", homeEndpoint)
89 | router.HandleFunc("/api", createItemEndpoint).Methods("POST")
90 | router.HandleFunc("/api", getItemsEndpoint).Methods("GET")
91 | router.HandleFunc("/api/{id}", getItemEndpoint).Methods("GET")
92 | router.HandleFunc("/api/{id}", updateItemEndpoint).Methods("PATCH")
93 | router.HandleFunc("/api/{id}", deleteItemEndpoint).Methods("DELETE")
94 |
95 | log.Fatal(http.ListenAndServe(":8080", router))
96 | }
97 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.2/mongorepository.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 |
6 | "go.mongodb.org/mongo-driver/bson"
7 | "go.mongodb.org/mongo-driver/mongo"
8 | "go.mongodb.org/mongo-driver/mongo/options"
9 | )
10 |
11 | type MongoRepository struct {
12 | }
13 |
14 | func (MongoRepository) CreateItem(newItem Item) {
15 | ctx, client := connnect()
16 |
17 | collection := client.Database("todo").Collection("items")
18 | collection.InsertOne(ctx, newItem)
19 |
20 | disconnect(ctx, client)
21 | }
22 |
23 | func (MongoRepository) UpdateItem(item Item) {
24 | update := bson.M{"$set": bson.M{"title": item.Title, "isdone": item.IsDone}}
25 |
26 | ctx, client := connnect()
27 |
28 | collection := client.Database("todo").Collection("items")
29 | collection.UpdateOne(ctx, Item{ID: item.ID}, update)
30 |
31 | disconnect(ctx, client)
32 | }
33 |
34 | func (MongoRepository) GetItems() (items []Item) {
35 | ctx, client := connnect()
36 |
37 | collection := client.Database("todo").Collection("items")
38 | cursor, _ := collection.Find(ctx, bson.M{})
39 |
40 | defer cursor.Close(ctx)
41 | for cursor.Next(ctx) {
42 | var oneItem Item
43 | cursor.Decode(&oneItem)
44 | items = append(items, oneItem)
45 | }
46 |
47 | disconnect(ctx, client)
48 |
49 | return
50 | }
51 |
52 | func (MongoRepository) GetItem(id int) (item Item) {
53 | ctx, client := connnect()
54 |
55 | collection := client.Database("todo").Collection("items")
56 | collection.FindOne(ctx, Item{ID: id}).Decode(&item)
57 |
58 | disconnect(ctx, client)
59 |
60 | return
61 | }
62 |
63 | func (MongoRepository) DeleteItem(id int) {
64 | ctx, client := connnect()
65 |
66 | collection := client.Database("todo").Collection("items")
67 | collection.DeleteMany(ctx, Item{ID: id})
68 |
69 | disconnect(ctx, client)
70 |
71 | return
72 | }
73 |
74 | func connnect() (context.Context, *mongo.Client) {
75 | ctx := context.Background()
76 | client, _ := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
77 |
78 | return ctx, client
79 | }
80 |
81 | func disconnect(ctx context.Context, client *mongo.Client) {
82 | defer client.Disconnect(ctx)
83 | }
84 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.2/todorepository.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Item struct {
4 | ID int `json:"id,omitempty bson:"id,omitempty"`
5 | Title string `json:"title,omitempty" bson:"title,omitempty"`
6 | IsDone bool `json:"isdone,omitempty" bson:"isdone,omitempty"`
7 | }
8 |
9 | type TodoRepository interface {
10 | CreateItem(Item)
11 | UpdateItem(Item)
12 | GetItems() []Item
13 | GetItem(int) Item
14 | DeleteItem(int)
15 | }
16 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.3/README.md.org:
--------------------------------------------------------------------------------
1 | * Challenge 4.3
2 | :PROPERTIES:
3 | :CUSTOM_ID: challenge-4.3
4 | :END:
5 | Modificar la API Rest del challenge 4.2 para usar Gin.
6 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.3/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/rfinochi/golang-workshop-todo
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607
7 | github.com/gin-gonic/gin v1.6.3
8 | github.com/klauspost/compress v1.10.5 // indirect
9 | github.com/pkg/errors v0.9.1 // indirect
10 | github.com/stretchr/testify v1.5.1
11 | github.com/xdg/stringprep v1.0.0 // indirect
12 | go.mongodb.org/mongo-driver v1.3.3
13 | )
14 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.3/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | "os"
8 | "strconv"
9 |
10 | "github.com/gin-gonic/contrib/static"
11 | "github.com/gin-gonic/gin"
12 | )
13 |
14 | func main() {
15 | router := SetupRouter()
16 |
17 | port := os.Getenv("PORT")
18 | if port == "" {
19 | port = os.Getenv("HTTP_PLATFORM_PORT")
20 | if port == "" {
21 | port = "8080"
22 | log.Printf("Defaulting to port %s", port)
23 | }
24 | }
25 |
26 | router.Run(fmt.Sprintf(":%s", port))
27 | }
28 |
29 | func SetupRouter() *gin.Engine {
30 | router := gin.Default()
31 |
32 | router.Use(static.Serve("/", static.LocalFile("./views", true)))
33 |
34 | api := router.Group("/api")
35 | api.GET("/", getItemsEndpoint)
36 | api.GET("/:id", getItemEndpoint)
37 | api.POST("/", postItemEndpoint)
38 | api.PUT("/", putItemEndpoint)
39 | api.PATCH("/:id", updateItemEndpoint)
40 | api.DELETE("/:id", deleteItemEndpoint)
41 |
42 | return router
43 | }
44 |
45 | func postItemEndpoint(c *gin.Context) {
46 | var newItem Item
47 | c.BindJSON(&newItem)
48 |
49 | repo := createRepository()
50 | repo.CreateItem(newItem)
51 |
52 | c.Header("Content-Type", "application/json")
53 | c.JSON(http.StatusCreated, gin.H{"message": "OK"})
54 | }
55 |
56 | func putItemEndpoint(c *gin.Context) {
57 | var newItem Item
58 | c.BindJSON(&newItem)
59 |
60 | repo := createRepository()
61 | repo.CreateItem(newItem)
62 |
63 | c.Header("Content-Type", "application/json")
64 | c.JSON(http.StatusCreated, gin.H{"message": "OK"})
65 | }
66 |
67 | func getItemEndpoint(c *gin.Context) {
68 | id, _ := strconv.Atoi(c.Param("id"))
69 |
70 | repo := createRepository()
71 | c.Header("Content-Type", "application/json")
72 | c.JSON(http.StatusOK, repo.GetItem(id))
73 | }
74 |
75 | func getItemsEndpoint(c *gin.Context) {
76 | repo := createRepository()
77 | c.JSON(http.StatusOK, repo.GetItems())
78 | }
79 |
80 | func updateItemEndpoint(c *gin.Context) {
81 | id, _ := strconv.Atoi(c.Param("id"))
82 |
83 | var updatedItem Item
84 | c.BindJSON(&updatedItem)
85 |
86 | repo := createRepository()
87 | updatedItem.ID = id
88 | repo.UpdateItem(updatedItem)
89 |
90 | c.Header("Content-Type", "application/json")
91 | c.JSON(http.StatusOK, gin.H{"message": "OK"})
92 | }
93 |
94 | func deleteItemEndpoint(c *gin.Context) {
95 | id, _ := strconv.Atoi(c.Param("id"))
96 |
97 | repo := createRepository()
98 | repo.DeleteItem(id)
99 |
100 | c.Header("Content-Type", "application/json")
101 | c.JSON(http.StatusOK, gin.H{"message": "OK"})
102 | }
103 |
104 | func createRepository() TodoRepository {
105 | repositoryType := os.Getenv("REPOSITORY_TYPE")
106 |
107 | if repositoryType == "Mongo" {
108 | return &MongoRepository{}
109 | } else {
110 | return &MemoryRepository{}
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.3/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 | "net/http"
8 | "net/http/httptest"
9 | "os"
10 | "testing"
11 |
12 | "github.com/gin-gonic/gin"
13 | "github.com/stretchr/testify/assert"
14 | )
15 |
16 | var router *gin.Engine
17 |
18 | func init() {
19 | router = SetupRouter()
20 | }
21 |
22 | func TestCompleteApi(t *testing.T) {
23 | os.Setenv("REPOSITORY_TYPE", "Memory")
24 |
25 | doAllAPIRequests(t)
26 | }
27 |
28 | func doAllAPIRequests(t *testing.T) {
29 | doGetItems(router, t, "", true, 0)
30 | doGetItem(router, t, 0, "", false)
31 |
32 | doPostItem(router, t, "POST", `{"id":1,"title":"Test_1","isdone":true}`)
33 | doGetItems(router, t, "Test_1", true, 1)
34 | doGetItem(router, t, 1, "Test_1", true)
35 |
36 | doPostItem(router, t, "PUT", `{"id":2,"title":"Test_2","isdone":true}`)
37 | doGetItems(router, t, "Test_1", true, 2)
38 | doGetItem(router, t, 2, "Test_2", true)
39 |
40 | doDeleteItem(router, t, 2)
41 | doGetItems(router, t, "Test_1", true, 1)
42 |
43 | doPatchItem(router, t, 1, `{"id":1,"title":"Test_3","isdone":false}`)
44 | doGetItems(router, t, "Test_3", false, 1)
45 | doGetItem(router, t, 1, "Test_3", false)
46 | }
47 |
48 | func doPostItem(r http.Handler, t *testing.T, method string, payload string) {
49 | request := doRequest(r, method, "/api/", payload)
50 |
51 | assert.Equal(t, http.StatusCreated, request.Code)
52 |
53 | var response map[string]string
54 |
55 | err := json.Unmarshal([]byte(request.Body.String()), &response)
56 | value, exists := response["message"]
57 |
58 | assert.Nil(t, err)
59 | assert.True(t, exists)
60 | assert.Equal(t, "OK", value)
61 | }
62 |
63 | func doGetItem(r http.Handler, t *testing.T, id int, title string, isdone bool) {
64 | request := doRequest(r, "GET", fmt.Sprintf("/api/%v", id), "")
65 |
66 | assert.Equal(t, http.StatusOK, request.Code)
67 |
68 | var response Item
69 |
70 | err := json.Unmarshal([]byte(request.Body.String()), &response)
71 |
72 | assert.Nil(t, err)
73 | assert.Equal(t, response.ID, id)
74 | assert.Equal(t, response.Title, title)
75 | assert.Equal(t, response.IsDone, isdone)
76 | }
77 |
78 | func doGetItems(r http.Handler, t *testing.T, title string, isdone bool, length int) {
79 | request := doRequest(r, "GET", "/api/", "")
80 |
81 | assert.Equal(t, http.StatusOK, request.Code)
82 |
83 | var response []Item
84 |
85 | err := json.Unmarshal([]byte(request.Body.String()), &response)
86 |
87 | assert.Nil(t, err)
88 | assert.Equal(t, len(response), length)
89 | if length > 0 {
90 | assert.Equal(t, response[0].ID, 1)
91 | assert.Equal(t, response[0].Title, title)
92 | assert.Equal(t, response[0].IsDone, isdone)
93 | }
94 | }
95 |
96 | func doDeleteItem(r http.Handler, t *testing.T, id int) {
97 | request := doRequest(r, "DELETE", fmt.Sprintf("/api/%v", id), "")
98 |
99 | assert.Equal(t, http.StatusOK, request.Code)
100 |
101 | var response map[string]string
102 |
103 | err := json.Unmarshal([]byte(request.Body.String()), &response)
104 | value, exists := response["message"]
105 |
106 | assert.Nil(t, err)
107 | assert.True(t, exists)
108 | assert.Equal(t, "OK", value)
109 | }
110 |
111 | func doPatchItem(r http.Handler, t *testing.T, id int, payload string) {
112 | request := doRequest(r, "PATCH", fmt.Sprintf("/api/%v", id), payload)
113 |
114 | assert.Equal(t, http.StatusOK, request.Code)
115 |
116 | var response map[string]string
117 |
118 | err := json.Unmarshal([]byte(request.Body.String()), &response)
119 | value, exists := response["message"]
120 |
121 | assert.Nil(t, err)
122 | assert.True(t, exists)
123 | assert.Equal(t, "OK", value)
124 | }
125 |
126 | func doRequest(r http.Handler, method string, path string, payload string) *httptest.ResponseRecorder {
127 | var req *http.Request
128 |
129 | if method == "POST" || method == "PATCH" || method == "PUT" {
130 | req, _ = http.NewRequest(method, path, bytes.NewBuffer([]byte(payload)))
131 | req.Header.Set("Content-Type", "application/json")
132 | } else {
133 | req, _ = http.NewRequest(method, path, nil)
134 | }
135 |
136 | w := httptest.NewRecorder()
137 |
138 | r.ServeHTTP(w, req)
139 |
140 | return w
141 | }
142 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.3/memoryrepository.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | var items = []Item{}
4 |
5 | type MemoryRepository struct {
6 | }
7 |
8 | func (MemoryRepository) CreateItem(newItem Item) {
9 | items = append(items, newItem)
10 | }
11 |
12 | func (MemoryRepository) UpdateItem(updatedItem Item) {
13 | for i, item := range items {
14 | if item.ID == updatedItem.ID {
15 | item.Title = updatedItem.Title
16 | item.IsDone = updatedItem.IsDone
17 | items = append(items[:i], item)
18 | }
19 | }
20 | }
21 |
22 | func (MemoryRepository) GetItems() []Item {
23 | return items
24 | }
25 |
26 | func (MemoryRepository) GetItem(id int) Item {
27 | var result Item
28 |
29 | for _, item := range items {
30 | if item.ID == id {
31 | result = item
32 | break
33 | }
34 | }
35 |
36 | return result
37 | }
38 |
39 | func (MemoryRepository) DeleteItem(id int) {
40 | for i, item := range items {
41 | if item.ID == id {
42 | items = append(items[:i], items[i+1:]...)
43 | break
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.3/mongorepository.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "os"
6 |
7 | "go.mongodb.org/mongo-driver/bson"
8 | "go.mongodb.org/mongo-driver/mongo"
9 | "go.mongodb.org/mongo-driver/mongo/options"
10 | )
11 |
12 | var uri string
13 |
14 | type MongoRepository struct {
15 | }
16 |
17 | func init() {
18 | var ok bool
19 |
20 | if uri, ok = os.LookupEnv("MONGO_URI"); !ok {
21 | uri = "mongodb://localhost:27017"
22 | }
23 | }
24 |
25 | func (MongoRepository) CreateItem(newItem Item) {
26 | ctx, client := connnect()
27 |
28 | collection := client.Database("todo").Collection("items")
29 | collection.InsertOne(ctx, newItem)
30 |
31 | disconnect(ctx, client)
32 | }
33 |
34 | func (MongoRepository) UpdateItem(item Item) {
35 | update := bson.M{"$set": bson.M{"title": item.Title, "isdone": item.IsDone}}
36 |
37 | ctx, client := connnect()
38 |
39 | collection := client.Database("todo").Collection("items")
40 | collection.UpdateOne(ctx, Item{ID: item.ID}, update)
41 |
42 | disconnect(ctx, client)
43 | }
44 |
45 | func (MongoRepository) GetItems() (items []Item) {
46 | ctx, client := connnect()
47 |
48 | collection := client.Database("todo").Collection("items")
49 | cursor, _ := collection.Find(ctx, bson.M{})
50 |
51 | defer cursor.Close(ctx)
52 | for cursor.Next(ctx) {
53 | var oneItem Item
54 | cursor.Decode(&oneItem)
55 | items = append(items, oneItem)
56 | }
57 |
58 | disconnect(ctx, client)
59 |
60 | return
61 | }
62 |
63 | func (MongoRepository) GetItem(id int) (item Item) {
64 | ctx, client := connnect()
65 |
66 | collection := client.Database("todo").Collection("items")
67 | collection.FindOne(ctx, Item{ID: id}).Decode(&item)
68 |
69 | disconnect(ctx, client)
70 |
71 | return
72 | }
73 |
74 | func (MongoRepository) DeleteItem(id int) {
75 | ctx, client := connnect()
76 |
77 | collection := client.Database("todo").Collection("items")
78 | collection.DeleteMany(ctx, Item{ID: id})
79 |
80 | disconnect(ctx, client)
81 | }
82 |
83 | func connnect() (context.Context, *mongo.Client) {
84 | ctx := context.Background()
85 | client, _ := mongo.Connect(ctx, options.Client().ApplyURI(uri))
86 |
87 | return ctx, client
88 | }
89 |
90 | func disconnect(ctx context.Context, client *mongo.Client) {
91 | defer client.Disconnect(ctx)
92 | }
93 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.3/todorepository.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // Item godoc
4 | type Item struct {
5 | ID int `json:"id,omitempty bson:"id,omitempty" datastore:"id"`
6 | Title string `json:"title,omitempty" bson:"title,omitempty" datastore:"title"`
7 | IsDone bool `json:"isdone,omitempty" bson:"isdone,omitempty" datastore:"isdone"`
8 | }
9 |
10 | // TodoRepository godoc
11 | type TodoRepository interface {
12 | CreateItem(Item)
13 | UpdateItem(Item)
14 | GetItems() []Item
15 | GetItem(int) Item
16 | DeleteItem(int)
17 | }
18 |
--------------------------------------------------------------------------------
/day-05/challenge4/4.3/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sample To-Do Web API
5 |
6 |
7 |
8 | Sample To-Do Web API
9 | API
10 | API Doc
11 |
12 |
13 |
--------------------------------------------------------------------------------
/day-06/19-dbaccess/mongo/dbaccess.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 |
6 | "go.mongodb.org/mongo-driver/bson"
7 | "go.mongodb.org/mongo-driver/bson/primitive"
8 | "go.mongodb.org/mongo-driver/mongo"
9 | "go.mongodb.org/mongo-driver/mongo/options"
10 | )
11 |
12 | type Item struct {
13 | ID int `json:"id,omitempty" bson:"id,omitempty"`
14 | Title string `json:"title,omitempty" bson:"title,omitempty"`
15 | IsDone bool `json:"isdone,omitempty" bson:"isdone,omitempty"`
16 | }
17 |
18 | type MongoDB struct {
19 | *mongo.Client
20 | }
21 |
22 | func NewMongoDB(ctx context.Context) (*MongoDB, error) {
23 | client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
24 | if err != nil {
25 | return nil, err
26 | }
27 | return &MongoDB{client}, nil
28 | }
29 |
30 | func (m *MongoDB) Disconnect(ctx context.Context) {
31 | defer m.Client.Disconnect(ctx)
32 | }
33 |
34 | func (m *MongoDB) CreateItem(ctx context.Context, newItem Item) string {
35 |
36 | collection := m.Database("todo").Collection("items")
37 | result, _ := collection.InsertOne(ctx, newItem)
38 |
39 | return result.InsertedID.(primitive.ObjectID).Hex()
40 | }
41 |
42 | func (m *MongoDB) UpdateItem(ctx context.Context, item Item) {
43 | update := bson.M{"$set": bson.M{"title": item.Title, "isdone": item.IsDone}}
44 |
45 | collection := m.Database("todo").Collection("items")
46 | collection.UpdateOne(ctx, Item{ID: item.ID}, update)
47 | }
48 |
49 | func (m *MongoDB) GetItems(ctx context.Context) (items []Item) {
50 | collection := m.Database("todo").Collection("items")
51 | cursor, _ := collection.Find(ctx, bson.M{})
52 |
53 | defer cursor.Close(ctx)
54 | for cursor.Next(ctx) {
55 | var oneItem Item
56 | cursor.Decode(&oneItem)
57 | items = append(items, oneItem)
58 | }
59 |
60 | return
61 | }
62 |
63 | func (m *MongoDB) GetItem(ctx context.Context, id int) (item Item) {
64 |
65 | collection := m.Database("todo").Collection("items")
66 | collection.FindOne(ctx, Item{ID: id}).Decode(&item)
67 | return
68 | }
69 |
70 | func (m *MongoDB) DeleteItem(ctx context.Context, id int) {
71 | collection := m.Database("todo").Collection("items")
72 | collection.DeleteMany(ctx, Item{ID: id})
73 | return
74 | }
75 |
--------------------------------------------------------------------------------
/day-06/19-dbaccess/mongo/go.mod:
--------------------------------------------------------------------------------
1 | module sample19mongo
2 |
3 | go 1.15
4 |
5 | require go.mongodb.org/mongo-driver v1.4.5
6 |
--------------------------------------------------------------------------------
/day-06/19-dbaccess/mongo/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | )
8 |
9 | func main() {
10 | item := Item{1, "Test_1", false}
11 | ctx := context.TODO()
12 | m, err := NewMongoDB(ctx)
13 | if err != nil {
14 | log.Fatalf("Got error from MongoDB %s", err)
15 | }
16 | defer m.Disconnect(ctx)
17 | result := m.CreateItem(ctx, item)
18 | fmt.Println(result)
19 |
20 | items := m.GetItems(ctx)
21 | fmt.Println(items)
22 |
23 | fmt.Println(m.GetItem(ctx, 1))
24 |
25 | item.Title = "Test_1U"
26 | item.IsDone = true
27 |
28 | m.UpdateItem(ctx, item)
29 | fmt.Println(m.GetItem(ctx, 1))
30 |
31 | m.DeleteItem(ctx, 1)
32 | fmt.Println(m.GetItem(ctx, 1))
33 | }
34 |
--------------------------------------------------------------------------------
/day-06/19-dbaccess/sqlite/data.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/day-06/19-dbaccess/sqlite/data.db
--------------------------------------------------------------------------------
/day-06/19-dbaccess/sqlite/go.mod:
--------------------------------------------------------------------------------
1 | module sample19
2 |
3 | go 1.15
4 |
5 | require github.com/mattn/go-sqlite3 v1.14.6
6 |
--------------------------------------------------------------------------------
/day-06/19-dbaccess/sqlite/go.sum:
--------------------------------------------------------------------------------
1 | github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
2 | github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
3 |
--------------------------------------------------------------------------------
/day-06/19-dbaccess/sqlite/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "fmt"
6 | _ "github.com/mattn/go-sqlite3"
7 | "time"
8 | )
9 |
10 | func main() {
11 | db, err := sql.Open("sqlite3", "./data.db")
12 | checkErr(err)
13 |
14 | defer db.Close() // Cerrar la base de datos siempre
15 | // Insertar
16 | stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) values(?,?,?)")
17 | checkErr(err)
18 |
19 | res, err := stmt.Exec("pparker", "Avengers", "2021-01-01")
20 | checkErr(err)
21 |
22 | id, err := res.LastInsertId()
23 | checkErr(err)
24 |
25 | fmt.Println(id)
26 | // Actualizar
27 | stmt, err = db.Prepare("update userinfo set username=? where uid=?")
28 | checkErr(err)
29 |
30 | res, err = stmt.Exec("spiderman", id)
31 | checkErr(err)
32 |
33 | affect, err := res.RowsAffected()
34 | checkErr(err)
35 |
36 | fmt.Println(affect)
37 |
38 | // Consultar
39 | rows, err := db.Query("SELECT * FROM userinfo")
40 | checkErr(err)
41 | var uid int
42 | var username string
43 | var department string
44 | var created time.Time
45 |
46 | for rows.Next() {
47 | err = rows.Scan(&uid, &username, &department, &created)
48 | checkErr(err)
49 | fmt.Println(uid)
50 | fmt.Println(username)
51 | fmt.Println(department)
52 | fmt.Println(created)
53 | }
54 |
55 | rows.Close() // Close libera recursos del iterador
56 |
57 | // Eliminar
58 | stmt, err = db.Prepare("delete from userinfo where uid=?")
59 | checkErr(err)
60 |
61 | res, err = stmt.Exec(id)
62 | checkErr(err)
63 |
64 | affect, err = res.RowsAffected()
65 | checkErr(err)
66 |
67 | fmt.Println(affect)
68 | }
69 |
70 | func checkErr(err error) {
71 | if err != nil {
72 | panic(err)
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/day-06/20-echo/README.md.org:
--------------------------------------------------------------------------------
1 | * Labstack Echo
2 | :PROPERTIES:
3 | :CUSTOM_ID: labstack-echo
4 | :END:
5 |
6 | ** Introducción
7 |
8 | *Echo* es un microframework Golang para crear servicios web.
9 |
10 | ** Rutas.
11 |
12 | :PROPERTIES:
13 | :CUSTOM_ID: rutas-middleware
14 | :END:
15 |
16 |
17 | *** Handlers
18 |
19 | =echo.Context= representa el acceso al estado de la solicitud (ruta,
20 | parámetros, /handlers/, etc) y contiene los métodos para generar las
21 | respuesta
22 |
23 | #+begin_src go
24 | func updateUser (c echo.Context) (err error) {
25 | // ...
26 | }
27 | #+end_src
28 |
29 | *** Rutas REST
30 | #+begin_src go
31 | import "github.com/labstack/echo/v4"
32 |
33 | func main(){
34 | e := echo.New()
35 | e.POST("/users", createUser)
36 | e.GET("/users/:id", findUser)
37 | e.PUT("/users/:id", updateUser)
38 | e.DELETE("/users/:id", deleteUser)
39 | e.Any("/",home)
40 | }
41 | #+end_src
42 |
43 | *** Grupos
44 |
45 | Establecer opciones similares para varias rutas.
46 | #+begin_src go
47 | //Todas las URLs con /v2/*
48 | g := e.Group("/v2")
49 | e.POST("/users", createUserV2)
50 | e.GET("/users/:id", findUserV2)
51 | e.PUT("/users/:id", updateUserV2)
52 | e.DELETE("/users/:id", deleteUserV2)
53 | #+end_src
54 |
55 | ** Middleware
56 |
57 | Funciones que se procesan antes de un handler.
58 |
59 | #+begin_src go
60 | func MyMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
61 | return func(c echo.Context) error {
62 | //Hacer algo
63 | return next(c)
64 | }
65 | }
66 | #+end_src
67 |
68 | *** Incluyendo middleware
69 |
70 | #+begin_src go
71 | //Antes de ejecutarse el router
72 | e.Pre(MyMiddleware)
73 | //Después de ejecutar el router
74 | e.Use(MyMiddleware)
75 | // A nivel de grupo
76 | admin := e.Group("/admin", MyMiddleware)
77 | //A nivel de ruta
78 | e.GET("/", , )
79 | #+end_src
80 |
81 | ** Obteniendo datos
82 |
83 | *** Desde el contexto
84 |
85 | #+begin_src go
86 | func(c echo.Context) error {
87 | name := c.FormValue("name") // valor de formulario
88 | printFull := c.QueryParam("full") // valor de query
89 | return c.String(http.StatusOK, name)
90 | }
91 | #+end_src
92 |
93 | *** Usando =Bind=
94 |
95 | #+begin_src go
96 | type User struct {
97 | Name string `json:"name" form:"name" query:"name"`
98 | Email string `json:"email" form:"email" query:"email"`
99 | }
100 | func handle(c echo.Context) (err error) {
101 | u := new(User)
102 | if err = c.Bind(u); err != nil {
103 | return
104 | }
105 | // Hacer algo con el usuario
106 | }
107 | #+end_src
108 |
109 | ** Respuestas
110 | :PROPERTIES:
111 | :CUSTOM_ID: echo-response
112 | :END:
113 |
114 | =echo.Context= es también utilizada para generar respuestas.
115 |
116 | #+begin_src go
117 | // Retornar una cadena
118 | c.String(http.StatusOK, "Hello, World!")
119 | // Retornar HTML
120 | c.HTML(http.StatusOK, "Hello, World!
")
121 | // Retorna JSON, serializa el valor de u
122 | c.JSON(http.StatusOK, u)
123 | // Retorna XML, serializa el valor de u
124 | c.XML(http.StatusOK, u)
125 | // Retorna el contenido del fichero
126 | c.File("")
127 | // Retorna el contenido del fichero como flujo de datos
128 | c.Stream(http.StatusOK, "", file)
129 | // Redirige
130 | c.Redirect(http.StatusMovedPermanently, "")
131 | #+end_src
132 |
--------------------------------------------------------------------------------
/day-06/20-echo/echo/cmd/litter/litter.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | "sample20/internal/handler"
6 |
7 | "github.com/labstack/echo/v4"
8 | "github.com/labstack/echo/v4/middleware"
9 | "github.com/labstack/gommon/log"
10 | "gopkg.in/mgo.v2"
11 | )
12 |
13 | func main() {
14 |
15 | e := echo.New()
16 | e.Logger.SetLevel(log.ERROR)
17 | key := os.Getenv("LITTER_SECRET")
18 | if key == "" {
19 | e.Logger.Fatal("Cannot start without secret key")
20 | }
21 | e.Use(middleware.Logger())
22 | e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
23 | SigningKey: []byte(key),
24 | Skipper: func(c echo.Context) bool {
25 | // Skip authentication for signup and login requests
26 | if c.Path() == "/login" || c.Path() == "/signup" {
27 | return true
28 | }
29 | return false
30 | },
31 | }))
32 |
33 | // Database connection
34 | db, err := mgo.Dial("localhost")
35 | if err != nil {
36 | e.Logger.Fatal(err)
37 | }
38 |
39 | // Create indices
40 | if err = db.Copy().DB("twitter").C("users").EnsureIndex(mgo.Index{
41 | Key: []string{"email"},
42 | Unique: true,
43 | }); err != nil {
44 | log.Fatal(err)
45 | }
46 |
47 | // Initialize handler
48 | h := &handler.Handler{DB: db, Key: key}
49 |
50 | // Routes
51 | e.POST("/signup", h.Signup)
52 | e.POST("/login", h.Login)
53 | e.POST("/follow/:id", h.Follow)
54 | e.POST("/posts", h.CreatePost)
55 | e.GET("/feed", h.FetchPost)
56 |
57 | // Start server
58 | e.Logger.Fatal(e.Start(":1323"))
59 | }
60 |
--------------------------------------------------------------------------------
/day-06/20-echo/echo/go.mod:
--------------------------------------------------------------------------------
1 | module sample20
2 |
3 | go 1.15
4 |
5 | require (
6 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
7 | github.com/labstack/echo/v4 v4.1.17
8 | github.com/labstack/gommon v0.3.0
9 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
10 | )
11 |
--------------------------------------------------------------------------------
/day-06/20-echo/echo/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
4 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
5 | github.com/labstack/echo/v4 v4.1.17 h1:PQIBaRplyRy3OjwILGkPg89JRtH2x5bssi59G2EL3fo=
6 | github.com/labstack/echo/v4 v4.1.17/go.mod h1:Tn2yRQL/UclUalpb5rPdXDevbkJ+lp/2svdyFBg6CHQ=
7 | github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
8 | github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
9 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
10 | github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
11 | github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
12 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
13 | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
14 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
15 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
16 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
17 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
18 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
19 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
20 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
21 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
22 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
23 | github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
24 | github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
25 | github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
26 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
27 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
28 | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
29 | golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
30 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
31 | golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
32 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
33 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
34 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
35 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
36 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
37 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
38 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
39 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
40 | golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8=
41 | golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
42 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
43 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
44 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
45 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
46 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
47 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
48 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
49 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
50 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
51 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
52 |
--------------------------------------------------------------------------------
/day-06/20-echo/echo/internal/handler/handler.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "gopkg.in/mgo.v2"
5 | )
6 |
7 | type (
8 | Handler struct {
9 | DB *mgo.Session
10 | Key string
11 | }
12 | )
13 |
--------------------------------------------------------------------------------
/day-06/20-echo/echo/internal/handler/post.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "net/http"
5 | "sample20/internal/model"
6 | "strconv"
7 |
8 | "github.com/labstack/echo/v4"
9 | "gopkg.in/mgo.v2"
10 | "gopkg.in/mgo.v2/bson"
11 | )
12 |
13 | func (h *Handler) CreatePost(c echo.Context) (err error) {
14 | u := &model.User{
15 | ID: bson.ObjectIdHex(userIDFromToken(c)),
16 | }
17 | p := &model.Post{
18 | ID: bson.NewObjectId(),
19 | From: u.ID.Hex(),
20 | }
21 | if err = c.Bind(p); err != nil {
22 | return
23 | }
24 |
25 | // Validation
26 | if p.To == "" || p.Message == "" {
27 | return &echo.HTTPError{Code: http.StatusBadRequest, Message: "invalid to or message fields"}
28 | }
29 |
30 | // Find user from database
31 | db := h.DB.Clone()
32 | defer db.Close()
33 | if err = db.DB("twitter").C("users").FindId(u.ID).One(u); err != nil {
34 | if err == mgo.ErrNotFound {
35 | return echo.ErrNotFound
36 | }
37 | return
38 | }
39 |
40 | // Save post in database
41 | if err = db.DB("twitter").C("posts").Insert(p); err != nil {
42 | return
43 | }
44 | return c.JSON(http.StatusCreated, p)
45 | }
46 |
47 | func (h *Handler) FetchPost(c echo.Context) (err error) {
48 | userID := userIDFromToken(c)
49 | page, _ := strconv.Atoi(c.QueryParam("page"))
50 | limit, _ := strconv.Atoi(c.QueryParam("limit"))
51 |
52 | // Defaults
53 | if page == 0 {
54 | page = 1
55 | }
56 | if limit == 0 {
57 | limit = 100
58 | }
59 |
60 | // Retrieve posts from database
61 | posts := []*model.Post{}
62 | db := h.DB.Clone()
63 | if err = db.DB("twitter").C("posts").
64 | Find(bson.M{"to": userID}).
65 | Skip((page - 1) * limit).
66 | Limit(limit).
67 | All(&posts); err != nil {
68 | return
69 | }
70 | defer db.Close()
71 |
72 | return c.JSON(http.StatusOK, posts)
73 | }
74 |
--------------------------------------------------------------------------------
/day-06/20-echo/echo/internal/handler/user.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "net/http"
5 | "time"
6 |
7 | "github.com/dgrijalva/jwt-go"
8 | "github.com/labstack/echo/v4"
9 | "gopkg.in/mgo.v2"
10 | "gopkg.in/mgo.v2/bson"
11 | "sample20/internal/model"
12 | )
13 |
14 | func (h *Handler) Signup(c echo.Context) (err error) {
15 | // Bind
16 | u := &model.User{ID: bson.NewObjectId()}
17 | if err = c.Bind(u); err != nil {
18 | return
19 | }
20 |
21 | // Validate
22 | if u.Email == "" || u.Password == "" {
23 | return &echo.HTTPError{Code: http.StatusBadRequest, Message: "invalid email or password"}
24 | }
25 |
26 | // Save user
27 | db := h.DB.Clone()
28 | defer db.Close()
29 | if err = db.DB("twitter").C("users").Insert(u); err != nil {
30 | return
31 | }
32 |
33 | return c.JSON(http.StatusCreated, u)
34 | }
35 |
36 | func (h *Handler) Login(c echo.Context) (err error) {
37 | // Bind
38 | u := new(model.User)
39 | if err = c.Bind(u); err != nil {
40 | return
41 | }
42 |
43 | // Find user
44 | db := h.DB.Clone()
45 | defer db.Close()
46 | if err = db.DB("twitter").C("users").
47 | Find(bson.M{"email": u.Email, "password": u.Password}).One(u); err != nil {
48 | if err == mgo.ErrNotFound {
49 | return &echo.HTTPError{Code: http.StatusUnauthorized, Message: "invalid email or password"}
50 | }
51 | return
52 | }
53 |
54 | //-----
55 | // JWT
56 | //-----
57 |
58 | // Create token
59 | token := jwt.New(jwt.SigningMethodHS256)
60 |
61 | // Set claims
62 | claims := token.Claims.(jwt.MapClaims)
63 | claims["id"] = u.ID
64 | claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
65 |
66 | // Generate encoded token and send it as response
67 | u.Token, err = token.SignedString([]byte(h.Key))
68 | if err != nil {
69 | return err
70 | }
71 |
72 | u.Password = "" // Don't send password
73 | return c.JSON(http.StatusOK, u)
74 | }
75 |
76 | func (h *Handler) Follow(c echo.Context) (err error) {
77 | userID := userIDFromToken(c)
78 | id := c.Param("id")
79 |
80 | // Add a follower to user
81 | db := h.DB.Clone()
82 | defer db.Close()
83 | if err = db.DB("twitter").C("users").
84 | UpdateId(bson.ObjectIdHex(id), bson.M{"$addToSet": bson.M{"followers": userID}}); err != nil {
85 | if err == mgo.ErrNotFound {
86 | return echo.ErrNotFound
87 | }
88 | }
89 |
90 | return
91 | }
92 |
93 | func userIDFromToken(c echo.Context) string {
94 | user := c.Get("user").(*jwt.Token)
95 | claims := user.Claims.(jwt.MapClaims)
96 | return claims["id"].(string)
97 | }
98 |
--------------------------------------------------------------------------------
/day-06/20-echo/echo/internal/model/post.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "gopkg.in/mgo.v2/bson"
5 | )
6 |
7 | type (
8 | Post struct {
9 | ID bson.ObjectId `json:"id" bson:"_id,omitempty"`
10 | To string `json:"to" bson:"to"`
11 | From string `json:"from" bson:"from"`
12 | Message string `json:"message" bson:"message"`
13 | }
14 | )
15 |
--------------------------------------------------------------------------------
/day-06/20-echo/echo/internal/model/user.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "gopkg.in/mgo.v2/bson"
5 | )
6 |
7 | type (
8 | User struct {
9 | ID bson.ObjectId `json:"id" bson:"_id,omitempty"`
10 | Email string `json:"email" bson:"email"`
11 | Password string `json:"password,omitempty" bson:"password"`
12 | Token string `json:"token,omitempty" bson:"-"`
13 | Followers []string `json:"followers,omitempty" bson:"followers,omitempty"`
14 | }
15 | )
16 |
--------------------------------------------------------------------------------
/day-07/21-sarama/README.md.org:
--------------------------------------------------------------------------------
1 | * Golang y Kafka
2 |
3 | ** Enviando mensajes con =sarama=
4 |
5 | =sarama= es una biblioteca desarrollada por Shopify para la
6 | comunicación con Apache Kafka
7 |
8 | Para utilizar =sarama= solo tenemos que importar el módulo
9 |
10 | #+begin_src go
11 | import "github.com/Shopify/sarama"
12 | #+end_src
13 |
14 | *** Configuración
15 |
16 | #+begin_src go
17 | config := sarama.NewConfig()
18 | config.Producer.RequiredAcks = sarama.WaitForAll // Wait for all in-sync replicas to ack the message
19 | config.Producer.Retry.Max = 10 // Retry up to 10 times to produce the message
20 | config.Producer.Return.Successes = true
21 | tlsConfig := createTlsConfiguration()
22 | if tlsConfig != nil {
23 | config.Net.TLS.Config = tlsConfig // de crypto/tls
24 | config.Net.TLS.Enable = true
25 | }
26 | #+end_src
27 |
28 | *** Enviando mensajes síncronos
29 |
30 | =sarama.SyncProducer= bloquea la gorutina en espera de confirmación
31 |
32 | #+begin_src go
33 | brokerlist := [...]string{"host1:por1", "host2:port2"}
34 | producer := sarama.NewSyncProducer(brokerList, config)
35 | partition, offset, err := s.DataCollector.SendMessage(&sarama.ProducerMessage{
36 | Key: sarama.StringEncoder("llave"),
37 | Topic: "topic",
38 | Value: sarama.StringEncoder("Mensaje"),
39 | })
40 | producer.Close() //Liberar recursos
41 | #+end_src
42 |
43 | *** Enviando mensajes asíncronos.
44 |
45 | =sarama.AsyncProducer= envía de forma asíncrona.
46 |
47 | #+begin_src go
48 | brokerlist := [...]string{"host1:por1", "host2:port2"}
49 | producer, err := sarama.NewAsyncProducer(brokerList, config)
50 | producer.Input() <- &sarama.ProducerMessage{
51 | Key: sarama.StringEncoder("llave"),
52 | Topic: "topic",
53 | Value: sarama.StringEncoder("Mensaje"),,
54 | }
55 | producer.AsyncClose() // Liberar recursos
56 | #+end_src
57 |
58 | ** Recibiendo mensajes con =sarama=
59 |
60 | =sarama= permite dos modos de recepción de mensajes.
61 |
62 | 1. Obtener mensajes desde una sola partición utilizando =sarama.Consumer=
63 | 2. Obtener mensajes de un clúster con =sarama.ConsumerGroup=
64 |
65 | En la carpeta =day-07/21-sarama/consumergroup= se incluye un ejemplo
66 | de consumidor usando =sarama.ConsumerGroup=
67 |
68 | ** Interceptors
69 |
70 | Los /interceptors/ permiten procesar un mensaje antes de que sea
71 | enviado o recibido.
72 |
73 | *** Interceptando mensajes enviados
74 |
75 | #+begin_src go
76 | //
77 | func (m *myinterp) OnSend(p *sarama.ProducerMessage) {
78 | // se procesa el mensaje
79 | }
80 | // Configuración del productor
81 | conf.Producer.Interceptors = []sarama.ProducerInterceptor{&myinterp{}}
82 | #+end_src
83 |
84 | *** Interceptando mensajes recibidos.
85 |
86 | #+begin_src go
87 | // Implementar sarama.ConsumerInterceptor
88 | func (m *myinterp) OnConsume(p *sarama.ConsumerMessage) {
89 | // se procesa el mensaje
90 | }
91 | // Configuración del consumidor
92 | conf.Consumer.Interceptors = []sarama.ConsumerInterceptor{&myinterp{}}
93 | #+end_src
94 |
--------------------------------------------------------------------------------
/day-07/21-sarama/consumergroup/README.md:
--------------------------------------------------------------------------------
1 | # Consumergroup example
2 |
3 | This example shows you how to use the Sarama consumer group consumer. The example simply starts consuming the given Kafka topics and logs the consumed messages.
4 |
5 | ```bash
6 | $ go run main.go -brokers="127.0.0.1:9092" -topics="sarama" -group="example"
7 | ```
--------------------------------------------------------------------------------
/day-07/21-sarama/consumergroup/go.mod:
--------------------------------------------------------------------------------
1 | module sample21
2 |
3 | go 1.15
4 |
5 | require github.com/Shopify/sarama v1.27.0
6 |
--------------------------------------------------------------------------------
/day-07/21-sarama/http_server/.gitignore:
--------------------------------------------------------------------------------
1 | http_server
2 | http_server.test
3 |
--------------------------------------------------------------------------------
/day-07/21-sarama/http_server/README.md:
--------------------------------------------------------------------------------
1 | # HTTP server example
2 |
3 | This HTTP server example shows you how to use the AsyncProducer and SyncProducer, and how to test them using mocks. The server simply sends the data of the HTTP request's query string to Kafka, and send a 200 result if that succeeds. For every request, it will send an access log entry to Kafka as well in the background.
4 |
5 | If you need to know whether a message was successfully sent to the Kafka cluster before you can send your HTTP response, using the `SyncProducer` is probably the simplest way to achieve this. If you don't care, e.g. for the access log, using the `AsyncProducer` will let you fire and forget. You can send the HTTP response, while the message is being produced in the background.
6 |
7 | One important thing to note is that both the `SyncProducer` and `AsyncProducer` are **thread-safe**. Go's `http.Server` handles requests concurrently in different goroutines, but you can use a single producer safely. This will actually achieve efficiency gains as the producer will be able to batch messages from concurrent requests together.
8 |
--------------------------------------------------------------------------------
/day-07/21-sarama/http_server/go.mod:
--------------------------------------------------------------------------------
1 | module http_server
2 |
3 | go 1.15
4 |
5 | require github.com/Shopify/sarama v1.27.2
6 |
--------------------------------------------------------------------------------
/day-07/21-sarama/http_server/http_server_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "io"
5 | "net/http"
6 | "net/http/httptest"
7 | "testing"
8 |
9 | "github.com/Shopify/sarama"
10 | "github.com/Shopify/sarama/mocks"
11 | )
12 |
13 | // In normal operation, we expect one access log entry,
14 | // and one data collector entry. Let's assume both will succeed.
15 | // We should return a HTTP 200 status.
16 | func TestCollectSuccessfully(t *testing.T) {
17 | dataCollectorMock := mocks.NewSyncProducer(t, nil)
18 | dataCollectorMock.ExpectSendMessageAndSucceed()
19 |
20 | accessLogProducerMock := mocks.NewAsyncProducer(t, nil)
21 | accessLogProducerMock.ExpectInputAndSucceed()
22 |
23 | // Now, use dependency injection to use the mocks.
24 | s := &Server{
25 | DataCollector: dataCollectorMock,
26 | AccessLogProducer: accessLogProducerMock,
27 | }
28 |
29 | // The Server's Close call is important; it will call Close on
30 | // the two mock producers, which will then validate whether all
31 | // expectations are resolved.
32 | defer safeClose(t, s)
33 |
34 | req, err := http.NewRequest("GET", "http://example.com/?data", nil)
35 | if err != nil {
36 | t.Fatal(err)
37 | }
38 | res := httptest.NewRecorder()
39 | s.Handler().ServeHTTP(res, req)
40 |
41 | if res.Code != 200 {
42 | t.Errorf("Expected HTTP status 200, found %d", res.Code)
43 | }
44 |
45 | if string(res.Body.Bytes()) != "Your data is stored with unique identifier important/0/1" {
46 | t.Error("Unexpected response body", res.Body)
47 | }
48 | }
49 |
50 | // Now, let's see if we handle the case of not being able to produce
51 | // to the data collector properly. In this case we should return a 500 status.
52 | func TestCollectionFailure(t *testing.T) {
53 | dataCollectorMock := mocks.NewSyncProducer(t, nil)
54 | dataCollectorMock.ExpectSendMessageAndFail(sarama.ErrRequestTimedOut)
55 |
56 | accessLogProducerMock := mocks.NewAsyncProducer(t, nil)
57 | accessLogProducerMock.ExpectInputAndSucceed()
58 |
59 | s := &Server{
60 | DataCollector: dataCollectorMock,
61 | AccessLogProducer: accessLogProducerMock,
62 | }
63 | defer safeClose(t, s)
64 |
65 | req, err := http.NewRequest("GET", "http://example.com/?data", nil)
66 | if err != nil {
67 | t.Fatal(err)
68 | }
69 | res := httptest.NewRecorder()
70 | s.Handler().ServeHTTP(res, req)
71 |
72 | if res.Code != 500 {
73 | t.Errorf("Expected HTTP status 500, found %d", res.Code)
74 | }
75 | }
76 |
77 | // We don't expect any data collector calls because the path is wrong,
78 | // so we are not setting any expectations on the dataCollectorMock. It
79 | // will still generate an access log entry though.
80 | func TestWrongPath(t *testing.T) {
81 | dataCollectorMock := mocks.NewSyncProducer(t, nil)
82 |
83 | accessLogProducerMock := mocks.NewAsyncProducer(t, nil)
84 | accessLogProducerMock.ExpectInputAndSucceed()
85 |
86 | s := &Server{
87 | DataCollector: dataCollectorMock,
88 | AccessLogProducer: accessLogProducerMock,
89 | }
90 | defer safeClose(t, s)
91 |
92 | req, err := http.NewRequest("GET", "http://example.com/wrong?data", nil)
93 | if err != nil {
94 | t.Fatal(err)
95 | }
96 | res := httptest.NewRecorder()
97 |
98 | s.Handler().ServeHTTP(res, req)
99 |
100 | if res.Code != 404 {
101 | t.Errorf("Expected HTTP status 404, found %d", res.Code)
102 | }
103 | }
104 |
105 | func safeClose(t *testing.T, o io.Closer) {
106 | if err := o.Close(); err != nil {
107 | t.Error(err)
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/day-07/21-sarama/interceptors/go.mod:
--------------------------------------------------------------------------------
1 | module sample21
2 |
3 | go 1.15
4 |
5 | require (
6 | github.com/Shopify/sarama v1.27.0
7 | go.opentelemetry.io/otel v0.10.0
8 | go.opentelemetry.io/otel/exporters/stdout v0.10.0
9 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940 // indirect
10 | )
11 |
--------------------------------------------------------------------------------
/day-07/21-sarama/interceptors/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "log"
7 | "os"
8 | "os/signal"
9 | "strings"
10 | "time"
11 |
12 | "github.com/Shopify/sarama"
13 | "go.opentelemetry.io/otel/exporters/stdout"
14 | )
15 |
16 | var (
17 | brokers = flag.String("brokers", "localhost:9092", "The Kafka brokers to connect to, as a comma separated list")
18 | topic = flag.String("topic", "default_topic", "The Kafka topic to use")
19 | logger = log.New(os.Stdout, "[OTelInterceptor] ", log.LstdFlags)
20 | )
21 |
22 | func main() {
23 | flag.Parse()
24 |
25 | if *brokers == "" {
26 | logger.Fatalln("at least one broker is required")
27 | }
28 | splitBrokers := strings.Split(*brokers, ",")
29 | sarama.Logger = log.New(os.Stdout, "[Sarama] ", log.LstdFlags)
30 |
31 | // oTel stdout example
32 | pusher, err := stdout.InstallNewPipeline([]stdout.Option{
33 | stdout.WithQuantiles([]float64{0.5, 0.9, 0.99}),
34 | }, nil)
35 | if err != nil {
36 | logger.Fatalf("failed to initialize stdout export pipeline: %v", err)
37 | }
38 | defer pusher.Stop()
39 |
40 | // simple sarama producer that adds a new producer interceptor
41 | conf := sarama.NewConfig()
42 | conf.Version = sarama.V0_11_0_0
43 | conf.Producer.Interceptors = []sarama.ProducerInterceptor{NewOTelInterceptor(splitBrokers)}
44 |
45 | producer, err := sarama.NewAsyncProducer(splitBrokers, conf)
46 | if err != nil {
47 | panic("Couldn't create a Kafka producer")
48 | }
49 | defer producer.AsyncClose()
50 |
51 | // kill -2, trap SIGINT to trigger a shutdown
52 | signals := make(chan os.Signal, 1)
53 | signal.Notify(signals, os.Interrupt)
54 |
55 | // ticker
56 | bulkSize := 2
57 | duration := 5 * time.Second
58 | ticker := time.NewTicker(duration)
59 | logger.Printf("Starting to produce %v messages every %v", bulkSize, duration)
60 | for {
61 | select {
62 | case t := <-ticker.C:
63 | now := t.Format(time.RFC3339)
64 | logger.Printf("\nproducing %v messages to topic %s at %s", bulkSize, *topic, now)
65 | for i := 0; i < bulkSize; i++ {
66 | producer.Input() <- &sarama.ProducerMessage{
67 | Topic: *topic, Key: nil,
68 | Value: sarama.StringEncoder(fmt.Sprintf("test message %v/%v from kafka-client-go-test at %s", i+1, bulkSize, now)),
69 | }
70 | }
71 | case <-signals:
72 | logger.Println("terminating the program")
73 | logger.Println("Bye :)")
74 | return
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/day-07/21-sarama/interceptors/trace_interceptor.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "strings"
6 |
7 | "github.com/Shopify/sarama"
8 | "go.opentelemetry.io/otel/api/global"
9 | "go.opentelemetry.io/otel/api/kv"
10 | "go.opentelemetry.io/otel/api/trace"
11 | )
12 |
13 | type OTelInterceptor struct {
14 | tracer trace.Tracer
15 | fixedAttrs []kv.KeyValue
16 | }
17 |
18 | // NewOTelInterceptor processes span for intercepted messages and add some
19 | // headers with the span data.
20 | func NewOTelInterceptor(brokers []string) *OTelInterceptor {
21 | oi := OTelInterceptor{}
22 | oi.tracer = global.TraceProvider().Tracer("shopify.com/sarama/examples/interceptors")
23 |
24 | // These are based on the spec, which was reachable as of 2020-05-15
25 | // https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md
26 | oi.fixedAttrs = []kv.KeyValue{
27 | kv.String("messaging.destination_kind", "topic"),
28 | kv.String("span.otel.kind", "PRODUCER"),
29 | kv.String("messaging.system", "kafka"),
30 | kv.String("net.transport", "IP.TCP"),
31 | kv.String("messaging.url", strings.Join(brokers, ",")),
32 | }
33 | return &oi
34 | }
35 |
36 | const (
37 | MessageIDHeaderName = "message_id"
38 | SpanHeaderName = "span_id"
39 | TraceHeaderName = "trace_id"
40 | )
41 |
42 | func shouldIgnoreMsg(msg *sarama.ProducerMessage) bool {
43 | // check message hasn't been here before (retries)
44 | var traceFound, spanFound, msgIDFound bool
45 | for _, h := range msg.Headers {
46 | if string(h.Key) == TraceHeaderName {
47 | traceFound = true
48 | continue
49 | }
50 | if string(h.Key) == SpanHeaderName {
51 | spanFound = true
52 | continue
53 | }
54 | if string(h.Key) == MessageIDHeaderName {
55 | msgIDFound = true
56 | }
57 | }
58 | return traceFound && spanFound && msgIDFound
59 | }
60 |
61 | func (oi *OTelInterceptor) OnSend(msg *sarama.ProducerMessage) {
62 | if shouldIgnoreMsg(msg) {
63 | return
64 | }
65 | _ = oi.tracer.WithSpan(context.TODO(), msg.Topic,
66 | func(ctx context.Context) error {
67 | span := trace.SpanFromContext(ctx)
68 | spanContext := span.SpanContext()
69 | attWithTopic := append(
70 | oi.fixedAttrs,
71 | kv.String("messaging.destination", msg.Topic),
72 | kv.String("messaging.message_id", spanContext.SpanID.String()),
73 | )
74 | span.SetAttributes(attWithTopic...)
75 |
76 | // remove existing partial tracing headers if exists
77 | noTraceHeaders := msg.Headers[:0]
78 | for _, h := range msg.Headers {
79 | key := string(h.Key)
80 | if key != TraceHeaderName && key != SpanHeaderName && key != MessageIDHeaderName {
81 | noTraceHeaders = append(noTraceHeaders, h)
82 | }
83 | }
84 | traceHeaders := []sarama.RecordHeader{
85 | {Key: []byte(TraceHeaderName), Value: []byte(spanContext.TraceID.String())},
86 | {Key: []byte(SpanHeaderName), Value: []byte(spanContext.SpanID.String())},
87 | {Key: []byte(MessageIDHeaderName), Value: []byte(spanContext.SpanID.String())},
88 | }
89 | msg.Headers = append(noTraceHeaders, traceHeaders...)
90 | return nil
91 | })
92 | }
93 |
--------------------------------------------------------------------------------
/day-07/21-sarama/producer-consumer/cmd/consumer/consumer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/Shopify/sarama"
6 | "strings"
7 | "sync"
8 | )
9 |
10 | var (
11 | wg sync.WaitGroup
12 | )
13 |
14 | func main() {
15 | //Create consumer
16 | consumer, err := sarama.NewConsumer(strings.Split("192.168.1.125:9092", ","), nil)
17 | if err != nil {
18 | fmt.Println("Failed to start consumer: %s", err)
19 | return
20 | }
21 | //Set partition
22 | partitionList, err := consumer.Partitions("nginx_log")
23 | if err != nil {
24 | fmt.Println("Failed to get the list of partitions: ", err)
25 | return
26 | }
27 | fmt.Println(partitionList)
28 | //Cyclic partition
29 | for partition := range partitionList {
30 | pc, err := consumer.ConsumePartition("nginx_log", int32(partition), sarama.OffsetNewest)
31 | if err != nil {
32 | fmt.Printf("Failed to start consumer for partition %d: %s\n", partition, err)
33 | return
34 | }
35 | defer pc.AsyncClose()
36 | wg.Add(1)
37 | go func(pc sarama.PartitionConsumer) {
38 | defer wg.Done()
39 | for msg := range pc.Messages() {
40 | fmt.Printf("Partition:%d, Offset:%d, Key:%s, Value:%s", msg.Partition, msg.Offset, string(msg.Key), string(msg.Value))
41 | fmt.Println()
42 | }
43 |
44 | }(pc)
45 | }
46 | //time.Sleep(time.Hour)
47 | wg.Wait()
48 | consumer.Close()
49 | }
50 |
--------------------------------------------------------------------------------
/day-07/21-sarama/producer-consumer/cmd/producer/producer.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/Shopify/sarama"
6 | )
7 |
8 | /*
9 | Initialize NewConfig configuration sarama.NewConfig
10 | Create producer sarama.NewSyncProducer
11 | Create message sarama.ProducerMessage
12 | Send message client.SendMessage
13 | */
14 | func main() {
15 | config := sarama.NewConfig()
16 | config.Producer.RequiredAcks = sarama.WaitForAll
17 | config.Producer.Partitioner = sarama.NewRandomPartitioner
18 | config.Producer.Return.Successes = true
19 |
20 | msg := &sarama.ProducerMessage{}
21 | msg.Topic = "TestTopic"
22 | msg.Value = sarama.StringEncoder("this is a test")
23 |
24 | client, err := sarama.NewSyncProducer([]string{"172.0.0.1:9092"}, config)
25 | if err != nil {
26 | fmt.Println("producer close, err:", err)
27 | return
28 | }
29 |
30 | defer client.Close()
31 |
32 | pid, offset, err := client.SendMessage(msg)
33 | if err != nil {
34 | fmt.Println("send message failed,", err)
35 | return
36 | }
37 |
38 | fmt.Printf("pid:%v offset:%v\n", pid, offset)
39 | }
40 |
--------------------------------------------------------------------------------
/day-07/21-sarama/producer-consumer/go.mod:
--------------------------------------------------------------------------------
1 | module sample21
2 |
3 | go 1.15
4 |
5 | require github.com/Shopify/sarama v1.27.2
6 |
--------------------------------------------------------------------------------
/day-07/22-concurrency-patterns/README.md.org:
--------------------------------------------------------------------------------
1 | * Patrones de concurrencia
2 |
3 | ** Futures
4 |
5 | Las /futures/ representan el resultado de un cálculo que se ejecuta de
6 | forma concurrente. Este patrón es nos permite ejecutar una operación
7 | costosa sin que impacte la ejecución del proceso principal.
8 |
9 | *** Interfaz para Future
10 |
11 | Definamos una interfaz que exprese el comportamiento que queremos en un /future/
12 |
13 | #+begin_src go
14 | type Value interface{}
15 |
16 | type Future interface {
17 | Get(c context.Context) (Value, error)
18 | }
19 | #+end_src
20 |
21 | Usamos =context.Context= para dar la posibilidad de cancelar la espera.
22 |
23 | *** Implementando Futures
24 |
25 | Una implementación simple para /future/ es usar un canal que comunique
26 | el estado de completamiento o error.
27 |
28 | #+begin_src go
29 | type result struct {
30 | value Value
31 | err error
32 | }
33 |
34 | type futureImpl struct {
35 | result chan *result
36 | }
37 | #+end_src
38 |
39 | Por conveniencia las estructuras son privadas.
40 |
41 | #+REVEAL: split
42 |
43 | El método =Get= bloquea la gorutina actual en espera del resultado del
44 | /future/ o la señal =Done= del contexto.
45 |
46 | #+begin_src go
47 | func (f *futureImpl) Get(c context.Context) (Value, error) {
48 | select {
49 | case <-ctx.Done():
50 | return nil, ctx.Err()
51 | case result := <-f.result:
52 | return result.value, result.err
53 | }
54 | }
55 | #+end_src
56 |
57 | #+REVEAL: split
58 |
59 | Finalmente una función para crear nuevas /futures/.
60 |
61 | #+begin_src go
62 | func NewFuture(f func() (Value, error)) Future {
63 | fut := &futureImpl {
64 | result: make(chan *result)
65 | }
66 | go func(){
67 | defer close(fut.result)
68 | value, err := f()
69 | f.result <- &result{value, err}
70 | }()
71 | return fut
72 | }
73 | #+end_src
74 |
--------------------------------------------------------------------------------
/reveal/css/layout.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Layout helpers.
3 | */
4 |
5 | // Stretch an element vertically based on available space
6 | .reveal .stretch,
7 | .reveal .r-stretch {
8 | max-width: none;
9 | max-height: none;
10 | }
11 |
12 | .reveal pre.stretch code,
13 | .reveal pre.r-stretch code {
14 | height: 100%;
15 | max-height: 100%;
16 | box-sizing: border-box;
17 | }
18 |
19 | // Text that auto-fits it's container
20 | .reveal .r-fit-text {
21 | display: inline-block; // https://github.com/rikschennink/fitty#performance
22 | white-space: nowrap;
23 | }
24 |
25 | // Stack multiple elements on top of each other
26 | .reveal .r-stack {
27 | display: grid;
28 | }
29 |
30 | .reveal .r-stack > * {
31 | grid-area: 1/1;
32 | margin: auto;
33 | }
34 |
35 | // Horizontal and vertical stacks
36 | .reveal .r-vstack,
37 | .reveal .r-hstack {
38 | display: flex;
39 |
40 | img, video {
41 | min-width: 0;
42 | min-height: 0;
43 | object-fit: contain;
44 | }
45 | }
46 |
47 | .reveal .r-vstack {
48 | flex-direction: column;
49 | align-items: center;
50 | justify-content: center;
51 | }
52 |
53 | .reveal .r-hstack {
54 | flex-direction: row;
55 | align-items: center;
56 | justify-content: center;
57 | }
58 |
59 | // Naming based on tailwindcss
60 | .reveal .items-stretch { align-items: stretch; }
61 | .reveal .items-start { align-items: flex-start; }
62 | .reveal .items-center { align-items: center; }
63 | .reveal .items-end { align-items: flex-end; }
64 |
65 | .reveal .justify-between { justify-content: space-between; }
66 | .reveal .justify-around { justify-content: space-around; }
67 | .reveal .justify-start { justify-content: flex-start; }
68 | .reveal .justify-center { justify-content: center; }
69 | .reveal .justify-end { justify-content: flex-end; }
70 |
--------------------------------------------------------------------------------
/reveal/css/print/paper.scss:
--------------------------------------------------------------------------------
1 | /* Default Print Stylesheet Template
2 | by Rob Glazebrook of CSSnewbie.com
3 | Last Updated: June 4, 2008
4 |
5 | Feel free (nay, compelled) to edit, append, and
6 | manipulate this file as you see fit. */
7 |
8 | @media print {
9 | html:not(.print-pdf) {
10 |
11 | background: #fff;
12 | width: auto;
13 | height: auto;
14 | overflow: visible;
15 |
16 | body {
17 | background: #fff;
18 | font-size: 20pt;
19 | width: auto;
20 | height: auto;
21 | border: 0;
22 | margin: 0 5%;
23 | padding: 0;
24 | overflow: visible;
25 | float: none !important;
26 | }
27 |
28 | .nestedarrow,
29 | .controls,
30 | .fork-reveal,
31 | .share-reveal,
32 | .state-background,
33 | .reveal .progress,
34 | .reveal .backgrounds,
35 | .reveal .slide-number {
36 | display: none !important;
37 | }
38 |
39 | body, p, td, li {
40 | font-size: 20pt!important;
41 | color: #000;
42 | }
43 |
44 | h1,h2,h3,h4,h5,h6 {
45 | color: #000!important;
46 | height: auto;
47 | line-height: normal;
48 | text-align: left;
49 | letter-spacing: normal;
50 | }
51 |
52 | /* Need to reduce the size of the fonts for printing */
53 | h1 { font-size: 28pt !important; }
54 | h2 { font-size: 24pt !important; }
55 | h3 { font-size: 22pt !important; }
56 | h4 { font-size: 22pt !important; font-variant: small-caps; }
57 | h5 { font-size: 21pt !important; }
58 | h6 { font-size: 20pt !important; font-style: italic; }
59 |
60 | a:link,
61 | a:visited {
62 | color: #000 !important;
63 | font-weight: bold;
64 | text-decoration: underline;
65 | }
66 |
67 | ul, ol, div, p {
68 | visibility: visible;
69 | position: static;
70 | width: auto;
71 | height: auto;
72 | display: block;
73 | overflow: visible;
74 | margin: 0;
75 | text-align: left !important;
76 | }
77 | .reveal pre,
78 | .reveal table {
79 | margin-left: 0;
80 | margin-right: 0;
81 | }
82 | .reveal pre code {
83 | padding: 20px;
84 | }
85 | .reveal blockquote {
86 | margin: 20px 0;
87 | }
88 | .reveal .slides {
89 | position: static !important;
90 | width: auto !important;
91 | height: auto !important;
92 |
93 | left: 0 !important;
94 | top: 0 !important;
95 | margin-left: 0 !important;
96 | margin-top: 0 !important;
97 | padding: 0 !important;
98 | zoom: 1 !important;
99 | transform: none !important;
100 |
101 | overflow: visible !important;
102 | display: block !important;
103 |
104 | text-align: left !important;
105 | perspective: none;
106 |
107 | perspective-origin: 50% 50%;
108 | }
109 | .reveal .slides section {
110 | visibility: visible !important;
111 | position: static !important;
112 | width: auto !important;
113 | height: auto !important;
114 | display: block !important;
115 | overflow: visible !important;
116 |
117 | left: 0 !important;
118 | top: 0 !important;
119 | margin-left: 0 !important;
120 | margin-top: 0 !important;
121 | padding: 60px 20px !important;
122 | z-index: auto !important;
123 |
124 | opacity: 1 !important;
125 |
126 | page-break-after: always !important;
127 |
128 | transform-style: flat !important;
129 | transform: none !important;
130 | transition: none !important;
131 | }
132 | .reveal .slides section.stack {
133 | padding: 0 !important;
134 | }
135 | .reveal section:last-of-type {
136 | page-break-after: avoid !important;
137 | }
138 | .reveal section .fragment {
139 | opacity: 1 !important;
140 | visibility: visible !important;
141 |
142 | transform: none !important;
143 | }
144 | .reveal section img {
145 | display: block;
146 | margin: 15px 0px;
147 | background: rgba(255,255,255,1);
148 | border: 1px solid #666;
149 | box-shadow: none;
150 | }
151 |
152 | .reveal section small {
153 | font-size: 0.8em;
154 | }
155 |
156 | .reveal .hljs {
157 | max-height: 100%;
158 | white-space: pre-wrap;
159 | word-wrap: break-word;
160 | word-break: break-word;
161 | font-size: 15pt;
162 | }
163 |
164 | .reveal .hljs .hljs-ln-numbers {
165 | white-space: nowrap;
166 | }
167 |
168 | .reveal .hljs td {
169 | font-size: inherit !important;
170 | color: inherit !important;
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/reveal/css/print/pdf.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * This stylesheet is used to print reveal.js
3 | * presentations to PDF.
4 | *
5 | * https://revealjs.com/pdf-export/
6 | */
7 |
8 | html.print-pdf {
9 | * {
10 | -webkit-print-color-adjust: exact;
11 | }
12 |
13 | & {
14 | width: 100%;
15 | height: 100%;
16 | overflow: visible;
17 | }
18 |
19 | body {
20 | margin: 0 auto !important;
21 | border: 0;
22 | padding: 0;
23 | float: none !important;
24 | overflow: visible;
25 | }
26 |
27 | /* Remove any elements not needed in print. */
28 | .nestedarrow,
29 | .reveal .controls,
30 | .reveal .progress,
31 | .reveal .playback,
32 | .reveal.overview,
33 | .state-background {
34 | display: none !important;
35 | }
36 |
37 | .reveal pre code {
38 | overflow: hidden !important;
39 | font-family: Courier, 'Courier New', monospace !important;
40 | }
41 |
42 | .reveal {
43 | width: auto !important;
44 | height: auto !important;
45 | overflow: hidden !important;
46 | }
47 | .reveal .slides {
48 | position: static;
49 | width: 100% !important;
50 | height: auto !important;
51 | zoom: 1 !important;
52 | pointer-events: initial;
53 |
54 | left: auto;
55 | top: auto;
56 | margin: 0 !important;
57 | padding: 0 !important;
58 |
59 | overflow: visible;
60 | display: block;
61 |
62 | perspective: none;
63 | perspective-origin: 50% 50%;
64 | }
65 |
66 | .reveal .slides .pdf-page {
67 | position: relative;
68 | overflow: hidden;
69 | z-index: 1;
70 |
71 | page-break-after: always;
72 | }
73 |
74 | .reveal .slides section {
75 | visibility: visible !important;
76 | display: block !important;
77 | position: absolute !important;
78 |
79 | margin: 0 !important;
80 | padding: 0 !important;
81 | box-sizing: border-box !important;
82 | min-height: 1px;
83 |
84 | opacity: 1 !important;
85 |
86 | transform-style: flat !important;
87 | transform: none !important;
88 | }
89 |
90 | .reveal section.stack {
91 | position: relative !important;
92 | margin: 0 !important;
93 | padding: 0 !important;
94 | page-break-after: avoid !important;
95 | height: auto !important;
96 | min-height: auto !important;
97 | }
98 |
99 | .reveal img {
100 | box-shadow: none;
101 | }
102 |
103 |
104 | /* Slide backgrounds are placed inside of their slide when exporting to PDF */
105 | .reveal .backgrounds {
106 | display: none;
107 | }
108 | .reveal .slide-background {
109 | display: block !important;
110 | position: absolute;
111 | top: 0;
112 | left: 0;
113 | width: 100%;
114 | height: 100%;
115 | z-index: auto !important;
116 | }
117 |
118 | /* Display slide speaker notes when 'showNotes' is enabled */
119 | .reveal.show-notes {
120 | max-width: none;
121 | max-height: none;
122 | }
123 | .reveal .speaker-notes-pdf {
124 | display: block;
125 | width: 100%;
126 | height: auto;
127 | max-height: none;
128 | top: auto;
129 | right: auto;
130 | bottom: auto;
131 | left: auto;
132 | z-index: 100;
133 | }
134 |
135 | /* Layout option which makes notes appear on a separate page */
136 | .reveal .speaker-notes-pdf[data-layout="separate-page"] {
137 | position: relative;
138 | color: inherit;
139 | background-color: transparent;
140 | padding: 20px;
141 | page-break-after: always;
142 | border: 0;
143 | }
144 |
145 | /* Display slide numbers when 'slideNumber' is enabled */
146 | .reveal .slide-number-pdf {
147 | display: block;
148 | position: absolute;
149 | font-size: 14px;
150 | }
151 |
152 | /* This accessibility tool is not useful in PDF and breaks it visually */
153 | .aria-status {
154 | display: none;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/reveal/css/theme/README.md:
--------------------------------------------------------------------------------
1 | ## Dependencies
2 |
3 | Themes are written using Sass to keep things modular and reduce the need for repeated selectors across files. Make sure that you have the reveal.js development environment installed before proceeding: https://revealjs.com/installation/#full-setup
4 |
5 | ## Creating a Theme
6 |
7 | To create your own theme, start by duplicating a ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source). It will be automatically compiled from Sass to CSS (see the [gulpfile](https://github.com/hakimel/reveal.js/blob/master/gulpfile.js)) when you run `npm run build -- css-themes`.
8 |
9 | Each theme file does four things in the following order:
10 |
11 | 1. **Include [/css/theme/template/mixins.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/mixins.scss)**
12 | Shared utility functions.
13 |
14 | 2. **Include [/css/theme/template/settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss)**
15 | Declares a set of custom variables that the template file (step 4) expects. Can be overridden in step 3.
16 |
17 | 3. **Override**
18 | This is where you override the default theme. Either by specifying variables (see [settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss) for reference) or by adding any selectors and styles you please.
19 |
20 | 4. **Include [/css/theme/template/theme.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/theme.scss)**
21 | The template theme file which will generate final CSS output based on the currently defined variables.
22 |
--------------------------------------------------------------------------------
/reveal/css/theme/source/beige.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Beige theme for reveal.js.
3 | *
4 | * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
5 | */
6 |
7 |
8 | // Default mixins and settings -----------------
9 | @import "../template/mixins";
10 | @import "../template/settings";
11 | // ---------------------------------------------
12 |
13 |
14 |
15 | // Include theme-specific fonts
16 | @import url(./fonts/league-gothic/league-gothic.css);
17 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
18 |
19 |
20 | // Override theme settings (see ../template/settings.scss)
21 | $mainColor: #333;
22 | $headingColor: #333;
23 | $headingTextShadow: none;
24 | $backgroundColor: #f7f3de;
25 | $linkColor: #8b743d;
26 | $linkColorHover: lighten( $linkColor, 20% );
27 | $selectionBackgroundColor: rgba(79, 64, 28, 0.99);
28 | $heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15);
29 |
30 | // Background generator
31 | @mixin bodyBackground() {
32 | @include radial-gradient( rgba(247,242,211,1), rgba(255,255,255,1) );
33 | }
34 |
35 | // Change text colors against dark slide backgrounds
36 | @include dark-bg-text-color(#fff);
37 |
38 |
39 | // Theme template ------------------------------
40 | @import "../template/theme";
41 | // ---------------------------------------------
42 |
--------------------------------------------------------------------------------
/reveal/css/theme/source/black.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Black theme for reveal.js. This is the opposite of the 'white' theme.
3 | *
4 | * By Hakim El Hattab, http://hakim.se
5 | */
6 |
7 |
8 | // Default mixins and settings -----------------
9 | @import "../template/mixins";
10 | @import "../template/settings";
11 | // ---------------------------------------------
12 |
13 |
14 | // Include theme-specific fonts
15 | @import url(./fonts/source-sans-pro/source-sans-pro.css);
16 |
17 |
18 | // Override theme settings (see ../template/settings.scss)
19 | $backgroundColor: #191919;
20 |
21 | $mainColor: #fff;
22 | $headingColor: #fff;
23 |
24 | $mainFontSize: 42px;
25 | $mainFont: 'Source Sans Pro', Helvetica, sans-serif;
26 | $headingFont: 'Source Sans Pro', Helvetica, sans-serif;
27 | $headingTextShadow: none;
28 | $headingLetterSpacing: normal;
29 | $headingTextTransform: uppercase;
30 | $headingFontWeight: 600;
31 | $linkColor: #42affa;
32 | $linkColorHover: lighten( $linkColor, 15% );
33 | $selectionBackgroundColor: lighten( $linkColor, 25% );
34 |
35 | $heading1Size: 2.5em;
36 | $heading2Size: 1.6em;
37 | $heading3Size: 1.3em;
38 | $heading4Size: 1.0em;
39 |
40 | // Change text colors against light slide backgrounds
41 | @include light-bg-text-color(#222);
42 |
43 |
44 | // Theme template ------------------------------
45 | @import "../template/theme";
46 | // ---------------------------------------------
47 |
--------------------------------------------------------------------------------
/reveal/css/theme/source/blood.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Blood theme for reveal.js
3 | * Author: Walther http://github.com/Walther
4 | *
5 | * Designed to be used with highlight.js theme
6 | * "monokai_sublime.css" available from
7 | * https://github.com/isagalaev/highlight.js/
8 | *
9 | * For other themes, change $codeBackground accordingly.
10 | *
11 | */
12 |
13 | // Default mixins and settings -----------------
14 | @import "../template/mixins";
15 | @import "../template/settings";
16 | // ---------------------------------------------
17 |
18 | // Include theme-specific fonts
19 |
20 | @import url(https://fonts.googleapis.com/css?family=Ubuntu:300,700,300italic,700italic);
21 |
22 | // Colors used in the theme
23 | $blood: #a23;
24 | $coal: #222;
25 | $codeBackground: #23241f;
26 |
27 | $backgroundColor: $coal;
28 |
29 | // Main text
30 | $mainFont: Ubuntu, 'sans-serif';
31 | $mainColor: #eee;
32 |
33 | // Headings
34 | $headingFont: Ubuntu, 'sans-serif';
35 | $headingTextShadow: 2px 2px 2px $coal;
36 |
37 | // h1 shadow, borrowed humbly from
38 | // (c) Default theme by Hakim El Hattab
39 | $heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15);
40 |
41 | // Links
42 | $linkColor: $blood;
43 | $linkColorHover: lighten( $linkColor, 20% );
44 |
45 | // Text selection
46 | $selectionBackgroundColor: $blood;
47 | $selectionColor: #fff;
48 |
49 | // Change text colors against dark slide backgrounds
50 | @include light-bg-text-color(#222);
51 |
52 |
53 | // Theme template ------------------------------
54 | @import "../template/theme";
55 | // ---------------------------------------------
56 |
57 | // some overrides after theme template import
58 |
59 | .reveal p {
60 | font-weight: 300;
61 | text-shadow: 1px 1px $coal;
62 | }
63 |
64 | section.has-light-background {
65 | p, h1, h2, h3, h4 {
66 | text-shadow: none;
67 | }
68 | }
69 |
70 | .reveal h1,
71 | .reveal h2,
72 | .reveal h3,
73 | .reveal h4,
74 | .reveal h5,
75 | .reveal h6 {
76 | font-weight: 700;
77 | }
78 |
79 | .reveal p code {
80 | background-color: $codeBackground;
81 | display: inline-block;
82 | border-radius: 7px;
83 | }
84 |
85 | .reveal small code {
86 | vertical-align: baseline;
87 | }
--------------------------------------------------------------------------------
/reveal/css/theme/source/league.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * League theme for reveal.js.
3 | *
4 | * This was the default theme pre-3.0.0.
5 | *
6 | * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
7 | */
8 |
9 |
10 | // Default mixins and settings -----------------
11 | @import "../template/mixins";
12 | @import "../template/settings";
13 | // ---------------------------------------------
14 |
15 |
16 |
17 | // Include theme-specific fonts
18 | @import url(./fonts/league-gothic/league-gothic.css);
19 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
20 |
21 | // Override theme settings (see ../template/settings.scss)
22 | $headingTextShadow: 0px 0px 6px rgba(0,0,0,0.2);
23 | $heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15);
24 |
25 | // Background generator
26 | @mixin bodyBackground() {
27 | @include radial-gradient( rgba(28,30,32,1), rgba(85,90,95,1) );
28 | }
29 |
30 | // Change text colors against light slide backgrounds
31 | @include light-bg-text-color(#222);
32 |
33 |
34 | // Theme template ------------------------------
35 | @import "../template/theme";
36 | // ---------------------------------------------
37 |
--------------------------------------------------------------------------------
/reveal/css/theme/source/moon.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Solarized Dark theme for reveal.js.
3 | * Author: Achim Staebler
4 | */
5 |
6 |
7 | // Default mixins and settings -----------------
8 | @import "../template/mixins";
9 | @import "../template/settings";
10 | // ---------------------------------------------
11 |
12 |
13 |
14 | // Include theme-specific fonts
15 | @import url(./fonts/league-gothic/league-gothic.css);
16 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
17 |
18 | /**
19 | * Solarized colors by Ethan Schoonover
20 | */
21 | html * {
22 | color-profile: sRGB;
23 | rendering-intent: auto;
24 | }
25 |
26 | // Solarized colors
27 | $base03: #002b36;
28 | $base02: #073642;
29 | $base01: #586e75;
30 | $base00: #657b83;
31 | $base0: #839496;
32 | $base1: #93a1a1;
33 | $base2: #eee8d5;
34 | $base3: #fdf6e3;
35 | $yellow: #b58900;
36 | $orange: #cb4b16;
37 | $red: #dc322f;
38 | $magenta: #d33682;
39 | $violet: #6c71c4;
40 | $blue: #268bd2;
41 | $cyan: #2aa198;
42 | $green: #859900;
43 |
44 | // Override theme settings (see ../template/settings.scss)
45 | $mainColor: $base1;
46 | $headingColor: $base2;
47 | $headingTextShadow: none;
48 | $backgroundColor: $base03;
49 | $linkColor: $blue;
50 | $linkColorHover: lighten( $linkColor, 20% );
51 | $selectionBackgroundColor: $magenta;
52 |
53 | // Change text colors against light slide backgrounds
54 | @include light-bg-text-color(#222);
55 |
56 | // Theme template ------------------------------
57 | @import "../template/theme";
58 | // ---------------------------------------------
59 |
--------------------------------------------------------------------------------
/reveal/css/theme/source/night.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Black theme for reveal.js.
3 | *
4 | * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
5 | */
6 |
7 |
8 | // Default mixins and settings -----------------
9 | @import "../template/mixins";
10 | @import "../template/settings";
11 | // ---------------------------------------------
12 |
13 |
14 | // Include theme-specific fonts
15 | @import url(https://fonts.googleapis.com/css?family=Montserrat:700);
16 | @import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic);
17 |
18 |
19 | // Override theme settings (see ../template/settings.scss)
20 | $backgroundColor: #111;
21 |
22 | $mainFont: 'Open Sans', sans-serif;
23 | $linkColor: #e7ad52;
24 | $linkColorHover: lighten( $linkColor, 20% );
25 | $headingFont: 'Montserrat', Impact, sans-serif;
26 | $headingTextShadow: none;
27 | $headingLetterSpacing: -0.03em;
28 | $headingTextTransform: none;
29 | $selectionBackgroundColor: #e7ad52;
30 |
31 | // Change text colors against light slide backgrounds
32 | @include light-bg-text-color(#222);
33 |
34 |
35 | // Theme template ------------------------------
36 | @import "../template/theme";
37 | // ---------------------------------------------
--------------------------------------------------------------------------------
/reveal/css/theme/source/serif.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * A simple theme for reveal.js presentations, similar
3 | * to the default theme. The accent color is brown.
4 | *
5 | * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed.
6 | */
7 |
8 |
9 | // Default mixins and settings -----------------
10 | @import "../template/mixins";
11 | @import "../template/settings";
12 | // ---------------------------------------------
13 |
14 |
15 |
16 | // Override theme settings (see ../template/settings.scss)
17 | $mainFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
18 | $mainColor: #000;
19 | $headingFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
20 | $headingColor: #383D3D;
21 | $headingTextShadow: none;
22 | $headingTextTransform: none;
23 | $backgroundColor: #F0F1EB;
24 | $linkColor: #51483D;
25 | $linkColorHover: lighten( $linkColor, 20% );
26 | $selectionBackgroundColor: #26351C;
27 |
28 | .reveal a {
29 | line-height: 1.3em;
30 | }
31 |
32 | // Change text colors against dark slide backgrounds
33 | @include dark-bg-text-color(#fff);
34 |
35 |
36 | // Theme template ------------------------------
37 | @import "../template/theme";
38 | // ---------------------------------------------
39 |
--------------------------------------------------------------------------------
/reveal/css/theme/source/simple.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * A simple theme for reveal.js presentations, similar
3 | * to the default theme. The accent color is darkblue.
4 | *
5 | * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed.
6 | * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
7 | */
8 |
9 |
10 | // Default mixins and settings -----------------
11 | @import "../template/mixins";
12 | @import "../template/settings";
13 | // ---------------------------------------------
14 |
15 |
16 |
17 | // Include theme-specific fonts
18 | @import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700);
19 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
20 |
21 |
22 | // Override theme settings (see ../template/settings.scss)
23 | $mainFont: 'Lato', sans-serif;
24 | $mainColor: #000;
25 | $headingFont: 'News Cycle', Impact, sans-serif;
26 | $headingColor: #000;
27 | $headingTextShadow: none;
28 | $headingTextTransform: none;
29 | $backgroundColor: #fff;
30 | $linkColor: #00008B;
31 | $linkColorHover: lighten( $linkColor, 20% );
32 | $selectionBackgroundColor: rgba(0, 0, 0, 0.99);
33 |
34 | // Change text colors against dark slide backgrounds
35 | @include dark-bg-text-color(#fff);
36 |
37 |
38 | // Theme template ------------------------------
39 | @import "../template/theme";
40 | // ---------------------------------------------
--------------------------------------------------------------------------------
/reveal/css/theme/source/sky.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Sky theme for reveal.js.
3 | *
4 | * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
5 | */
6 |
7 |
8 | // Default mixins and settings -----------------
9 | @import "../template/mixins";
10 | @import "../template/settings";
11 | // ---------------------------------------------
12 |
13 |
14 |
15 | // Include theme-specific fonts
16 | @import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic);
17 | @import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700);
18 |
19 |
20 | // Override theme settings (see ../template/settings.scss)
21 | $mainFont: 'Open Sans', sans-serif;
22 | $mainColor: #333;
23 | $headingFont: 'Quicksand', sans-serif;
24 | $headingColor: #333;
25 | $headingLetterSpacing: -0.08em;
26 | $headingTextShadow: none;
27 | $backgroundColor: #f7fbfc;
28 | $linkColor: #3b759e;
29 | $linkColorHover: lighten( $linkColor, 20% );
30 | $selectionBackgroundColor: #134674;
31 |
32 | // Fix links so they are not cut off
33 | .reveal a {
34 | line-height: 1.3em;
35 | }
36 |
37 | // Background generator
38 | @mixin bodyBackground() {
39 | @include radial-gradient( #add9e4, #f7fbfc );
40 | }
41 |
42 | // Change text colors against dark slide backgrounds
43 | @include dark-bg-text-color(#fff);
44 |
45 |
46 |
47 | // Theme template ------------------------------
48 | @import "../template/theme";
49 | // ---------------------------------------------
50 |
--------------------------------------------------------------------------------
/reveal/css/theme/source/solarized.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Solarized Light theme for reveal.js.
3 | * Author: Achim Staebler
4 | */
5 |
6 |
7 | // Default mixins and settings -----------------
8 | @import "../template/mixins";
9 | @import "../template/settings";
10 | // ---------------------------------------------
11 |
12 |
13 |
14 | // Include theme-specific fonts
15 | @import url(./fonts/league-gothic/league-gothic.css);
16 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
17 |
18 |
19 | /**
20 | * Solarized colors by Ethan Schoonover
21 | */
22 | html * {
23 | color-profile: sRGB;
24 | rendering-intent: auto;
25 | }
26 |
27 | // Solarized colors
28 | $base03: #002b36;
29 | $base02: #073642;
30 | $base01: #586e75;
31 | $base00: #657b83;
32 | $base0: #839496;
33 | $base1: #93a1a1;
34 | $base2: #eee8d5;
35 | $base3: #fdf6e3;
36 | $yellow: #b58900;
37 | $orange: #cb4b16;
38 | $red: #dc322f;
39 | $magenta: #d33682;
40 | $violet: #6c71c4;
41 | $blue: #268bd2;
42 | $cyan: #2aa198;
43 | $green: #859900;
44 |
45 | // Override theme settings (see ../template/settings.scss)
46 | $mainColor: $base00;
47 | $headingColor: $base01;
48 | $headingTextShadow: none;
49 | $backgroundColor: $base3;
50 | $linkColor: $blue;
51 | $linkColorHover: lighten( $linkColor, 20% );
52 | $selectionBackgroundColor: $magenta;
53 |
54 | // Background generator
55 | // @mixin bodyBackground() {
56 | // @include radial-gradient( rgba($base3,1), rgba(lighten($base3, 20%),1) );
57 | // }
58 |
59 |
60 |
61 | // Theme template ------------------------------
62 | @import "../template/theme";
63 | // ---------------------------------------------
64 |
--------------------------------------------------------------------------------
/reveal/css/theme/source/white.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * White theme for reveal.js. This is the opposite of the 'black' theme.
3 | *
4 | * By Hakim El Hattab, http://hakim.se
5 | */
6 |
7 |
8 | // Default mixins and settings -----------------
9 | @import "../template/mixins";
10 | @import "../template/settings";
11 | // ---------------------------------------------
12 |
13 |
14 | // Include theme-specific fonts
15 | @import url(./fonts/source-sans-pro/source-sans-pro.css);
16 |
17 |
18 | // Override theme settings (see ../template/settings.scss)
19 | $backgroundColor: #fff;
20 |
21 | $mainColor: #222;
22 | $headingColor: #222;
23 |
24 | $mainFontSize: 42px;
25 | $mainFont: 'Source Sans Pro', Helvetica, sans-serif;
26 | $headingFont: 'Source Sans Pro', Helvetica, sans-serif;
27 | $headingTextShadow: none;
28 | $headingLetterSpacing: normal;
29 | $headingTextTransform: uppercase;
30 | $headingFontWeight: 600;
31 | $linkColor: #2a76dd;
32 | $linkColorHover: lighten( $linkColor, 15% );
33 | $selectionBackgroundColor: lighten( $linkColor, 25% );
34 |
35 | $heading1Size: 2.5em;
36 | $heading2Size: 1.6em;
37 | $heading3Size: 1.3em;
38 | $heading4Size: 1.0em;
39 |
40 | // Change text colors against dark slide backgrounds
41 | @include dark-bg-text-color(#fff);
42 |
43 |
44 | // Theme template ------------------------------
45 | @import "../template/theme";
46 | // ---------------------------------------------
47 |
--------------------------------------------------------------------------------
/reveal/css/theme/template/exposer.scss:
--------------------------------------------------------------------------------
1 | // Exposes theme's variables for easy re-use in CSS for plugin authors
2 |
3 | :root {
4 | --background-color: #{$backgroundColor};
5 | --main-font: #{$mainFont};
6 | --main-font-size: #{$mainFontSize};
7 | --main-color: #{$mainColor};
8 | --block-margin: #{$blockMargin};
9 | --heading-margin: #{$headingMargin};
10 | --heading-font: #{$headingFont};
11 | --heading-color: #{$headingColor};
12 | --heading-line-height: #{$headingLineHeight};
13 | --heading-letter-spacing: #{$headingLetterSpacing};
14 | --heading-text-transform: #{$headingTextTransform};
15 | --heading-text-shadow: #{$headingTextShadow};
16 | --heading-font-weight: #{$headingFontWeight};
17 | --heading1-text-shadow: #{$heading1TextShadow};
18 | --heading1-size: #{$heading1Size};
19 | --heading2-size: #{$heading2Size};
20 | --heading3-size: #{$heading3Size};
21 | --heading4-size: #{$heading4Size};
22 | --code-font: #{$codeFont};
23 | --link-color: #{$linkColor};
24 | --link-color-hover: #{$linkColorHover};
25 | --selection-background-color: #{$selectionBackgroundColor};
26 | --selection-color: #{$selectionColor};
27 | }
28 |
--------------------------------------------------------------------------------
/reveal/css/theme/template/mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin vertical-gradient( $top, $bottom ) {
2 | background: $top;
3 | background: -moz-linear-gradient( top, $top 0%, $bottom 100% );
4 | background: -webkit-gradient( linear, left top, left bottom, color-stop(0%,$top), color-stop(100%,$bottom) );
5 | background: -webkit-linear-gradient( top, $top 0%, $bottom 100% );
6 | background: -o-linear-gradient( top, $top 0%, $bottom 100% );
7 | background: -ms-linear-gradient( top, $top 0%, $bottom 100% );
8 | background: linear-gradient( top, $top 0%, $bottom 100% );
9 | }
10 |
11 | @mixin horizontal-gradient( $top, $bottom ) {
12 | background: $top;
13 | background: -moz-linear-gradient( left, $top 0%, $bottom 100% );
14 | background: -webkit-gradient( linear, left top, right top, color-stop(0%,$top), color-stop(100%,$bottom) );
15 | background: -webkit-linear-gradient( left, $top 0%, $bottom 100% );
16 | background: -o-linear-gradient( left, $top 0%, $bottom 100% );
17 | background: -ms-linear-gradient( left, $top 0%, $bottom 100% );
18 | background: linear-gradient( left, $top 0%, $bottom 100% );
19 | }
20 |
21 | @mixin radial-gradient( $outer, $inner, $type: circle ) {
22 | background: $outer;
23 | background: -moz-radial-gradient( center, $type cover, $inner 0%, $outer 100% );
24 | background: -webkit-gradient( radial, center center, 0px, center center, 100%, color-stop(0%,$inner), color-stop(100%,$outer) );
25 | background: -webkit-radial-gradient( center, $type cover, $inner 0%, $outer 100% );
26 | background: -o-radial-gradient( center, $type cover, $inner 0%, $outer 100% );
27 | background: -ms-radial-gradient( center, $type cover, $inner 0%, $outer 100% );
28 | background: radial-gradient( center, $type cover, $inner 0%, $outer 100% );
29 | }
30 |
31 | @mixin light-bg-text-color( $color ) {
32 | section.has-light-background {
33 | &, h1, h2, h3, h4, h5, h6 {
34 | color: $color;
35 | }
36 | }
37 | }
38 |
39 | @mixin dark-bg-text-color( $color ) {
40 | section.has-dark-background {
41 | &, h1, h2, h3, h4, h5, h6 {
42 | color: $color;
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/reveal/css/theme/template/settings.scss:
--------------------------------------------------------------------------------
1 | // Base settings for all themes that can optionally be
2 | // overridden by the super-theme
3 |
4 | // Background of the presentation
5 | $backgroundColor: #2b2b2b;
6 |
7 | // Primary/body text
8 | $mainFont: 'Lato', sans-serif;
9 | $mainFontSize: 40px;
10 | $mainColor: #eee;
11 |
12 | // Vertical spacing between blocks of text
13 | $blockMargin: 20px;
14 |
15 | // Headings
16 | $headingMargin: 0 0 $blockMargin 0;
17 | $headingFont: 'League Gothic', Impact, sans-serif;
18 | $headingColor: #eee;
19 | $headingLineHeight: 1.2;
20 | $headingLetterSpacing: normal;
21 | $headingTextTransform: uppercase;
22 | $headingTextShadow: none;
23 | $headingFontWeight: normal;
24 | $heading1TextShadow: $headingTextShadow;
25 |
26 | $heading1Size: 3.77em;
27 | $heading2Size: 2.11em;
28 | $heading3Size: 1.55em;
29 | $heading4Size: 1.00em;
30 |
31 | $codeFont: monospace;
32 |
33 | // Links and actions
34 | $linkColor: #13DAEC;
35 | $linkColorHover: lighten( $linkColor, 20% );
36 |
37 | // Text selection
38 | $selectionBackgroundColor: #FF5E99;
39 | $selectionColor: #fff;
40 |
41 | // Generates the presentation background, can be overridden
42 | // to return a background image or gradient
43 | @mixin bodyBackground() {
44 | background: $backgroundColor;
45 | }
46 |
--------------------------------------------------------------------------------
/reveal/dist/reset.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v4.0 | 20180602
3 | License: none (public domain)
4 | */
5 |
6 | html, body, div, span, applet, object, iframe,
7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8 | a, abbr, acronym, address, big, cite, code,
9 | del, dfn, em, img, ins, kbd, q, s, samp,
10 | small, strike, strong, sub, sup, tt, var,
11 | b, u, i, center,
12 | dl, dt, dd, ol, ul, li,
13 | fieldset, form, label, legend,
14 | table, caption, tbody, tfoot, thead, tr, th, td,
15 | article, aside, canvas, details, embed,
16 | figure, figcaption, footer, header, hgroup,
17 | main, menu, nav, output, ruby, section, summary,
18 | time, mark, audio, video {
19 | margin: 0;
20 | padding: 0;
21 | border: 0;
22 | font-size: 100%;
23 | font: inherit;
24 | vertical-align: baseline;
25 | }
26 | /* HTML5 display-role reset for older browsers */
27 | article, aside, details, figcaption, figure,
28 | footer, header, hgroup, main, menu, nav, section {
29 | display: block;
30 | }
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/league-gothic/LICENSE:
--------------------------------------------------------------------------------
1 | SIL Open Font License (OFL)
2 | http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL
3 |
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/league-gothic/league-gothic.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'League Gothic';
3 | src: url('./league-gothic.eot');
4 | src: url('./league-gothic.eot?#iefix') format('embedded-opentype'),
5 | url('./league-gothic.woff') format('woff'),
6 | url('./league-gothic.ttf') format('truetype');
7 |
8 | font-weight: normal;
9 | font-style: normal;
10 | }
11 |
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/league-gothic/league-gothic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/league-gothic/league-gothic.eot
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/league-gothic/league-gothic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/league-gothic/league-gothic.ttf
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/league-gothic/league-gothic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/league-gothic/league-gothic.woff
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/LICENSE:
--------------------------------------------------------------------------------
1 | SIL Open Font License
2 |
3 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
4 |
5 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
6 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
7 |
8 | —————————————————————————————-
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | —————————————————————————————-
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
14 |
15 | The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
16 |
17 | DEFINITIONS
18 | “Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
19 |
20 | “Reserved Font Name” refers to any names specified as such after the copyright statement(s).
21 |
22 | “Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s).
23 |
24 | “Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
25 |
26 | “Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
27 |
28 | PERMISSION & CONDITIONS
29 | Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
30 |
31 | 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
32 |
33 | 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
34 |
35 | 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
36 |
37 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
38 |
39 | 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
40 |
41 | TERMINATION
42 | This license becomes null and void if any of the above conditions are not met.
43 |
44 | DISCLAIMER
45 | THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.eot
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.ttf
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.woff
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.eot
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.ttf
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.woff
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.eot
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.ttf
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.woff
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.eot
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.ttf
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yorodm/golang-workshop/ed9d76aeb6395d543c96ce8e31f89870a0575c08/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.woff
--------------------------------------------------------------------------------
/reveal/dist/theme/fonts/source-sans-pro/source-sans-pro.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Source Sans Pro';
3 | src: url('./source-sans-pro-regular.eot');
4 | src: url('./source-sans-pro-regular.eot?#iefix') format('embedded-opentype'),
5 | url('./source-sans-pro-regular.woff') format('woff'),
6 | url('./source-sans-pro-regular.ttf') format('truetype');
7 | font-weight: normal;
8 | font-style: normal;
9 | }
10 |
11 | @font-face {
12 | font-family: 'Source Sans Pro';
13 | src: url('./source-sans-pro-italic.eot');
14 | src: url('./source-sans-pro-italic.eot?#iefix') format('embedded-opentype'),
15 | url('./source-sans-pro-italic.woff') format('woff'),
16 | url('./source-sans-pro-italic.ttf') format('truetype');
17 | font-weight: normal;
18 | font-style: italic;
19 | }
20 |
21 | @font-face {
22 | font-family: 'Source Sans Pro';
23 | src: url('./source-sans-pro-semibold.eot');
24 | src: url('./source-sans-pro-semibold.eot?#iefix') format('embedded-opentype'),
25 | url('./source-sans-pro-semibold.woff') format('woff'),
26 | url('./source-sans-pro-semibold.ttf') format('truetype');
27 | font-weight: 600;
28 | font-style: normal;
29 | }
30 |
31 | @font-face {
32 | font-family: 'Source Sans Pro';
33 | src: url('./source-sans-pro-semibolditalic.eot');
34 | src: url('./source-sans-pro-semibolditalic.eot?#iefix') format('embedded-opentype'),
35 | url('./source-sans-pro-semibolditalic.woff') format('woff'),
36 | url('./source-sans-pro-semibolditalic.ttf') format('truetype');
37 | font-weight: 600;
38 | font-style: italic;
39 | }
40 |
--------------------------------------------------------------------------------
/reveal/plugin/highlight/monokai.css:
--------------------------------------------------------------------------------
1 | /*
2 | Monokai style - ported by Luigi Maselli - http://grigio.org
3 | */
4 |
5 | .hljs {
6 | display: block;
7 | overflow-x: auto;
8 | padding: 0.5em;
9 | background: #272822;
10 | color: #ddd;
11 | }
12 |
13 | .hljs-tag,
14 | .hljs-keyword,
15 | .hljs-selector-tag,
16 | .hljs-literal,
17 | .hljs-strong,
18 | .hljs-name {
19 | color: #f92672;
20 | }
21 |
22 | .hljs-code {
23 | color: #66d9ef;
24 | }
25 |
26 | .hljs-class .hljs-title {
27 | color: white;
28 | }
29 |
30 | .hljs-attribute,
31 | .hljs-symbol,
32 | .hljs-regexp,
33 | .hljs-link {
34 | color: #bf79db;
35 | }
36 |
37 | .hljs-string,
38 | .hljs-bullet,
39 | .hljs-subst,
40 | .hljs-title,
41 | .hljs-section,
42 | .hljs-emphasis,
43 | .hljs-type,
44 | .hljs-built_in,
45 | .hljs-builtin-name,
46 | .hljs-selector-attr,
47 | .hljs-selector-pseudo,
48 | .hljs-addition,
49 | .hljs-variable,
50 | .hljs-template-tag,
51 | .hljs-template-variable {
52 | color: #a6e22e;
53 | }
54 |
55 | .hljs-comment,
56 | .hljs-quote,
57 | .hljs-deletion,
58 | .hljs-meta {
59 | color: #75715e;
60 | }
61 |
62 | .hljs-keyword,
63 | .hljs-selector-tag,
64 | .hljs-literal,
65 | .hljs-doctag,
66 | .hljs-title,
67 | .hljs-section,
68 | .hljs-type,
69 | .hljs-selector-id {
70 | font-weight: bold;
71 | }
72 |
--------------------------------------------------------------------------------
/reveal/plugin/highlight/zenburn.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Zenburn style from voldmar.ru (c) Vladimir Epifanov
4 | based on dark.css by Ivan Sagalaev
5 |
6 | */
7 |
8 | .hljs {
9 | display: block;
10 | overflow-x: auto;
11 | padding: 0.5em;
12 | background: #3f3f3f;
13 | color: #dcdcdc;
14 | }
15 |
16 | .hljs-keyword,
17 | .hljs-selector-tag,
18 | .hljs-tag {
19 | color: #e3ceab;
20 | }
21 |
22 | .hljs-template-tag {
23 | color: #dcdcdc;
24 | }
25 |
26 | .hljs-number {
27 | color: #8cd0d3;
28 | }
29 |
30 | .hljs-variable,
31 | .hljs-template-variable,
32 | .hljs-attribute {
33 | color: #efdcbc;
34 | }
35 |
36 | .hljs-literal {
37 | color: #efefaf;
38 | }
39 |
40 | .hljs-subst {
41 | color: #8f8f8f;
42 | }
43 |
44 | .hljs-title,
45 | .hljs-name,
46 | .hljs-selector-id,
47 | .hljs-selector-class,
48 | .hljs-section,
49 | .hljs-type {
50 | color: #efef8f;
51 | }
52 |
53 | .hljs-symbol,
54 | .hljs-bullet,
55 | .hljs-link {
56 | color: #dca3a3;
57 | }
58 |
59 | .hljs-deletion,
60 | .hljs-string,
61 | .hljs-built_in,
62 | .hljs-builtin-name {
63 | color: #cc9393;
64 | }
65 |
66 | .hljs-addition,
67 | .hljs-comment,
68 | .hljs-quote,
69 | .hljs-meta {
70 | color: #7f9f7f;
71 | }
72 |
73 |
74 | .hljs-emphasis {
75 | font-style: italic;
76 | }
77 |
78 | .hljs-strong {
79 | font-weight: bold;
80 | }
81 |
--------------------------------------------------------------------------------
/reveal/plugin/math/math.esm.js:
--------------------------------------------------------------------------------
1 | function e(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function t(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function n(n){for(var r=1;r {
8 |
9 | // The reveal.js instance this plugin is attached to
10 | let deck;
11 |
12 | let defaultOptions = {
13 | messageStyle: 'none',
14 | tex2jax: {
15 | inlineMath: [ [ '$', '$' ], [ '\\(', '\\)' ] ],
16 | skipTags: [ 'script', 'noscript', 'style', 'textarea', 'pre' ]
17 | },
18 | skipStartupTypeset: true
19 | };
20 |
21 | function loadScript( url, callback ) {
22 |
23 | let head = document.querySelector( 'head' );
24 | let script = document.createElement( 'script' );
25 | script.type = 'text/javascript';
26 | script.src = url;
27 |
28 | // Wrapper for callback to make sure it only fires once
29 | let finish = () => {
30 | if( typeof callback === 'function' ) {
31 | callback.call();
32 | callback = null;
33 | }
34 | }
35 |
36 | script.onload = finish;
37 |
38 | // IE
39 | script.onreadystatechange = () => {
40 | if ( this.readyState === 'loaded' ) {
41 | finish();
42 | }
43 | }
44 |
45 | // Normal browsers
46 | head.appendChild( script );
47 |
48 | }
49 |
50 | return {
51 | id: 'math',
52 |
53 | init: function( reveal ) {
54 |
55 | deck = reveal;
56 |
57 | let revealOptions = deck.getConfig().math || {};
58 |
59 | let options = { ...defaultOptions, ...revealOptions };
60 | let mathjax = options.mathjax || 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js';
61 | let config = options.config || 'TeX-AMS_HTML-full';
62 | let url = mathjax + '?config=' + config;
63 |
64 | options.tex2jax = { ...defaultOptions.tex2jax, ...revealOptions.tex2jax };
65 |
66 | options.mathjax = options.config = null;
67 |
68 | loadScript( url, function() {
69 |
70 | MathJax.Hub.Config( options );
71 |
72 | // Typeset followed by an immediate reveal.js layout since
73 | // the typesetting process could affect slide height
74 | MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, deck.getRevealElement() ] );
75 | MathJax.Hub.Queue( deck.layout );
76 |
77 | // Reprocess equations in slides when they turn visible
78 | deck.on( 'slidechanged', function( event ) {
79 |
80 | MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] );
81 |
82 | } );
83 |
84 | } );
85 |
86 | }
87 | }
88 |
89 | };
90 |
91 | export default Plugin;
92 |
--------------------------------------------------------------------------------
/reveal/plugin/zoom/zoom.esm.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * reveal.js Zoom plugin
3 | */
4 | var e={id:"zoom",init:function(e){e.getRevealElement().addEventListener("mousedown",(function(o){var n=/Linux/.test(window.navigator.platform)?"ctrl":"alt",i=(e.getConfig().zoomKey?e.getConfig().zoomKey:n)+"Key",d=e.getConfig().zoomLevel?e.getConfig().zoomLevel:2;o[i]&&!e.isOverview()&&(o.preventDefault(),t.to({x:o.clientX,y:o.clientY,scale:d,pan:!1}))}))}},t=function(){var e=1,o=0,n=0,i=-1,d=-1,s="WebkitTransform"in document.body.style||"MozTransform"in document.body.style||"msTransform"in document.body.style||"OTransform"in document.body.style||"transform"in document.body.style;function r(t,o){var n=y();if(t.width=t.width||1,t.height=t.height||1,t.x-=(window.innerWidth-t.width*o)/2,t.y-=(window.innerHeight-t.height*o)/2,s)if(1===o)document.body.style.transform="",document.body.style.OTransform="",document.body.style.msTransform="",document.body.style.MozTransform="",document.body.style.WebkitTransform="";else{var i=n.x+"px "+n.y+"px",d="translate("+-t.x+"px,"+-t.y+"px) scale("+o+")";document.body.style.transformOrigin=i,document.body.style.OTransformOrigin=i,document.body.style.msTransformOrigin=i,document.body.style.MozTransformOrigin=i,document.body.style.WebkitTransformOrigin=i,document.body.style.transform=d,document.body.style.OTransform=d,document.body.style.msTransform=d,document.body.style.MozTransform=d,document.body.style.WebkitTransform=d}else 1===o?(document.body.style.position="",document.body.style.left="",document.body.style.top="",document.body.style.width="",document.body.style.height="",document.body.style.zoom=""):(document.body.style.position="relative",document.body.style.left=-(n.x+t.x)/o+"px",document.body.style.top=-(n.y+t.y)/o+"px",document.body.style.width=100*o+"%",document.body.style.height=100*o+"%",document.body.style.zoom=o);e=o,document.documentElement.classList&&(1!==e?document.documentElement.classList.add("zoomed"):document.documentElement.classList.remove("zoomed"))}function m(){var t=.12*window.innerWidth,i=.12*window.innerHeight,d=y();nwindow.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-n)/i)*(14/e)),owindow.innerWidth-t&&window.scroll(d.x+(1-(window.innerWidth-o)/t)*(14/e),d.y)}function y(){return{x:void 0!==window.scrollX?window.scrollX:window.pageXOffset,y:void 0!==window.scrollY?window.scrollY:window.pageYOffset}}return s&&(document.body.style.transition="transform 0.8s ease",document.body.style.OTransition="-o-transform 0.8s ease",document.body.style.msTransition="-ms-transform 0.8s ease",document.body.style.MozTransition="-moz-transform 0.8s ease",document.body.style.WebkitTransition="-webkit-transform 0.8s ease"),document.addEventListener("keyup",(function(o){1!==e&&27===o.keyCode&&t.out()})),document.addEventListener("mousemove",(function(t){1!==e&&(o=t.clientX,n=t.clientY)})),{to:function(o){if(1!==e)t.out();else{if(o.x=o.x||0,o.y=o.y||0,o.element){var n=o.element.getBoundingClientRect();o.x=n.left-20,o.y=n.top-20,o.width=n.width+40,o.height=n.height+40}void 0!==o.width&&void 0!==o.height&&(o.scale=Math.max(Math.min(window.innerWidth/o.width,window.innerHeight/o.height),1)),o.scale>1&&(o.x*=o.scale,o.y*=o.scale,r(o,o.scale),!1!==o.pan&&(i=setTimeout((function(){d=setInterval(m,1e3/60)}),800)))}},out:function(){clearTimeout(i),clearInterval(d),r({x:0,y:0},1),e=1},magnify:function(e){this.to(e)},reset:function(){this.out()},zoomLevel:function(){return e}}}();export default function(){return e}
5 |
--------------------------------------------------------------------------------
/reveal/plugin/zoom/zoom.js:
--------------------------------------------------------------------------------
1 | !function(e,o){"object"==typeof exports&&"undefined"!=typeof module?module.exports=o():"function"==typeof define&&define.amd?define(o):(e="undefined"!=typeof globalThis?globalThis:e||self).RevealZoom=o()}(this,(function(){"use strict";
2 | /*!
3 | * reveal.js Zoom plugin
4 | */var e={id:"zoom",init:function(e){e.getRevealElement().addEventListener("mousedown",(function(t){var n=/Linux/.test(window.navigator.platform)?"ctrl":"alt",i=(e.getConfig().zoomKey?e.getConfig().zoomKey:n)+"Key",d=e.getConfig().zoomLevel?e.getConfig().zoomLevel:2;t[i]&&!e.isOverview()&&(t.preventDefault(),o.to({x:t.clientX,y:t.clientY,scale:d,pan:!1}))}))}},o=function(){var e=1,t=0,n=0,i=-1,d=-1,s="WebkitTransform"in document.body.style||"MozTransform"in document.body.style||"msTransform"in document.body.style||"OTransform"in document.body.style||"transform"in document.body.style;function r(o,t){var n=l();if(o.width=o.width||1,o.height=o.height||1,o.x-=(window.innerWidth-o.width*t)/2,o.y-=(window.innerHeight-o.height*t)/2,s)if(1===t)document.body.style.transform="",document.body.style.OTransform="",document.body.style.msTransform="",document.body.style.MozTransform="",document.body.style.WebkitTransform="";else{var i=n.x+"px "+n.y+"px",d="translate("+-o.x+"px,"+-o.y+"px) scale("+t+")";document.body.style.transformOrigin=i,document.body.style.OTransformOrigin=i,document.body.style.msTransformOrigin=i,document.body.style.MozTransformOrigin=i,document.body.style.WebkitTransformOrigin=i,document.body.style.transform=d,document.body.style.OTransform=d,document.body.style.msTransform=d,document.body.style.MozTransform=d,document.body.style.WebkitTransform=d}else 1===t?(document.body.style.position="",document.body.style.left="",document.body.style.top="",document.body.style.width="",document.body.style.height="",document.body.style.zoom=""):(document.body.style.position="relative",document.body.style.left=-(n.x+o.x)/t+"px",document.body.style.top=-(n.y+o.y)/t+"px",document.body.style.width=100*t+"%",document.body.style.height=100*t+"%",document.body.style.zoom=t);e=t,document.documentElement.classList&&(1!==e?document.documentElement.classList.add("zoomed"):document.documentElement.classList.remove("zoomed"))}function m(){var o=.12*window.innerWidth,i=.12*window.innerHeight,d=l();nwindow.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-n)/i)*(14/e)),twindow.innerWidth-o&&window.scroll(d.x+(1-(window.innerWidth-t)/o)*(14/e),d.y)}function l(){return{x:void 0!==window.scrollX?window.scrollX:window.pageXOffset,y:void 0!==window.scrollY?window.scrollY:window.pageYOffset}}return s&&(document.body.style.transition="transform 0.8s ease",document.body.style.OTransition="-o-transform 0.8s ease",document.body.style.msTransition="-ms-transform 0.8s ease",document.body.style.MozTransition="-moz-transform 0.8s ease",document.body.style.WebkitTransition="-webkit-transform 0.8s ease"),document.addEventListener("keyup",(function(t){1!==e&&27===t.keyCode&&o.out()})),document.addEventListener("mousemove",(function(o){1!==e&&(t=o.clientX,n=o.clientY)})),{to:function(t){if(1!==e)o.out();else{if(t.x=t.x||0,t.y=t.y||0,t.element){var n=t.element.getBoundingClientRect();t.x=n.left-20,t.y=n.top-20,t.width=n.width+40,t.height=n.height+40}void 0!==t.width&&void 0!==t.height&&(t.scale=Math.max(Math.min(window.innerWidth/t.width,window.innerHeight/t.height),1)),t.scale>1&&(t.x*=t.scale,t.y*=t.scale,r(t,t.scale),!1!==t.pan&&(i=setTimeout((function(){d=setInterval(m,1e3/60)}),800)))}},out:function(){clearTimeout(i),clearInterval(d),r({x:0,y:0},1),e=1},magnify:function(e){this.to(e)},reset:function(){this.out()},zoomLevel:function(){return e}}}();return function(){return e}}));
5 |
--------------------------------------------------------------------------------