From 1f77e5d0acc1afb00a28ddd1b0206ffc658e1223 Mon Sep 17 00:00:00 2001
From: novalagung
Date: Tue, 23 Apr 2024 18:42:23 +0700
Subject: [PATCH 1/9] feat: update A.31. Channel
---
content/A-channel.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/content/A-channel.md b/content/A-channel.md
index 1f5836199..4f8414b80 100644
--- a/content/A-channel.md
+++ b/content/A-channel.md
@@ -131,9 +131,9 @@ Passing data bertipe channel lewat parameter sifatnya **pass by reference**, yan
![Parameter channel](images/A_channel_3_channel_param.png)
----
+## A.32.3 Penjelasan tambahan
-Berikut merupakan penjelasan tambahan untuk kode di atas.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
#### • Iterasi Data Slice/Array Langsung Pada Saat Inisialisasi
From 4be74cc5367d126160e505db6b7a878a4ca2f6c3 Mon Sep 17 00:00:00 2001
From: novalagung
Date: Tue, 23 Apr 2024 18:42:41 +0700
Subject: [PATCH 2/9] feat: update A.50. File
---
content/A-file.md | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/content/A-file.md b/content/A-file.md
index 2ab152a7b..58b386141 100644
--- a/content/A-file.md
+++ b/content/A-file.md
@@ -1,12 +1,12 @@
# A.50. File
-Ada beberapa cara yang bisa digunakan untuk operasi file di Go. Pada chapter ini kita akan mempelajari teknik yang paling dasar, yaitu dengan memanfaatkan `os.File`.
+Pada chapter ini kita akan belajar beberapa teknik operasi file yang paling dasar.
## A.50.1. Membuat File Baru
-Pembuatan file di Go sangatlah mudah, cukup dengan memanggil fungsi `os.Create()` lalu memasukkan path file yang ingin dibuat sebagai parameter. Jika ternyata file yang akan dibuat sudah ada, maka akan ditimpa. Bisa memanfaatkan `os.IsNotExist()` untuk mendeteksi apakah file sudah dibuat atau belum.
+Pembuatan file di Go sangat mudah, dilakukan dengan memanfaatkan fungsi `os.Create()` disertai path file sebagai argument pemanggilan fungsi. Jika ternyata file yang akan dibuat sudah ada duluan, maka operasi `os.Create()` akan menimpa file yang sudah ada dengan file baru. Untuk menghindari penimpaan file, gunakan fungsi `os.IsNotExist()` untuk mendeteksi apakah file yang ingin dibuat sudah ada atau belum.
-Berikut merupakan contoh pembuatan file.
+Contoh program operasi pembuatan file:
```go
package main
@@ -43,17 +43,17 @@ func main() {
}
```
-Fungsi `os.Stat()` mengembalikan 2 data, yaitu informasi tetang path yang dicari, dan error (jika ada). Masukkan error kembalian fungsi tersebut sebagai parameter fungsi `os.IsNotExist()`, untuk mendeteksi apakah file yang akan dibuat sudah ada. Jika belum ada, maka fungsi tersebut akan mengembalikan nilai `true`.
+Fungsi `os.Stat()` mengembalikan 2 data, yaitu informasi tetang path yang dicari, dan error (jika ada). Masukkan error kembalian fungsi tersebut sebagai argument pemanggilan fungsi `os.IsNotExist()`, untuk mengetahui apakah file yang akan dibuat sudah ada. Jika rupanya file belum ada ada, maka fungsi tersebut akan mengembalikan nilai `true`.
-Fungsi `os.Create()` digunakan untuk membuat file pada path tertentu. Fungsi ini mengembalikan objek `*os.File` dari file yang bersangkutan. File yang baru terbuat statusnya adalah otomatis **open**, maka dari itu perlu untuk di-**close** menggunakan method `file.Close()` setelah file tidak digunakan lagi.
+Fungsi `os.Create()` ini mengembalikan objek bertipe `*os.File`. File yang baru dibuat, statusnya adalah otomatis **open**. Setelah operasi file selesai, file harus di-**close** menggunakan method `file.Close()`.
-Membiarkan file terbuka ketika sudah tak lagi digunakan bukan hal yang baik, karena efeknya ke memory dan akses ke file itu sendiri, file akan di-lock sehingga tidak bisa digunakan oleh proses lain selama status file masih open atau belum di-close.
+Membiarkan file terbuka ketika sudah tak lagi digunakan adalah tidak baik, karena ada efek ke memory dan akses ke file itu sendiri, file menjadi terkunci/locked, membuatnya tidak bisa diakses oleh proses lain selama status file statusnya masih **open** dan belum di-close.
![Membuat file baru](images/A_file_1_create.png)
## A.50.2. Mengedit Isi File
-Untuk mengedit file, yang perlu dilakukan pertama adalah membuka file dengan level akses **write**. Setelah mendapatkan objek file-nya, gunakan method `WriteString()` untuk pengisian data. Terakhir panggil method `Sync()` untuk menyimpan perubahan.
+Untuk mengedit file, yang pertama perlu dilakukan adalah membuka file dengan level akses **write**. Setelah mendapatkan objek file-nya, gunakan method `WriteString()` untuk penulisan data. Di akhir, panggil method `Sync()` untuk menyimpan perubahan.
```go
func writeFile() {
@@ -80,13 +80,13 @@ func main() {
}
```
-Pada program di atas, file dibuka dengan level akses **read** dan **write** dengan kode permission **0664**. Setelah itu, beberapa string diisikan ke dalam file tersebut menggunakan `WriteString()`. Di akhir, semua perubahan terhadap file akan disimpan dengan dipanggilnya `Sync()`.
+Pada program di atas, file dibuka dengan level akses **read** dan **write** dengan kode permission **0664**. Setelah itu, beberapa string diisikan ke dalam file tersebut menggunakan `WriteString()`. Di akhir, semua perubahan terhadap file menjadi tersimpan dengan adanya pemanggilan method `Sync()`.
![Mengedit file](images/A_file_2_write.png)
## A.50.3. Membaca Isi File
-File yang ingin dibaca harus dibuka terlebih dahulu menggunakan fungsi `os.OpenFile()` dengan level akses minimal adalah **read**. Setelah itu, gunakan method `Read()` dengan parameter adalah variabel, yang di mana hasil proses baca akan disimpan ke variabel tersebut.
+File yang ingin dibaca harus dibuka terlebih dahulu menggunakan fungsi `os.OpenFile()` dengan level akses minimal adalah **read**. Dari object file kembalian fungsi tersebut, gunakan method `Read()` dengan disertai argument berupa variabel yang akan menampung data hasil operasi baca.
```go
// tambahkan di bagian import package io
@@ -120,21 +120,21 @@ func main() {
}
```
-Pada kode di atas `os.OpenFile()` digunakan untuk membuka file. Fungsi tersebut memiliki beberapa parameter.
+Fungsi `os.OpenFile()` dalam pemanggilannya memerlukan beberapa argument parameter untuk di-isi:
1. Parameter pertama adalah path file yang akan dibuka.
2. Parameter kedua adalah level akses. `os.O_RDONLY` maksudnya adalah **read only**.
3. Parameter ketiga adalah permission file-nya.
-Variabel `text` disiapkan bertipe slice `[]byte` dengan alokasi elemen 1024. Variabel tersebut bertugas menampung data hasil statement `file.Read()`. Proses pembacaan file akan dilakukan terus menerus, berurutan dari baris pertama hingga akhir.
+Variabel `text` disiapkan bertipe slice `[]byte` dengan alokasi elemen `1024`. Variabel tersebut bertugas menampung data hasil statement `file.Read()`. Proses pembacaan file dilakukan terus menerus, berurutan dari baris pertama hingga akhir.
-Error yang muncul ketika eksekusi `file.Read()` akan di-filter, ketika error tersebut adalah selain `io.EOF` maka proses baca file akan berlanjut. Error `io.EOF` sendiri menandakan bahwa file yang sedang dibaca adalah baris terakhir isi atau **end of file**.
+Error yang muncul ketika eksekusi `file.Read()` akan di-filter, ketika error adalah selain `io.EOF` maka proses baca file akan berlanjut. Error `io.EOF` sendiri menandakan bahwa file yang sedang dibaca adalah baris terakhir isi atau **end of file**.
![Membaca isi file](images/A_file_3_read.png)
## A.50.4. Menghapus File
-Cara menghapus file sangatlah mudah, cukup panggil fungsi `os.Remove()`, masukan path file yang ingin dihapus sebagai parameter.
+Operasi menghapus file dilakukan via fungsi `os.Remove()`. Panggil fungsi tersebut, kemudian isi path dari file yang ingin dihapus sebagai argument fungsi.
```go
func deleteFile() {
From 396dcce4d71d6b730600e5c3d938220be4d9a269 Mon Sep 17 00:00:00 2001
From: novalagung
Date: Tue, 23 Apr 2024 18:53:11 +0700
Subject: [PATCH 3/9] feat: semantic markdown update
---
content/2-instalasi-golang.md | 6 ++--
content/A-buffered-channel.md | 2 +-
content/A-channel-range-close.md | 6 ++--
content/A-channel.md | 6 ++--
content/A-concurrency-pipeline.md | 10 +++----
content/A-data-type-conversion.md | 16 +++++-----
content/A-pipeline-context-cancellation.md | 26 ++++++++--------
.../A-simplified-fan-in-fan-out-pipeline.md | 26 ++++++++--------
content/A-variabel.md | 2 +-
content/B-golang-web-hello-world.md | 6 ++--
content/B-http-basic-auth.md | 4 +--
content/B-simple-configuration.md | 10 +++----
content/C-cors-preflight-request.md | 12 ++++----
content/C-dockerize-golang.md | 30 +++++++++----------
content/C-echo-routing.md | 16 +++++-----
content/C-golang-aws-s3.md | 4 +--
content/C-golang-grpc-protobuf.md | 14 ++++-----
content/C-golang-jwt.md | 12 ++++----
content/C-golang-ldap-authentication.md | 8 ++---
content/C-golang-protobuf-implementation.md | 10 +++----
content/C-golang-sso-saml-sp.md | 4 +--
content/C-https-tls.md | 8 ++---
.../C-parsing-http-request-payload-echo.md | 8 ++---
content/C-secure-middleware.md | 10 +++----
content/C-write-pdf-file.md | 8 ++---
...ert-1mil-csv-record-into-db-in-a-minute.md | 20 ++++++-------
26 files changed, 142 insertions(+), 142 deletions(-)
diff --git a/content/2-instalasi-golang.md b/content/2-instalasi-golang.md
index 0e4feb93a..82025dc35 100644
--- a/content/2-instalasi-golang.md
+++ b/content/2-instalasi-golang.md
@@ -10,7 +10,7 @@ URL untuk mengunduh *installer* Go: https://golang.org/dl/. Silakan langsung und
## A.2.1. Instalasi Go *Stable*
-#### • Instalasi Go di Windows
+#### ◉ Instalasi Go di Windows
1. Download terlebih dahulu *installer*-nya di [https://golang.org/dl/](https://golang.org/dl/). Pilih *installer* untuk sistem operasi Windows sesuai jenis bit yang digunakan.
@@ -26,7 +26,7 @@ URL untuk mengunduh *installer* Go: https://golang.org/dl/. Silakan langsung und
> Sering terjadi, command `go version` tidak bisa dijalankan meskipun instalasi sukses. Solusinya bisa dengan restart CMD (tutup CMD, kemudian buka lagi). Setelah itu coba jalankan ulang command di atas.
-#### • Instalasi Go di MacOS
+#### ◉ Instalasi Go di MacOS
Cara termudah instalasi Go di MacOS adalah menggunakan [Homebrew](http://brew.sh/).
@@ -57,7 +57,7 @@ Cara termudah instalasi Go di MacOS adalah menggunakan [Homebrew](http://brew.sh
5. Jika output adalah sama dengan versi Go yang ter-*install*, menandakan proses instalasi berhasil.
-#### • Instalasi Go di Linux
+#### ◉ Instalasi Go di Linux
1. Unduh arsip *installer* dari [https://golang.org/dl/](https://golang.org/dl/), pilih installer untuk Linux yang sesuai dengan jenis bit komputer anda. Proses download bisa dilakukan lewat CLI, menggunakan `wget` atau `curl`.
diff --git a/content/A-buffered-channel.md b/content/A-buffered-channel.md
index 9a1af3024..b5e16bf82 100644
--- a/content/A-buffered-channel.md
+++ b/content/A-buffered-channel.md
@@ -59,7 +59,7 @@ Pengiriman data indeks ke 0, 1, 2 dan 3 akan berjalan secara asynchronous, hal i
Karena pengiriman dan penerimaan data via buffered channel terjadi tidak selalu synchronous (tergantung jumlah buffer-nya), maka ada kemungkinan dimana eksekusi program selesai namun tidak semua data diterima via channel `messages`. Karena alasan ini pada bagian akhir ditambahkan statement `time.Sleep(1 * time.Second)` agar ada jeda 1 detik sebelum program selesai.
-#### • Fungsi `time.Sleep()`
+#### ◉ Fungsi `time.Sleep()`
Fungsi ini digunakan untuk menambahkan delay sebelum statement berikutnya dieksekusi. Durasi delay ditentukan oleh parameter, misal `1 * time.Second` maka durasi delay adalah 1 detik.
diff --git a/content/A-channel-range-close.md b/content/A-channel-range-close.md
index 7615cd947..9ce0ee6a7 100644
--- a/content/A-channel-range-close.md
+++ b/content/A-channel-range-close.md
@@ -48,11 +48,11 @@ Setelah 20 data yang dikirim sukses diterima, channel `ch` di-non-aktifkan denga
![Penerapan for-range-close pada channel](images/A_channel_range_close_1_for_range_close.png)
----
+## A.34.2. Penjelasan tambahan
-Berikut adalah penjelasan tambahan mengenai channel.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-## A.34.1.1. Channel Direction
+#### ◉ Channel Direction
Go mendesain API channel untuk mendukung level akses channel, apakah hanya sebagai penerima, pengirim, atau penerima sekaligus pengirim. Konsep ini disebut dengan **channel direction**.
diff --git a/content/A-channel.md b/content/A-channel.md
index 4f8414b80..583ada27a 100644
--- a/content/A-channel.md
+++ b/content/A-channel.md
@@ -131,11 +131,11 @@ Passing data bertipe channel lewat parameter sifatnya **pass by reference**, yan
![Parameter channel](images/A_channel_3_channel_param.png)
-## A.32.3 Penjelasan tambahan
+## A.32.3. Penjelasan tambahan
Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-#### • Iterasi Data Slice/Array Langsung Pada Saat Inisialisasi
+#### ◉ Iterasi Data Slice/Array Langsung Pada Saat Inisialisasi
Data slice yang baru di inisialisasi bisa langsung di-iterasi, caranya mudah dengan menuliskannya langsung setelah keyword `range`.
@@ -145,7 +145,7 @@ for _, each := range []string{"wick", "hunt", "bourne"} {
}
```
-#### • Eksekusi Goroutine Pada IIFE
+#### ◉ Eksekusi Goroutine Pada IIFE
Eksekusi goroutine tidak harus pada fungsi atau closure yang sudah terdefinisi. Sebuah IIFE juga bisa dijalankan sebagai goroutine baru. Caranya dengan langsung menambahkan keyword `go` pada waktu deklarasi-eksekusi IIFE-nya.
diff --git a/content/A-concurrency-pipeline.md b/content/A-concurrency-pipeline.md
index 4aaab1039..d842a7649 100644
--- a/content/A-concurrency-pipeline.md
+++ b/content/A-concurrency-pipeline.md
@@ -260,7 +260,7 @@ Pada bagian ini kita akan re-write ulang program 2, isinya masih sama persis kal
Kenapa kita pecah, karena ketiga proses tersebut bisa dijalankan bersama secara konkuren, dalam artian misalnya ketika `file1` sudah selesai dibaca, perhitungan md5sum-nya bisa dijalankan secara bersama dengan pembacaan `file2`. Begitu juga untuk proses rename-nya, misalnya, proses rename `file24` bisa dijalnkan secara konkuren bersamaan dengan proses hitung md5sum `file22` dan bersamaan dengan proses baca `file28`.
-#### • Basis Kode Program
+#### ◉ Basis Kode Program
Mungkin agar lebih terlihat perbandingannya nanti di akhir, kita siapkan file terpisah saja untuk program ini. Siapkan file baru bernama `3-find-md5-sum-of-file-then-rename-it-concurrently.go`.
@@ -291,7 +291,7 @@ type FileInfo struct {
Kurang lebih sama seperti sebelumnya, hanya saja ada beberapa packages lain yg di-import dan ada struct `FileInfo`. Struct ini digunakan sebagai metadata tiap file. Karena nantinya proses read file, md5sum, dan rename file akan dipecah menjadi 3 goroutine berbeda, maka perlu ada metadata untuk mempermudah tracking file, agar nanti ketika dapat md5 sum nya tidak salah simpan, dan ketika rename tidak salah file.
-#### • Pipeline 1: Baca File
+#### ◉ Pipeline 1: Baca File
Siapkan fungsi main, lalu panggil fungsi `readFiles()`.
@@ -359,7 +359,7 @@ Mengenai channel `chanOut` sendiri, akan di-close ketika dipastikan **semua file
Ok lanjut, karena di sini ada channel yang digunakan sebagai media pengiriman data (`FileInfo`), maka juga harus ada penerima data channel-nya dong. Yups.
-#### • Pipeline 2: MD5 Hash Konten File
+#### ◉ Pipeline 2: MD5 Hash Konten File
Tepat di bawah pipeline 1, tambahkan pemanggilan fungsi `getSum()` sebanyak 3x, bisa lebih banyak sih sebenarnya, bebas. Kemudian jadikan nilai balik pemanggilan fungsi tersebut sebagai variadic argument pemanggilan fungsi `mergeChanFileInfo()`.
@@ -447,7 +447,7 @@ Secara garis besar, pada fungsi ini terjadi beberapa proses:
* Channel `chanOut` ini dijadikan sebagai nilai balik fungsi.
* Di situ kita gunakan `sync.WaitGroup` untuk kontrol goroutine. Kita akan tunggu hingga semua channel input adalah closed, setelah itu barulah kita close channel `chanOut` ini.
-#### • Pipeline 3: Rename file
+#### ◉ Pipeline 3: Rename file
Tambahkan statement pipeline ketiga, yaitu pemanggilan fungsi Fan-out `rename()`, lalu panggil fungsi Fan-in `mergeChanFileInfo()` untuk multiplex channel kembalian fungsi `rename()`.
@@ -493,7 +493,7 @@ Bisa dilihat di atas kita rename file asli yang informasi path-nya ada di `FileI
Setelah semua file berhasil di-rename, maka channel `chanOut` di-close.
-#### • Pipeline 4 / Output
+#### ◉ Pipeline 4 / Output
Serangkaian proses yang sudah kita setup punya ketergantungan tinggi satu sama lain, dan eksekusinya harus berurutan meskipun *concurrently*. Ini secara langsung juga mempermudah kita dalam mengolah output hasil pipeline. Kita cukup fokus ke channel hasil Fan-in yang paling terakhir, yaitu channel `chanRename`.
diff --git a/content/A-data-type-conversion.md b/content/A-data-type-conversion.md
index 1bfef034c..135d4f3ba 100644
--- a/content/A-data-type-conversion.md
+++ b/content/A-data-type-conversion.md
@@ -6,7 +6,7 @@ Di beberapa chapter sebelum ini kita telah menerapkan beberapa cara konversi dat
Package `strconv` berisi banyak fungsi yang sangat membantu kita untuk melakukan konversi. Berikut merupakan beberapa fungsi yang dalam package tersebut.
-#### • Fungsi `strconv.Atoi()`
+#### ◉ Fungsi `strconv.Atoi()`
Fungsi ini digunakan untuk konversi data dari tipe `string` ke `int`. `strconv.Atoi()` menghasilkan 2 buah nilai kembalian, yaitu hasil konversi dan `error` (jika konversi sukses, maka `error` berisi `nil`).
@@ -26,7 +26,7 @@ func main() {
}
```
-#### • Fungsi `strconv.Itoa()`
+#### ◉ Fungsi `strconv.Itoa()`
Merupakan kebalikan dari `strconv.Atoi`, berguna untuk konversi `int` ke `string`.
@@ -37,7 +37,7 @@ var str = strconv.Itoa(num)
fmt.Println(str) // "124"
```
-#### • Fungsi `strconv.ParseInt()`
+#### ◉ Fungsi `strconv.ParseInt()`
Digunakan untuk konversi `string` berbentuk numerik dengan basis tertentu ke tipe numerik non-desimal dengan lebar data bisa ditentukan.
@@ -63,7 +63,7 @@ if err == nil {
}
```
-#### • Fungsi `strconv.FormatInt()`
+#### ◉ Fungsi `strconv.FormatInt()`
Berguna untuk konversi data numerik `int64` ke `string` dengan basis numerik bisa ditentukan sendiri.
@@ -74,7 +74,7 @@ var str = strconv.FormatInt(num, 8)
fmt.Println(str) // 30
```
-#### • Fungsi `strconv.ParseFloat()`
+#### ◉ Fungsi `strconv.ParseFloat()`
Digunakan untuk konversi `string` ke numerik desimal dengan lebar data bisa ditentukan.
@@ -89,7 +89,7 @@ if err == nil {
Pada contoh di atas, string `"24.12"` dikonversi ke float dengan lebar tipe data `float32`. Hasil konversi `strconv.ParseFloat` adalah sesuai dengan standar [IEEE Standard for Floating-Point Arithmetic](https://en.wikipedia.org/wiki/IEEE_floating_point).
-#### • Fungsi `strconv.FormatFloat()`
+#### ◉ Fungsi `strconv.FormatFloat()`
Berguna untuk konversi data bertipe `float64` ke `string` dengan format eksponen, lebar digit desimal, dan lebar tipe data bisa ditentukan.
@@ -113,7 +113,7 @@ Ada beberapa format eksponen yang bisa digunakan. Detailnya bisa dilihat di tabe
| `g` | Akan menggunakan format eksponen `e` untuk eksponen besar dan `f` untuk selainnya |
| `G` | Akan menggunakan format eksponen `E` untuk eksponen besar dan `f` untuk selainnya |
-#### • Fungsi `strconv.ParseBool()`
+#### ◉ Fungsi `strconv.ParseBool()`
Digunakan untuk konversi `string` ke `bool`.
@@ -126,7 +126,7 @@ if err == nil {
}
```
-#### • Fungsi `strconv.FormatBool()`
+#### ◉ Fungsi `strconv.FormatBool()`
Digunakan untuk konversi `bool` ke `string`.
diff --git a/content/A-pipeline-context-cancellation.md b/content/A-pipeline-context-cancellation.md
index 442227033..e904ca11e 100644
--- a/content/A-pipeline-context-cancellation.md
+++ b/content/A-pipeline-context-cancellation.md
@@ -19,7 +19,7 @@ Jadi kurang lebih akan ada dua result:
Ok langsung saja, pertama yang perlu dipersiapkan adalah tulis dulu kode program versi *concurrent* tanpa *cancellation*. Bisa langsung copy-paste, atau tulis dari awal dengan mengikut tutorial ini secara keseluruhan. Untuk penjelasan detail program versi sekuensial silakan merujuk ke chapter sebelumnya saja, di sini kita tulis langsung agar bisa cepat dimulai bagian program konkuren.
-#### • Import Packages dan Definisi Variabel
+#### ◉ Import Packages dan Definisi Variabel
```go
package main
@@ -40,7 +40,7 @@ const contentLength = 5000
var tempPath = filepath.Join(os.Getenv("TEMP"), "chapter-A.61-pipeline-cancellation-context")
```
-#### • Definisi struct `FileInfo`
+#### ◉ Definisi struct `FileInfo`
```go
type FileInfo struct {
@@ -51,7 +51,7 @@ type FileInfo struct {
}
```
-#### • Fungsi `main()`
+#### ◉ Fungsi `main()`
```go
func main() {
@@ -65,7 +65,7 @@ func main() {
}
```
-#### • Fungsi `randomString()`
+#### ◉ Fungsi `randomString()`
```go
func randomString(length int) string {
@@ -82,7 +82,7 @@ func randomString(length int) string {
}
```
-#### • Fungsi `generateFiles()`
+#### ◉ Fungsi `generateFiles()`
```go
func generateFiles() {
@@ -111,7 +111,7 @@ func generateFiles() {
log.Printf("%d/%d of total files created", counterSuccess, counterTotal)
}
```
-#### • Fungsi `generateFileIndexes()`
+#### ◉ Fungsi `generateFileIndexes()`
```go
func generateFileIndexes() <-chan FileInfo {
@@ -131,7 +131,7 @@ func generateFileIndexes() <-chan FileInfo {
}
```
-#### • Fungsi `createFiles()`
+#### ◉ Fungsi `createFiles()`
```go
func createFiles(chanIn <-chan FileInfo, numberOfWorkers int) <-chan FileInfo {
@@ -179,7 +179,7 @@ Hasil eksekusi program:
Ok, sekarang kita akan refactor kode tersebut, kita tambahkan mekanisme *cancellation* menggunakan `context.Context` API. Silakan duplikasi file program, lalu ikuti petunjuk berikut.
-#### • Import package `context`
+#### ◉ Import package `context`
Tambahkan package `context` dalam block import packages.
@@ -191,7 +191,7 @@ import (
)
```
-#### • Tambahkan definisi konstanta timeout
+#### ◉ Tambahkan definisi konstanta timeout
Di sini saya tentukan timeout adalah 3 detik. Nantinya kita akan modifikasi angka timeout untuk keperluan testing.
@@ -199,7 +199,7 @@ Di sini saya tentukan timeout adalah 3 detik. Nantinya kita akan modifikasi angk
const timeoutDuration = 3 * time.Second
```
-#### • Penerapan context di fungsi `main()`
+#### ◉ Penerapan context di fungsi `main()`
Pada fungsi main, lakukan sedikit perubahan. Yang sebelumnya ada statement berikut:
@@ -253,7 +253,7 @@ Jadi pada contoh yang kita tulis di atas, kurang lebih yang akan dilakukan adala
* Fungsi `generateFilesWithContext()` dipanggil dengan disisipkan object context.
* Callback `context.CancelFunc` dipanggil secara deferred. Ini merupakan idiomatic Go dalam penerapan context. Meskipun context sudah punya timeout atau deadline dan kita tidak perlu meng-*cancel* context secara manual, sangat dianjurkan untuk tetap memanggil callback `cancel()` tersebut secara deferred.
-#### • Modifikasi fungsi `generateFiles()`
+#### ◉ Modifikasi fungsi `generateFiles()`
Isi dari fungsi `generateFiles()` kita ubah menjadi pemanggilan fungsi `generateFilesWithContext()` dengan parameter context kosong.
@@ -319,7 +319,7 @@ Nah jadi lewat seleksi kondisi 2 case di atas, kita bisa dengan mudah mengidenti
Selain beberapa hal yang sudah saya sampaikan, ada *minor changes* lainnya, yaitu pada pemanggilan fungsi `generateFileIndexes()` dan `createFiles()` ditambahkan argument context.
-#### • Penambahan context pada fungsi `generateFiles()`
+#### ◉ Penambahan context pada fungsi `generateFiles()`
Kenapa ini perlu? karena **meski eksekusi fungsi `generateFilesWithContext()` otomatis di stop ketika cancelled, proses di dalamnya akan tetap berjalan jika tidak di-*handle* dengan baik *cancellation*-nya.**
@@ -355,7 +355,7 @@ Dibanding sebelumnya, perbedaannya adalah ada *channel selection*. Jadi di bagia
* Jika ada notif cancel paksa, maka case pertama akan terpenuhi, dan perulangan di-`break`.
* Selebihnya, pengiriman jobs akan berlangsung seperti normalnya.
-#### • Penambahan context pada fungsi `createFiles()`
+#### ◉ Penambahan context pada fungsi `createFiles()`
Hal yang sama (cancel di level sub prosees) juga perlu diterapkan pada `createFiles()`, karena jika tidak, maka proses pembuatan file akan tetap berjalan sesuai dengan jumlah jobs yang dikirim meskipun sudah di-cancel secara paksa.
diff --git a/content/A-simplified-fan-in-fan-out-pipeline.md b/content/A-simplified-fan-in-fan-out-pipeline.md
index c01ada997..63da60969 100644
--- a/content/A-simplified-fan-in-fan-out-pipeline.md
+++ b/content/A-simplified-fan-in-fan-out-pipeline.md
@@ -20,7 +20,7 @@ Ok langsung saja, pertama yang perlu dipersiapkan adalah tulis dulu kode program
Siapkan folder project baru, isinya satu buah file `1-generate-dummy-files-sequentially.go`.
-#### • Import Packages dan Definisi Variabel
+#### ◉ Import Packages dan Definisi Variabel
```go
package main
@@ -40,7 +40,7 @@ const contentLength = 5000
var tempPath = filepath.Join(os.Getenv("TEMP"), "chapter-A.60-worker-pool")
```
-#### • Fungsi `main()`
+#### ◉ Fungsi `main()`
```go
func main() {
@@ -54,7 +54,7 @@ func main() {
}
```
-#### • Fungsi `randomString()`
+#### ◉ Fungsi `randomString()`
```go
func randomString(length int) string {
@@ -71,7 +71,7 @@ func randomString(length int) string {
```
-#### • Fungsi `generateFiles()`
+#### ◉ Fungsi `generateFiles()`
```go
func generateFiles() {
@@ -103,7 +103,7 @@ Kita lanjut dulu saja. Berikut adalah output jika program di atas di-run.
Sekarang saya buat file program `2-generate-dummy-files-concurrently.go` yang isinya adalah sama yaitu untuk keperluan generate dummy files, tapi pembuatannya dilakukan secara konkuren.
-#### • Import Packages dan Definisi Variabel
+#### ◉ Import Packages dan Definisi Variabel
Import beberapa hal pada file baru ini, lalu definisikan beberapa variabel juga.
@@ -126,7 +126,7 @@ const contentLength = 5000
var tempPath = filepath.Join(os.Getenv("TEMP"), "chapter-A.60-worker-pool")
```
-#### • Definisi struct `FileInfo`
+#### ◉ Definisi struct `FileInfo`
Kita perlu siapkan struct baru bernama `FileInfo`, struct ini digunakan sebagai skema payload data ketika dikirimkan via channel dari goroutine jobs ke goroutine worker.
@@ -143,7 +143,7 @@ type FileInfo struct {
* Property `WorkerIndex` digunakan sebagai penanda worker mana yang akan melakukan operasi pembuatan file tersebut.
* Property `Err` default isinya kosong. Nantinya akan diisi dengan objek error ketika ada error saat pembuatan file.
-#### • Fungsi `main()`
+#### ◉ Fungsi `main()`
```go
func main() {
@@ -157,7 +157,7 @@ func main() {
}
```
-#### • Fungsi `randomString()`
+#### ◉ Fungsi `randomString()`
```go
func randomString(length int) string {
@@ -173,7 +173,7 @@ func randomString(length int) string {
}
```
-#### • Fungsi `generateFiles()`
+#### ◉ Fungsi `generateFiles()`
```go
func generateFiles() {
@@ -215,7 +215,7 @@ Fungsi `createFiles()` di sini merupakan fungsi **Fan-out Fan-in** karena meneri
Fungsi `createFiles()` menghasilkan channel yang isinya merupakan result dari operasi tiap-tiap jobs. Dari data yang dilewatkan via channel tersebut akan ketahuan misal ada error atau tidak saat pembuatan files. Channel tersebut kemudian di-loop lalu ditampilkan tiap-tiap result-nya.
-#### • Fungsi `generateFileIndexes()`
+#### ◉ Fungsi `generateFileIndexes()`
Fungsi ini merupakan fungsi Fan-out distribusi jobs. Di dalamnya dilakukan perulangan sejumlah `totalFile`, kemudian data tiap index digunakan untuk pembentukan filename lalu dikirim ke channel outputnya.
@@ -239,7 +239,7 @@ func generateFileIndexes() <-chan FileInfo {
Setelah dipastikan semua job terkirim, kita close channel output `chanOut` tersebut.
-#### • Fungsi `dispatchWorkers()`
+#### ◉ Fungsi `dispatchWorkers()`
Bagian ini merupakan yang paling butuh *effort* untuk dipahami. Jadi fungsi `createFiles()` seperti yang sudah saja jelaskan secara singkat di atas, fungsi ini merupakan fungsi gabungan Fan-out (menerima channel output dari pipeline sebelumnya) dan juga Fan-in (menjalankan beberapa worker untuk memproses channel output dari pipeline sebelumnya, lalu output masing-masing worker yang juga merupakan channel - langsung di merge jadi satu channel saja).
@@ -315,13 +315,13 @@ Semoga cukup jelas ya. Kelebihan metode ini ini salah satunya adalah kita bisa d
Saya akan coba jalankan program pertama dan kedua, lalu mari kita lihat perbedaannya.
-#### • Program Generate Dummy File *Sequentially*
+#### ◉ Program Generate Dummy File *Sequentially*
![Generate dummy files sequentially](images/A_simplified_fan_in_fan_out_pipeline_2_benchmark.png)
Testing di awal chapter ini hasilnya butuh sekitar **19 detik** untuk menyelesaikan generate dummy files sebanyak 3000 secara sekuensial. Tapi kali ini lebih lambat, yaitu **23 detik** dan ini wajar, karena di tiap operasi kita munculkan log ke stdout (via `log.Println()`).
-#### • Program Generate Dummy File *Concurrently*
+#### ◉ Program Generate Dummy File *Concurrently*
![Generate dummy files concurrently](images/A_simplified_fan_in_fan_out_pipeline_3_concurrent.png)
diff --git a/content/A-variabel.md b/content/A-variabel.md
index 39e017488..ff265bcc7 100644
--- a/content/A-variabel.md
+++ b/content/A-variabel.md
@@ -51,7 +51,7 @@ var firstName string = "john"
Nilai variabel bisa di-isi langsung pada saat deklarasi variabel.
-#### • Penggunaan Fungsi `fmt.Printf()`
+#### ◉ Penggunaan Fungsi `fmt.Printf()`
Fungsi ini digunakan untuk menampilkan output dalam bentuk tertentu. Kegunaannya sama seperti fungsi `fmt.Println()`, hanya saja struktur outputnya didefinisikan di awal.
diff --git a/content/B-golang-web-hello-world.md b/content/B-golang-web-hello-world.md
index 9f0b213df..6acbd481a 100644
--- a/content/B-golang-web-hello-world.md
+++ b/content/B-golang-web-hello-world.md
@@ -69,7 +69,7 @@ Cek pada browser rute yang sudah dibuat, output akan muncul.
Berikut merupakan penjelasan detail per-bagian program yang telah kita buat dari contoh di atas.
-#### • Penggunaan `http.HandleFunc()`
+#### ◉ Penggunaan `http.HandleFunc()`
Fungsi ini digunakan untuk **routing**, menentukan aksi dari sebuah url tertentu ketika diakses (di sini url tersebut kita sebut sebagai rute/route). Rute dituliskan dalam `string` sebagai parameter pertama, dan aksi-nya sendiri dibungkus dalam fungsi (bisa berupa closure) yang ditempatkan pada parameter kedua (kita sebut sebagai handler).
@@ -83,7 +83,7 @@ Ketika rute-rute tersebut diakses lewat browser, outpunya adalah isi-handler dar
> Pada contoh di atas, ketika rute yang tidak terdaftar diakses, secara otomatis handler rute `/` akan terpanggil.
-#### • Penjelasan Mengenai **Handler**
+#### ◉ Penjelasan Mengenai **Handler**
Route handler atau handler atau parameter kedua fungsi `http.HandleFunc()`, adalah sebuah fungsi dengan ber-skema `func (ResponseWriter, *Request)`.
@@ -105,7 +105,7 @@ Pada contoh program yang telah kita buat, handler `Index()` memunculkan text `"W
Sebuah handler bisa dipergunakan pada banyak rute, bisa dilihat pada di atas handler `Index()` digunakan pada rute `/` dan `/index`.
-#### • Penggunaan `http.ListenAndServe()`
+#### ◉ Penggunaan `http.ListenAndServe()`
Fungsi ini digunakan untuk membuat web server baru. Pada contoh yang telah dibuat, web server di-*start* pada port `9000` (bisa dituliskan dalam bentuk `localhost:9000` atau cukup `:9000` saja).
diff --git a/content/B-http-basic-auth.md b/content/B-http-basic-auth.md
index 91d0f47ab..5350c63e3 100644
--- a/content/B-http-basic-auth.md
+++ b/content/B-http-basic-auth.md
@@ -149,7 +149,7 @@ func init() {
Selanjutnya, kita perlu menyiapkan beberapa fungsi yg digunakan pada `main.go`, yaitu `Auth()` dan `AllowOnlyGET()`.
-#### • Fungsi `Auth()`
+#### ◉ Fungsi `Auth()`
Buka `middleware.go`, siapkan fungsi `Auth()`.
@@ -188,7 +188,7 @@ Fungsi `r.BasicAuth()` mengembalikan 3 informasi:
Jika basic auth request tidak valid, maka tampilkan pesan error sebagai nilai balik. Sedangkan jika basic auth adalah valid, maka dilanjutkan ke proses otentikasi, mengecek apakah username dan password yang dikirim cocok dengan username dan password yang ada di aplikasi kita.
-#### • Fungsi `AllowOnlyGET()`
+#### ◉ Fungsi `AllowOnlyGET()`
Fungsi ini bertugas untuk memastikan bahwa request yang diperbolehkan hanya yang ber-method `GET`. Selainnya, maka akan dianggap invalid request.
diff --git a/content/B-simple-configuration.md b/content/B-simple-configuration.md
index 8516fea7e..223e19d31 100644
--- a/content/B-simple-configuration.md
+++ b/content/B-simple-configuration.md
@@ -202,11 +202,11 @@ Coba ubah konfigurasi pada `config.json` nilai `log.verbose` menjadi `false`. La
Ok, kita telah selesai belajar tentang cara membuat file konfigurasi yang mudah dibaca dan praktis. Namun penerapan kontrol konfigurasi dengan metode ini kurang dianjurkan karena beberapa hal:
-#### • Tidak mendukung komentar
+#### ◉ Tidak mendukung komentar
Komentar sangat penting karena untuk aplikasi besar yang konfigurasi item-nya sangat banyak - akan susah untuk dipahami. Sebenarnya perihal ini bisa di-*resolve* menggunakan jenis konfigurasi lain seperti `YAML`, `.env`, atau lainnya.
-#### • Nilai konfigurasi harus diketahui di awal
+#### ◉ Nilai konfigurasi harus diketahui di awal
Kita harus tau semua value tiap-tiap konfigurasi terlebih dahulu, dan dituliskan ke file, sebelum aplikasi di-up. Dari sini akan sangat susah jika misal ada beberapa konfigurasi yang kita tidak tau nilainya tapi tau cara pengambilannya.
@@ -214,19 +214,19 @@ Contohnya pada beberapa kasus, seperti di AWS, database server yang di-setup sec
Dengan ini akan sangat susah jika kita harus cari terlebih dahulu value konfigurasi tersebut untuk kemudian dituliskan ke file. Memakan waktu dan kurang baik dari banyak sisi.
-#### • Tidak terpusat
+#### ◉ Tidak terpusat
Dalam pengembangan aplikasi, banyak konfigurasi yang nilai-nya akan didapat lewat jalan lain, seperti *environment variables* atau *command arguments*.
Akan lebih mudah jika hanya ada satu sumber konfigurasi saja untuk dijadikan acuan.
-#### • Statis (tidak dinamis)
+#### ◉ Statis (tidak dinamis)
Konfigurasi umumnya dibaca hanya jika diperlukan. Penulisan konfigurasi dalam file membuat proses pembacaan file harus dilakukan di awal, haru kemudian kita bisa ambil nilai konfigurasi dari data yang sudah ada di memori.
Hal tersebut memiliki beberapa konsekuensi, untuk aplikasi yang di-manage secara automated, sangat mungkin adanya perubahan nilai konfigurasi. Dari sini berarti pembacaan konfigurasi file tidak boleh hanya dilakukan di awal saja. Tapi juga tidak boleh dilakukan di setiap waktu, karena membaca file itu ada *cost*-nya dari prespektif I/O.
-#### • Solusi
+#### ◉ Solusi
Kita akan membahas solusi dari beberapa masalah di atas pada chapter terpisah, yaitu [Best Practice Configuration Menggunakan Environment Variable
](/C-best-practice-configuration-env-var)
diff --git a/content/C-cors-preflight-request.md b/content/C-cors-preflight-request.md
index 4fefa8da0..b1680b359 100644
--- a/content/C-cors-preflight-request.md
+++ b/content/C-cors-preflight-request.md
@@ -91,7 +91,7 @@ Khusus untuk beberapa header seperti `Accept`, `Origin`, `Referer`, dan `User-Ag
## C.14.3. Testing CORS
-#### • Persiapan
+#### ◉ Persiapan
Ada beberapa hal yang perlu dipersiapkan. Pertama, pastikan punya google chrome. Lalu install extension [jQuery Injector](https://chrome.google.com/webstore/detail/jquery-injector/ekkjohcjbjcjjifokpingdbdlfekjcgi?hl=en). Buka https://www.google.com lalu inject jQuery. Dengan melakukan inject jQuery secara paksa maka dari situs google kita bisa menggunakan jQuery.
@@ -109,7 +109,7 @@ Silakan lihat gambar berikut untuk memperjelas.
Bisa dilihat, tidak ada error, karena memang request dari google diijinkan. Silakan coba-coba melakukan request AJAX lainnya dengan method POST, DELETE, atau lainnya; atau ditambah dengan menyisipkan header tertentu dalam ajax request.
-#### • Akses http://localhost:9000 dari Origin yang Tidak Didaftarkan di CORS
+#### ◉ Akses http://localhost:9000 dari Origin yang Tidak Didaftarkan di CORS
Selanjutnya coba buka tab baru, buka https://novalagung.com, lalu jalankan script yang sama.
@@ -119,7 +119,7 @@ Selanjutnya coba buka tab baru, buka https://novalagung.com, lalu jalankan scrip
Dari screenshot dan error log di atas, bisa dilihat bahwa request gagal. Hal ini dikarenakan origin https://novalagung.com tidak diijinkan untuk mengakses http://localhost:9000.
-#### • CORS Multiple Origin
+#### ◉ CORS Multiple Origin
Sekarang coba tambahkan situs https://novalagung.com ke CORS header.
@@ -141,7 +141,7 @@ Masih tetap error, tapi berbeda dengan error sebelumnya.
Sebenarnya sudah kita singgung juga di atas, bahwa di spesifikasi adalah diperbolehkan isi header `Access-Control-Allow-Origin` lebih dari satu website. Namun, kebanyakan browser tidak mendukung bagian ini. Oleh karena itu error di atas muncul. Konfigurasi ini termasuk tidak valid, hasilnya kedua website tersebut tidak punya ijin masuk.
-#### • Allow All
+#### ◉ Allow All
Gunakan tanda asteriks (`*`) sebagai nilai ketiga CORS header untuk memberi ijin ke semua.
@@ -158,7 +158,7 @@ w.Header().Set("Access-Control-Allow-Headers", "*")
## C.14.4. Preflight Request
-#### • Teori
+#### ◉ Teori
Dalam konteks CORS, request dikategorikan menjadi 2 yaitu, **Simple Request** dan **Preflighted Request**. Beberapa contoh request yang sudah kita pelajari di atas termasuk simple request.
@@ -206,7 +206,7 @@ http.HandleFunc("/index", func(w http.ResponseWriter, r *http.Request) {
})
```
-#### • Praktek
+#### ◉ Praktek
Langsung saja buka google.com lalu lakukan AJAX request yang memenuhi alah satu kriteria preflighted request, misalnya, gunakan header `Content-Type: application/json`.
diff --git a/content/C-dockerize-golang.md b/content/C-dockerize-golang.md
index 010077fdd..3a60d4937 100644
--- a/content/C-dockerize-golang.md
+++ b/content/C-dockerize-golang.md
@@ -14,23 +14,23 @@ Pastikan Docker Engine ter-*install* untuk pengguna Windows atau MacOS. Untuk pe
## C.35.2. Istilah Dalam Docker
-#### • Container
+#### ◉ Container
Container adalah sebuah environment ter-isolasi, merupakan bentuk virtualisasi yang lebih kecil dan ringan dibanding VM (Virtual Machine). Virtualisasi pada container disebut dengan *Containerization*.
-#### • Docker Container
+#### ◉ Docker Container
Docker container adalah sebuah container yang di-manage oleh Docker Engine.
-#### • Docker Engine
+#### ◉ Docker Engine
Docker engine merupakan *daemon* yang bertugas untuk manajemen container-container.
-#### • Docker Image
+#### ◉ Docker Image
Docker Image adalah sebuah file yang di-*generate* oleh docker, yang file tersebut nantinya digunakan untuk basis pembuatan dan eksekusi container.
-#### • Containerize dan Dockerize
+#### ◉ Containerize dan Dockerize
Containerize merupakan istilah terhadap aplikasi yang di-*build* ke bentuk Image. Sedangkan Dockerize merupakan istilah untuk containerize menggunakan Docker. Perlu diketahui bahwa penyedia container tidak hanya Docker saja, ada banyak engine container lainnya yang bisa dipergunakan.
@@ -189,7 +189,7 @@ Ok, file `Dockerfile` sudah siap, mari kita lanjut ke proses *build* dan *start
## C.35.6. *Build Image* dan *Create Container*
-#### • Build Image
+#### ◉ Build Image
Pertama masuk ke direktori folder project, lalu jalankan *command* `docker build` berikut.
@@ -204,7 +204,7 @@ Kurang lebih outputnya seperti gambar berikut. O iya gunakan *command* `docker i
![Build Image](images/C_dockerize_golang_2_build_image.png)
-#### • Create Container
+#### ◉ Create Container
Image sudah siap, sekarang mari kita buat container baru menggunakan basis image `my-image-hello-world`. *Command*-nya kurang lebih berikut:
@@ -227,7 +227,7 @@ Semoga cukup jelas penjabaran di atas. Setelah container berhasil dibuat, cek me
![Create Container](images/C_dockerize_golang_3_create_container.png)
-#### • Start Container
+#### ◉ Start Container
Ok, sekarang container juga sudah dibuat, lanjut untuk *start* container tersebut, caranya menggunakan command `docker container start`. Jika sudah, coba cek di browser aplikasi web hello world, harusnya sudah bisa diakses.
@@ -244,7 +244,7 @@ Jika mengalami error saat start container, bisa jadi karena port `8080` sudak di
O iya, pada image di atas juga bisa dilihat penggunaan *command* `docker container ls` untuk memunculkan list container yang sedand *running* atau aktif. Untuk menampilkan semua container (aktif maupun non-aktif), cukup dengan menambahkan flag `-a` atau `--all`.
-#### • Stop Container
+#### ◉ Stop Container
Untuk stop container bisa dengan *command* `docker container stop `.
@@ -253,7 +253,7 @@ docker container stop my-container-hello-world
docker container ls
```
-#### • Hapus Container
+#### ◉ Hapus Container
Untuk hapus container bisa dengan *command* `docker container rm `.
@@ -262,7 +262,7 @@ docker container rm my-container-hello-world
docker container ls
```
-#### • Hapus Image
+#### ◉ Hapus Image
Untuk hapus image bisa dengan *command* `docker image rm `. O iya, untuk penghapusan image, harus dipastikan terlebih dahulu tidak ada container yang running menggunakan basis image yang ingin dihapus.
@@ -279,14 +279,14 @@ Atau bisa juga menggunakan *command* `docker run`. *Command* ini akan membuat co
Mungkin perbandingannya seperti ini:
-#### • Jalankan container lewat `create` lalu `start`
+#### ◉ Jalankan container lewat `create` lalu `start`
```bash
docker container create --name my-container-hello-world -e PORT=8080 -e INSTANCE_ID="my first instance" -p 8080:8080 my-image-hello-world
docker container start my-container-hello-world
```
-#### • Jalankan container lewat `run`
+#### ◉ Jalankan container lewat `run`
```bash
docker container run --name my-container-hello-world -e PORT=8080 -e INSTANCE_ID="my first instance" -p 8080:8080 my-image-hello-world
@@ -300,11 +300,11 @@ O iya, khusus untuk *command* `docker run` biasanya dijalankan dengan tambahan b
docker container run --name my-container-hello-world --rm -it -e PORT=8080 -e INSTANCE_ID="my first instance" -p 8080:8080 my-image-hello-world
```
-#### • Flag `--rm`
+#### ◉ Flag `--rm`
Flag ini digunakan untuk meng-automatisasi proses penghapusan container sewaktu container tersebut di stop. Jadi kita tidak perlu delete manual pakai `docker container rm`. Hal ini sangat membantu karena *command* `docker run` akan membuat container baru setiap dijalankan. Tapi sebenarnya pada contoh sebelumnya kita tidak perlu khawatir akan dibuat container baru karena sudah ada flag `--name`. Flag tersebut digunakan untuk menentukan nama container, yang di mana nama container harus unik. Jadi kalau ada duplikasi pasti langsung error. Nah dari sini berarti kalau temen-temen tidak pakai `--name` sangat dianjurkan paka `--rm` dalam penerapan `docker run`.
-#### • Flag `-it`
+#### ◉ Flag `-it`
Flag ini merupakan flag gabungan antara `-i` yang digunakan untuk meng-enable *interactive mode* dan `-t` untuk *enable* `TTY`. Dengan ini kita bisa masuk ke mode interaktif yang di mana jika kita terminate atau kill command menggunakan `CTRL + C` atau `CMD + C` (untuk mac), maka otomatis container akan di stop.
diff --git a/content/C-echo-routing.md b/content/C-echo-routing.md
index b9a11d5d3..9ea7e7a1d 100644
--- a/content/C-echo-routing.md
+++ b/content/C-echo-routing.md
@@ -79,7 +79,7 @@ Method `ctx.String()` dari objek context milik handler digunakan untuk mempermud
Selain `ctx.String()` ada banyak method sejenis lainnya, berikut selengkapnya.
-#### • Method `.String()`
+#### ◉ Method `.String()`
Digunakan untuk render plain text sebagai output (isi response header `Content-Type` adalah `text/plain`). Method ini tugasnya sama dengan method `.Write()` milik objek `http.ResponseWriter`.
@@ -90,7 +90,7 @@ r.GET("/index", func(ctx echo.Context) error {
})
```
-#### • Method `.HTML()`
+#### ◉ Method `.HTML()`
Digunakan untuk render html sebagai output. Isi response header `Content-Type` adalah `text/html`.
@@ -101,7 +101,7 @@ r.GET("/html", func(ctx echo.Context) error {
})
```
-#### • Method `.Redirect()`
+#### ◉ Method `.Redirect()`
Digunakan untuk redirect, pengganti `http.Redirect()`.
@@ -111,7 +111,7 @@ r.GET("/index", func(ctx echo.Context) error {
})
```
-#### • Method `.JSON()`
+#### ◉ Method `.JSON()`
Digunakan untuk render data JSON sebagai output. Isi response header `Content-Type` adalah `application/json`.
@@ -126,7 +126,7 @@ r.GET("/json", func(ctx echo.Context) error {
Echo juga menyediakan beberapa method untuk keperluan parsing request, di antaranya:
-#### • Parsing Query String
+#### ◉ Parsing Query String
Method `.QueryParam()` digunakan untuk mengambil data pada query string request, sesuai dengan key yang diinginkan.
@@ -145,7 +145,7 @@ Test menggunakan curl:
curl -X GET http://localhost:9000/page1?name=grayson
```
-#### • Parsing URL Path Param
+#### ◉ Parsing URL Path Param
Method `.Param()` digunakan untuk mengambil data path parameter sesuai skema rute.
@@ -166,7 +166,7 @@ Test menggunakan curl:
curl -X GET http://localhost:9000/page2/grayson
```
-#### • Parsing URL Path Param dan Setelahnya
+#### ◉ Parsing URL Path Param dan Setelahnya
Selain mengambil parameter sesuai spesifik path, kita juga bisa mengambil data **parameter path dan setelahnya**.
@@ -189,7 +189,7 @@ Test menggunakan curl:
curl -X GET http://localhost:9000/page3/tim/need/some/sleep
```
-#### • Parsing Form Data
+#### ◉ Parsing Form Data
Data yang dikirim sebagai request body dengan jenis adalah Form Data bisa di-ambil dengan mudah menggunakan `ctx.FormValue()`.
diff --git a/content/C-golang-aws-s3.md b/content/C-golang-aws-s3.md
index dc2e02934..b731b568c 100644
--- a/content/C-golang-aws-s3.md
+++ b/content/C-golang-aws-s3.md
@@ -14,11 +14,11 @@ Pada dasarnya Simple Storage Service (S3) adalah layanan penyimpanan file/object
Beberapa istilah yang biasa kita temukan saat kita bekerja dengan Amazon S3 antara lain:
-#### • Bucket
+#### ◉ Bucket
Bucket adalah wadah untuk object bisa disimpan ke dalam Amazon S3. Kita bisa menganalogikan bucket seperti directory yang ada di harddisk kita, dimana kita bisa membuat folder/path dan menyimpan file di dalamnya. Seperti contoh, misal kita membuat bucket `adamstudio-bucket` di region `ap-southeast-1` dan mengupload file `adamstudio.jpg`, maka kita bisa mengakses file tersebut dengan URL `https://adamstudio-bucket.s3.ap-southeast-1.amazonaws.com/adamstudio.jpg` (dengan authorisasi tertentu pastinya).
-#### • Object
+#### ◉ Object
Object secara singkat bisa kita artikan sebagai file, meskipun pada dasarnya berbeda, karena object juga menyimpan metadata file dan data-data lainnya.
diff --git a/content/C-golang-grpc-protobuf.md b/content/C-golang-grpc-protobuf.md
index 744fbff20..f202307db 100644
--- a/content/C-golang-grpc-protobuf.md
+++ b/content/C-golang-grpc-protobuf.md
@@ -59,18 +59,18 @@ Salah satu pembeda yang paling terlihat dibanding chapter sebelumnya adalah di s
Lanjut. Di bawah ini merupakan penjelasan per bagian dari struktur project di atas.
-#### • Folder `common`
+#### ◉ Folder `common`
Folder `common`, berisikan 2 buah sub folder, `config` dan `model`.
- Folder `config` berisikan informasi shared atau global, yang digunakan aplikasi client maupun server.
- Folder `model` berisikan file `.proto`. Silakan salin file `garage.proto` dan `user.proto` pada chapter sebelumnya ke folder tersebut.
-#### • Folder `client`
+#### ◉ Folder `client`
Isinya adalah satu buah file main, yang nantinya di jalankan sebagai aplikasi client. Aplikasi client ini akan berkomunikasi dengan 2 buah aplikasi server.
-#### • Folder `services`
+#### ◉ Folder `services`
Satu buah file proto untuk satu aplikasi rpc server (service). Karena ada dua file proto, berarti jelasnya ada dua aplikasi rpc server, `service-user` dan `service-garage`. Folder `services` ini menampung kedua aplikasi service tersebut.
@@ -97,7 +97,7 @@ Keyword `service` digunakan untuk membuat service. Service ini nantinya juga iku
OK, sekarang tambahkan kode berikut ke file proto.
-#### • Service `Users`
+#### ◉ Service `Users`
Buka file `user.proto`, tambahkan kode berikut di akhir baris.
@@ -147,7 +147,7 @@ Setelah di-compile, dua buah interface terbuat dengan skema nama ``.
diff --git a/content/C-golang-ldap-authentication.md b/content/C-golang-ldap-authentication.md
index 5c058e9af..17fdb17a3 100644
--- a/content/C-golang-ldap-authentication.md
+++ b/content/C-golang-ldap-authentication.md
@@ -4,11 +4,11 @@ Pada chapter ini kita belajar mengenai otentikasi user ke *Directory Service* le
## C.33.1. Definisi
-#### • LDAP
+#### ◉ LDAP
LDAP (Lightweight Directory Access Protocol) adalah protokol yang digunakan untuk mengakses **Directory Services** dalam sebuah komunikasi client-server.
-#### • Directory Services
+#### ◉ Directory Services
Directory Services adalah sebuah sistem yang menyimpan, mengelola, dan menyediakan akses informasi untuk menghubungkan sumber daya network (atau network resources). Network resources yang dimaksud contohnya:
@@ -26,11 +26,11 @@ Dengan terhubungnya resources tersebut, akan mudah bagi kita untuk mengelola ban
Selain itu, juga LDAP sering dimanfaatkan dalam implementasi SSO (Single sign-on).
-#### • Bind Operation
+#### ◉ Bind Operation
Operasi bind digunakan untuk otentikasi client ke directory server, dan juga untuk mengubah state otorisasi client tersebut. Operasi bind dilakukan dengan mengirim informasi bind dn dan password.
-#### • Directory Server untuk Testing
+#### ◉ Directory Server untuk Testing
Karena komunikasi adalah client-server maka kita perlu menggunakan salah satu directory server untuk keperluan testing. Beruntung-nya [Forum Systems](http://www.forumsys.com) berbaik hati menyediakan directory server yg bisa diakses secara gratis oleh public, dan pada chapter ini akan kita menggunakannya.
diff --git a/content/C-golang-protobuf-implementation.md b/content/C-golang-protobuf-implementation.md
index 72621f4cc..9f363cd4f 100644
--- a/content/C-golang-protobuf-implementation.md
+++ b/content/C-golang-protobuf-implementation.md
@@ -62,7 +62,7 @@ tree .
Folder `yourproject/model` berisikan file-file `.proto` (dua buah file proto didefinisikan). Dari kedua file di atas akan di-generate file model `.go` menggunakan command `protoc`. Nantinya generated file tersebut dipakai dalam `main.go`.
-#### • File `user.proto`
+#### ◉ File `user.proto`
OK, mari kita masuk ke bagian tulis-menulis kode. Buka file `user.proto`, tulis kode berikut.
@@ -152,7 +152,7 @@ type UserList struct {
}
```
-#### • File `garage.proto`
+#### ◉ File `garage.proto`
Sekarang beralih ke file ke-dua, `garage.proto`. Silakan tulis kode berikut.
@@ -313,7 +313,7 @@ Package model yang isinya generated proto file, di-import. Dari package tersebut
}
```
-#### • Print proto object
+#### ◉ Print proto object
Print salah satu objek yang sudah dibuat di atas.
@@ -333,7 +333,7 @@ Pada statement print pertama, objek ditampilkan apa adanya. Generated struct mem
Di statement print kedua, method `.String()` diakses, menampilkan semua property yang didefinisikan dalam proto message (property `XXX_` tidak dimunculkan).
-#### • Konversi objek proto ke json string
+#### ◉ Konversi objek proto ke json string
Tambahkan kode berikut:
@@ -361,7 +361,7 @@ Jalankan aplikasi, cek hasilnya.
Selain method `.Marshal()`, konversi ke json string bisa dilakukan lewat method `.MarshalToString()`.
-#### • Konversi json string ke objek proto
+#### ◉ Konversi json string ke objek proto
Proses unmarshal dari json string ke objek proto, dapat menggunakan `protojson.Unmarshal`, dengan parameter pertama disisipi data json byte dan parameter kedua disisipi objek proto pointer.
diff --git a/content/C-golang-sso-saml-sp.md b/content/C-golang-sso-saml-sp.md
index 228b764b3..45db97f02 100644
--- a/content/C-golang-sso-saml-sp.md
+++ b/content/C-golang-sso-saml-sp.md
@@ -8,7 +8,7 @@ Kali ini topik yang dipilih adalah SAML SSO versi 2.0. Kita akan pelajari cara p
Sebelum kita masuk ke bagian tulis menulis kode, alangkah baiknya sedikit membahas tentang definisi dari SSO dan SAML itu sendiri.
-#### • SSO
+#### ◉ SSO
SSO atau Single Sign-On merupakan servis untuk otentikasi dan manajemen session. Dengan SSO, maka akses ke banyak aplikasi cukup bisa sekali otentikasi saja. Contoh SSO:
@@ -20,7 +20,7 @@ SSO atau Single Sign-On merupakan servis untuk otentikasi dan manajemen session.
Ada beberapa jenis penerapan SSO yang bisa dipilih, salah satunya adalah **Security Assertion Markup Language** atau **SAML** yang akan kita bahas pada chapter ini.
-#### • SAML
+#### ◉ SAML
SAML merupakan protokol open standard untuk otentikasi dan otorisasi antara penyedia layanan (**Service Provider**) dan penyedia identitas (**Identity Provider**). SAML berbasis *assertion* berupa XML.
diff --git a/content/C-https-tls.md b/content/C-https-tls.md
index 686874b94..70e63931f 100644
--- a/content/C-https-tls.md
+++ b/content/C-https-tls.md
@@ -4,11 +4,11 @@ Pada bagian ini kita akan belajar cara meng-enable fasilitas SSL/TLS pada web se
## C.24.1. Definisi
-#### • SSL
+#### ◉ SSL
**SSL, Secure Sockets Layer**, adalah standar untuk pengamanan komunikasi lewat internet. Data atau informasi yang sedang dikomunikasikan dari sebuah system ke system lain akan di-proteksi, dengan cara adalah mengacak informasi tersebut menggunakan algoritma enkripsi.
-#### • SSL Certificates
+#### ◉ SSL Certificates
**SSL Certificate**, adalah sebuah file berisikan informasi mengenai website, yang nantinya dibutuhkan untuk enkripsi data. SSL Certificate berisi **Public Key**. Public key digunakan untuk meng-enkripsi data yang akan di transfer.
@@ -26,11 +26,11 @@ Berikut merupakan penjelasan dalam bentuk gambar yang diambil dari [coinjolt.com
Kedua file certificate dan file private key harus disimpan dengan sangat super aman di server.
-#### • TLS
+#### ◉ TLS
**TLS, Transport Layer Security**, adalah versi yang lebih update dari SSL.
-#### • HTTPS
+#### ◉ HTTPS
**HTTPS, Hyper Text Transfer Protocol Secure**, adalah ekstensi dari HTTP yang berguna untuk pengamanan komunikasi lewat internet. Data atau informasi yang dikomunikasikan di-enkripsi menggunakan **TLS**.
diff --git a/content/C-parsing-http-request-payload-echo.md b/content/C-parsing-http-request-payload-echo.md
index 3a4288166..c55917c5f 100644
--- a/content/C-parsing-http-request-payload-echo.md
+++ b/content/C-parsing-http-request-payload-echo.md
@@ -66,7 +66,7 @@ Jalankan aplikasi, lakukan testing. Bisa gunakan `curl` ataupun API testing tool
Di bawah ini shortcut untuk melakukan request menggunakan `curl` pada 4 jenis payload yang kita telah bahas. Response dari seluruh request adalah sama, menandakan bahwa data yang dikirim berhasil ditampung.
-#### • Form Data
+#### ◉ Form Data
```bash
curl -X POST http://localhost:9000/user \
@@ -76,7 +76,7 @@ curl -X POST http://localhost:9000/user \
# output => {"name":"Nope","email":"nope@novalagung.com"}
```
-#### • JSON Payload
+#### ◉ JSON Payload
```bash
curl -X POST http://localhost:9000/user \
@@ -86,7 +86,7 @@ curl -X POST http://localhost:9000/user \
# output => {"name":"Nope","email":"nope@novalagung.com"}
```
-#### • XML Payload
+#### ◉ XML Payload
```bash
curl -X POST http://localhost:9000/user \
@@ -100,7 +100,7 @@ curl -X POST http://localhost:9000/user \
# output => {"name":"Nope","email":"nope@novalagung.com"}
```
-#### • Query String
+#### ◉ Query String
```bash
curl -X GET http://localhost:9000/user?name=Joe&email=nope@novalagung.com
diff --git a/content/C-secure-middleware.md b/content/C-secure-middleware.md
index 895e66c01..df4469d40 100644
--- a/content/C-secure-middleware.md
+++ b/content/C-secure-middleware.md
@@ -65,7 +65,7 @@ func main() {
Pembuatan objek secure middleware dilakukan menggunakan `secure.New()` dengan isi parameter adalah konfigurasi. Bisa dilihat ada 5 buah property konfigurasi di-set. Berikut merupakan penjelasan tiap-tiap property tersebut.
-#### • Konfigurasi `AllowedHosts`
+#### ◉ Konfigurasi `AllowedHosts`
```go
AllowedHosts: []string{"localhost:9000", "www.google.com"}
@@ -73,7 +73,7 @@ AllowedHosts: []string{"localhost:9000", "www.google.com"}
Host yang diperbolehkan mengakses web server ditentukan hanya 2, yaitu localhost:9000 yang merupakan web server itu sendiri, dan google.com. Silakan coba mengakses aplikasi kita ini menggunakan AJAX lewat google.com dan domainnya lainnya untuk mengetes apakah fungsionalitas nya berjalan.
-#### • Konfigurasi `FrameDeny`
+#### ◉ Konfigurasi `FrameDeny`
```go
FrameDeny: true
@@ -87,7 +87,7 @@ Di library secure, untuk men-disable ijin akses aplikasi dari dalam iframe, bisa
Untuk mengetes, silakan buat aplikasi web terpisah yang mer-render sebuah view. Dalam view tersebut siapkan satu buah iframe yang mengarah ke `https://localhost:9000/index`.
-#### • Konfigurasi `CustomFrameOptionsValue`
+#### ◉ Konfigurasi `CustomFrameOptionsValue`
```go
CustomFrameOptionsValue: "SAMEORIGIN"
@@ -99,7 +99,7 @@ Dengan menambahkan satu buah property lagi yaitu `CustomFrameOptionsValue: "SAME
Untuk mengetes, buat rute baru yang me-render sebuah view. Dalam view tersebut siapkan satu buah iframe yang mengarah ke `/index`.
-#### • Konfigurasi `ContentTypeNosniff`
+#### ◉ Konfigurasi `ContentTypeNosniff`
```go
ContentTypeNosniff: true
@@ -107,7 +107,7 @@ ContentTypeNosniff: true
Property `ContentTypeNosniff: true` digunakan untuk disable MIME-sniffing yang dilakukan oleh browser IE. Lebih jelasnya silakan baca [X-Content-Type-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options).
-#### • Konfigurasi `BrowserXssFilter`
+#### ◉ Konfigurasi `BrowserXssFilter`
```go
BrowserXssFilter: true
diff --git a/content/C-write-pdf-file.md b/content/C-write-pdf-file.md
index 834331be1..ce7d0529f 100644
--- a/content/C-write-pdf-file.md
+++ b/content/C-write-pdf-file.md
@@ -45,23 +45,23 @@ Statement `gofpdf.New()` digunakan untuk membuat objek dokumen baru. Fungsi `.Ne
Fungsi `.New()` mengembalikan objek PDF. Dari situ kita bisa mengakses banyak method sesuai kebutuhan, beberapa di antaranya adalah 4 buah method yang dicontohkan di atas.
-#### • Method `.AddPage()`
+#### ◉ Method `.AddPage()`
Method ini digunakan untuk menambah halaman baru. Defaultnya, objek dokumen yang baru dibuat tidak memiliki halaman. Dengan memanggil `.AddPage()` maka halaman baru dibuat.
Setelah *at least* satu halaman tersedia, kita bisa lanjut ke proses tulis menulis.
-#### • Method `.SetFont()`
+#### ◉ Method `.SetFont()`
Method ini digunakan untuk menge-set konfigurasi font dokumen. Font Family, Font Style, dan Font Size disisipkan dalam parameter secara berurutan.
-#### • Method `.Text()`
+#### ◉ Method `.Text()`
Digunakan untuk menulis text pada koordinat tertentu. Pada kode di atas, `40` artinya `40mm` dari kiri, sedangkan `10` artinya `10mm` dari atas. Satuan milimeter digunakan karena pada saat penciptaan objek dipilih `mm` sebagai satuan.
Method ini melakukan penulisan text pada current page.
-#### • Method `.Image()`
+#### ◉ Method `.Image()`
Digunakan untuk menambahkan image. Method ini memerlukan beberapa parameter.
diff --git a/content/D-insert-1mil-csv-record-into-db-in-a-minute.md b/content/D-insert-1mil-csv-record-into-db-in-a-minute.md
index 02c65b138..b2c46a01c 100644
--- a/content/D-insert-1mil-csv-record-into-db-in-a-minute.md
+++ b/content/D-insert-1mil-csv-record-into-db-in-a-minute.md
@@ -6,13 +6,13 @@ Pada bagian insert data kita terapkan mekanisme failover, jadi ketika ada operas
## D.1.1. Penjelasan
-#### • Worker Pool
+#### ◉ Worker Pool
Worker pool adalah teknik manajemen goroutine dalam *concurrent programming* pada Go. Sejumlah worker dijalankan dan masing-masing memiliki tugas yang sama yaitu menyelesaikan sejumlah jobs.
Dengan metode worker pool ini, maka penggunaan memory dan performansi program akan bisa optimal.
-#### • Database Connection Pool
+#### ◉ Database Connection Pool
*Connection pool* adalah metode untuk manajemen sejumlah koneksi database, agar bisa digunakan secara optimal.
@@ -20,7 +20,7 @@ Connection pool sangat penting dalam kasus operasi data yang berhubungan dengan
Karena pada concurrent programming, beberapa proses akan berjalan bersamaan, maka penggunaan 1 koneksi db akan menghambat proses tersebut. Perlu ada beberapa koneksi database, agar goroutine tidak rebutan objek koneksi database.
-#### • Failover
+#### ◉ Failover
Failover merupakan mekanisme backup ketika sebuah proses gagal. Pada konteks ini, failover mengarah ke proses untuk me-retry operasi insert ketika gagal.
@@ -63,7 +63,7 @@ Jika pembaca ingin menggunakan driver lain, juga silakan.
## D.1.3. Praktek
-#### • Definisi Konstanta
+#### ◉ Definisi Konstanta
Ada beberapa konstanta yang perlu dipersiapkan. Pertama connection string untuk komunikasi ke database server. Sesuaikan value nya dengan yang dipergunakan.
@@ -101,7 +101,7 @@ Terakhir, siapkan variabel untuk menampung data header dari pembacaan CSV nanti.
var dataHeaders = make([]string, 0)
```
-#### • Fungsi Buka Koneksi Database
+#### ◉ Fungsi Buka Koneksi Database
Buat fungsi untuk buka koneksi database, yg dikembalikan objek database kembalian fungsi `sql.Open()`.
@@ -131,7 +131,7 @@ O iya jangan lupa untuk import driver nya.
import _ "github.com/go-sql-driver/mysql"
```
-#### • Fungsi Baca CSV
+#### ◉ Fungsi Baca CSV
Buka file CSV, lalu gunakan objek file untuk membuat objek CSV reader baru.
@@ -149,7 +149,7 @@ func openCsvFile() (*csv.Reader, *os.File, error) {
}
```
-#### • Fungsi Menjalankan Workers
+#### ◉ Fungsi Menjalankan Workers
Ok, sekarang kita mulai masuk ke aspek konkurensi dari pembahasan ini. Siapkan fungsi yang isinya men-dispatch beberapa goroutine sejumlah `totalWorker`.
@@ -181,7 +181,7 @@ Fungsi `doTheJob()` yang nantinya kita buat, isinya adalah operasi insert data k
Idealnya di akhir aplikasi akan terjadi pemanggilan `wg.Done()` sejumlah 1 juta karena ada 1 juta jobs.
-#### • Fungsi Baca CSV dan Pengiriman Jobs ke Worker
+#### ◉ Fungsi Baca CSV dan Pengiriman Jobs ke Worker
Proses pembacaan CSV, apapun metodenya pasti yang dijalankan adalah membaca data dari line ke line dari baris paling bawah.
@@ -221,7 +221,7 @@ Setelah proses baca data selesai, channel di close. Karena pengiriman dan peneri
Jika blok kode perulangan dalam fungsi di atas selesai, maka sudah tidak ada lagi operasi kirim terima data, maka kita close channelnya.
-#### • Fungsi Insert Data ke Database
+#### ◉ Fungsi Insert Data ke Database
```go
func doTheJob(workerIndex, counter int, db *sql.DB, values []interface{}) {
@@ -281,7 +281,7 @@ O iya, mengenai kode untuk manajemen db connection poll mana ya? sepertinya tida
Btw, di atas juga ada satu fungsi lagi, `generateQuestionsMark()`, gunanya untuk membantu pembentukan query insert data secara dinamis.
-#### • Fungsi Main
+#### ◉ Fungsi Main
Terakhir, panggil semua fungsi yang sudah dibuat pada main.
From 58267f95410bdf7757f01c9d61b99b362473ba73 Mon Sep 17 00:00:00 2001
From: novalagung
Date: Tue, 23 Apr 2024 18:53:41 +0700
Subject: [PATCH 4/9] feat: semantic markdown update
---
content/A-fungsi-sebagai-parameter.md | 4 ++--
content/A-fungsi-variadic.md | 8 ++++----
content/A-goroutine.md | 8 ++++----
content/A-konstanta.md | 2 +-
content/A-method.md | 6 +++---
5 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/content/A-fungsi-sebagai-parameter.md b/content/A-fungsi-sebagai-parameter.md
index 8eacd0a12..0010838e7 100644
--- a/content/A-fungsi-sebagai-parameter.md
+++ b/content/A-fungsi-sebagai-parameter.md
@@ -82,11 +82,11 @@ func filter(data []string, callback FilterCallback) []string {
Skema `func(string) bool` diubah menjadi tipe dengan nama `FilterCallback`. Tipe tersebut kemudian digunakan sebagai tipe data parameter `callback`.
----
+## A.22.3. Penjelasan tambahan
Di bawah ini merupakan penjelasan tambahan mengenai fungsi `strings.Contains()`.
-## A.22.2.1. Penggunaan Fungsi `string.Contains()`
+#### ◉ Penggunaan Fungsi `string.Contains()`
Inti dari fungsi ini adalah untuk deteksi apakah sebuah substring adalah bagian dari string, jika iya maka akan bernilai `true`, dan sebaliknya. Contoh penggunaannya:
diff --git a/content/A-fungsi-variadic.md b/content/A-fungsi-variadic.md
index 75e54f00a..81ee86407 100644
--- a/content/A-fungsi-variadic.md
+++ b/content/A-fungsi-variadic.md
@@ -56,17 +56,17 @@ Nilai tiap parameter bisa diakses seperti cara pengaksesan tiap elemen slice. Pa
for _, number := range numbers {
```
----
+## A.20.2. Penjelasan tambahan
-Berikut merupakan penjelasan tambahan dari kode yang telah kita tulis.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-#### • Penggunaan Fungsi `fmt.Sprintf()`
+#### ◉ Penggunaan Fungsi `fmt.Sprintf()`
Fungsi `fmt.Sprintf()` pada dasarnya sama dengan `fmt.Printf()`, hanya saja fungsi ini tidak menampilkan nilai, melainkan mengembalikan nilainya dalam bentuk string. Pada kasus di atas, nilai kembalian `fmt.Sprintf()` ditampung oleh variabel `msg`.
Selain `fmt.Sprintf()`, ada juga `fmt.Sprint()` dan `fmt.Sprintln()`.
-#### • Penggunaan Fungsi `float64()`
+#### ◉ Penggunaan Fungsi `float64()`
Sebelumnya sudah dibahas bahwa `float64` merupakan tipe data. Tipe data jika ditulis sebagai fungsi (penandanya ada tanda kurungnya) berguna untuk **casting**. Casting sendiri adalah teknik untuk konversi tipe sebuah data ke tipe lain. Sebagian besar tipe data dasar yang telah dipelajari pada chapter [A.9. Variabel](/A-variabel.html) bisa di-cast. Dan cara penerapannya juga sama, cukup panggil sebagai fungsi, lalu masukan data yang ingin dikonversi sebagai parameter.
diff --git a/content/A-goroutine.md b/content/A-goroutine.md
index 653650da0..78e919798 100644
--- a/content/A-goroutine.md
+++ b/content/A-goroutine.md
@@ -51,17 +51,17 @@ Bisa dilihat di output, tulisan `"halo"` dan `"apa kabar"` bermunculan selang-se
Pada gambar di atas, program dieksekusi 2 kali. Hasil eksekusi pertama berbeda dengan kedua, penyebabnya adalah karena kita menggunakan 2 prosesor. Goroutine mana yang dieksekusi terlebih dahulu tergantung kedua prosesor tersebut.
----
+## A.30.2. Penjelasan tambahan
-Berikut adalah penjelasan tambahan tentang beberapa fungsi yang baru kita pelajari di atas.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-## A.30.1.1. Penggunaan Fungsi `runtime.GOMAXPROCS()`
+#### ◉ Penggunaan Fungsi `runtime.GOMAXPROCS()`
Fungsi ini digunakan untuk menentukan jumlah core atau processor yang digunakan dalam eksekusi program.
Jumlah yang diinputkan secara otomatis akan disesuaikan dengan jumlah asli *logical processor* yang ada. Jika jumlahnya lebih, maka dianggap menggunakan sejumlah prosesor yang ada.
-## A.30.1.2. Penggunaan Fungsi `fmt.Scanln()`
+#### ◉ Penggunaan Fungsi `fmt.Scanln()`
Fungsi ini akan meng-capture semua karakter sebelum user menekan tombol enter, lalu menyimpannya pada variabel.
diff --git a/content/A-konstanta.md b/content/A-konstanta.md
index 52c3a60e5..fdd7756fe 100644
--- a/content/A-konstanta.md
+++ b/content/A-konstanta.md
@@ -20,7 +20,7 @@ const lastName = "wick"
fmt.Print("nice to meet you ", lastName, "!\n")
```
-#### • Penggunaan Fungsi `fmt.Print()`
+#### ◉ Penggunaan Fungsi `fmt.Print()`
Fungsi ini memiliki peran yang sama seperti fungsi `fmt.Println()`, pembedanya fungsi `fmt.Print()` tidak menghasilkan baris baru di akhir outputnya.
diff --git a/content/A-method.md b/content/A-method.md
index 491676ade..edc5e2d95 100644
--- a/content/A-method.md
+++ b/content/A-method.md
@@ -124,11 +124,11 @@ var s2 = &student{"ethan hunt", 22}
s2.sayHello()
```
----
+## A.30.2. Penjelasan tambahan
-Berikut adalah penjelasan tambahan mengenai beberapa hal pada chapter ini.
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-#### • Penggunaan Fungsi `strings.Split()`
+#### ◉ Penggunaan Fungsi `strings.Split()`
Pada chapter ini ada fungsi baru yang kita gunakan: `strings.Split()`. Fungsi ini berguna untuk memisah string menggunakan pemisah yang ditentukan sendiri. Hasilnya adalah array berisikan kumpulan substring.
From d89bc3a9aa9b29b67ae7abd4391524a93562c8a2 Mon Sep 17 00:00:00 2001
From: novalagung
Date: Tue, 23 Apr 2024 18:54:05 +0700
Subject: [PATCH 5/9] feat: update A.21. Fungsi Closure
---
content/A-fungsi-closure.md | 42 ++++++++++++++++++++-----------------
1 file changed, 23 insertions(+), 19 deletions(-)
diff --git a/content/A-fungsi-closure.md b/content/A-fungsi-closure.md
index 4c60405ec..b8e6ab54b 100644
--- a/content/A-fungsi-closure.md
+++ b/content/A-fungsi-closure.md
@@ -1,12 +1,12 @@
# A.21. Fungsi Closure
-Definisi **Closure** adalah sebuah fungsi yang bisa disimpan dalam variabel. Dengan menerapkan konsep tersebut, kita bisa membuat fungsi di dalam fungsi, atau bahkan membuat fungsi yang mengembalikan fungsi.
-
-Closure merupakan *anonymous function* atau fungsi tanpa nama. Biasa dimanfaatkan untuk membungkus suatu proses yang hanya dipakai sekali atau dipakai pada blok tertentu saja.
+Definisi **Closure** adalah suatu *anonymous function* (atau fungsi tanpa nama) yang disimpan dalam variabel. Dengan adanya closure, kita bisa mendesain beberapa hal diantaranya seperti: membuat fungsi di dalam fungsi, atau bahkan membuat fungsi yang mengembalikan fungsi. Closure biasa dimanfaatkan untuk membungkus suatu proses yang hanya dijalankan sekali saja atau hanya dipakai pada blok tertentu saja.
## A.21.1. Closure Disimpan Sebagai Variabel
-Sebuah fungsi tanpa nama bisa disimpan dalam variabel. Variabel yang menyimpan closure memiliki sifat seperti fungsi yang disimpannya. Di bawah ini adalah contoh program sederhana untuk mencari nilai terendah dan tertinggi dari suatu array. Logika pencarian dibungkus dalam closure yang ditampung oleh variabel `getMinMax`.
+Sebuah fungsi tanpa nama bisa disimpan dalam variabel. Variabel closure memiliki sifat seperti fungsi yang disimpannya.
+
+Di bawah ini adalah contoh program sederhana yang menerapkan closure untuk pencarian nilai terendah dan tertinggi dari data array. Logika pencarian dibungkus dalam closure yang ditampung oleh variabel `getMinMax`.
```go
package main
@@ -35,8 +35,7 @@ func main() {
}
```
-
-Bisa dilihat pada kode di atas bagaimana sebuah closure dibuat dan dipanggil. Sedikit berbeda memang dibanding pembuatan fungsi biasa. Fungsi ditulis tanpa nama, lalu ditampung dalam variabel.
+Bisa dilihat pada kode di atas bagaimana cara deklarasi closure dan cara pemanggilannya. Sedikit berbeda memang dibanding pembuatan fungsi biasa, pada closure fungsi ditulis tanpa memiliki nama lalu ditampung ke variabel.
```go
var getMinMax = func(n []int) (int, int) {
@@ -44,7 +43,7 @@ var getMinMax = func(n []int) (int, int) {
}
```
-Cara pemanggilannya, dengan menuliskan nama variabel tersebut sebagai fungsi, seperti pemanggilan fungsi biasa.
+Cara pemanggilan closure adalah dengan memperlakukan variabel closure seperti fungsi, dituliskan seperti pemanggilan fungsi.
```go
var min, max = getMinMax(numbers)
@@ -54,25 +53,25 @@ Output program:
![Penerapan closure](images/A_fungsi_closure_1_closure.png)
----
+## A.21.2. Penjelasan tambahan
-Berikut adalah penjelasan tambahan mengenai kode di atas
+Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
-## A.21.1.1. Penggunaan Template String `%v`
+#### ◉ Penggunaan Template String `%v`
-Template `%v` digunakan untuk menampilkan segala jenis data. Bisa array, int, float, bool, dan lainnya.
+Template `%v` digunakan untuk menampilkan data tanpa melihat tipe datanya. Jadi bisa digunakan untuk menampilkan data array, int, float, bool, dan lainnya. Bisa dilihat di contoh statement, data bertipe array dan numerik ditampilkan menggunakan `%v`.
```go
fmt.Printf("data : %v\nmin : %v\nmax : %v\n", numbers, min, max)
```
-Bisa dilihat pada statement di atas, data bertipe array dan numerik ditampilkan menggunakan `%v`. Template ini biasa dimanfaatkan untuk menampilkan sebuah data yang tipe nya bisa dinamis atau belum diketahui. Sangat tepat jika digunakan pada data bertipe `interface{}` yang nantinya akan di bahas pada chapter [A.27. Interface](/A-interface.html).
+Template `%v` ini biasa dimanfaatkan untuk menampilkan sebuah data yang tipe nya bisa dinamis atau belum diketahui. Biasa digunakan untuk keperluan debugging, misalnya untuk menampilkan data bertipe `any` atau `interface{}`.
----
+> Pembahasan mengenai tipe data `any` atau `interface{}` ada di chapter [A.27. Interface](/A-interface.html)
-## A.21.2. Immediately-Invoked Function Expression (IIFE)
+#### ◉ Immediately-Invoked Function Expression (IIFE)
-Closure jenis ini dieksekusi langsung pada saat deklarasinya. Biasa digunakan untuk membungkus proses yang hanya dilakukan sekali, bisa mengembalikan nilai, bisa juga tidak.
+Closure jenis IIFE ini eksekusinya adalah langsung saat deklarasi. Teknik ini biasa diterapkan untuk membungkus proses yang hanya dilakukan sekali. IIFE bisa memiliki nilai balik atau bisa juga tidak.
Di bawah ini merupakan contoh sederhana penerapan metode IIFE untuk filtering data array.
@@ -104,7 +103,7 @@ Output program:
![Penerapan IIFE](images/A_fungsi_closure_2_iife.png)
-Ciri khas IIFE adalah adanya kurung parameter tepat setelah deklarasi closure berakhir. Jika ada parameter, bisa juga dituliskan dalam kurung parameternya.
+Ciri khas dari penulisan IIFE adalah adanya tanda kurung parameter yang ditulis di akhir deklarasi closure. Jika IIFE memiliki parameter, maka argument-nya juga ditulis. Contoh:
```go
var newNumbers = func(min int) []int {
@@ -112,13 +111,13 @@ var newNumbers = func(min int) []int {
}(3)
```
-Pada contoh di atas IIFE menghasilkan nilai balik yang kemudian ditampung `newNumber`. Perlu diperhatikan bahwa yang ditampung adalah **nilai kembaliannya** bukan body fungsi atau **closure**.
+Di contoh sederhana di atas, IIFE menghasilkan nilai balik yang ditampung variabel `newNumber`. Perlu diperhatikan bahwa yang ditampung adalah **nilai kembaliannya** bukan body fungsi atau **closure**-nya.
> Closure bisa juga dengan gaya manifest typing, caranya dengan menuliskan skema closure-nya sebagai tipe data. Contoh:
var closure (func (string, int, []string) int)
closure = func (a string, b int, c []string) int {
// ..
}
## A.21.3. Closure Sebagai Nilai Kembalian
-Salah satu keunikan closure lainnya adalah bisa dijadikan sebagai nilai balik fungsi, cukup aneh memang, tapi pada suatu kondisi teknik ini sangat membantu. Di bawah ini disediakan sebuah fungsi bernama `findMax()`, fungsi ini salah satu nilai kembaliannya berupa closure.
+Salah satu keunikan lain dari closure adalah: closure bisa dijadikan sebagai nilai balik fungsi. Cukup aneh, tapi pada kondisi tertentu teknik ini sangat berguna. Sebagai contoh, di bawah ini dideklarasikan sebuah fungsi bernama `findMax()` yang salah satu nilai kembaliannya adalah berupa closure.
```go
package main
@@ -148,7 +147,12 @@ return len(res), func() []int {
> Fungsi tanpa nama yang akan dikembalikan boleh disimpan pada variabel terlebih dahulu. Contohnya:
var getNumbers = func() []int {
return res
}
return len(res), getNumbers
-Sedikit tentang fungsi `findMax()`, fungsi ini digunakan untuk mencari banyaknya angka-angka yang nilainya di bawah atau sama dengan angka tertentu. Nilai kembalian pertama adalah jumlah angkanya. Nilai kembalian kedua berupa closure yang mengembalikan angka-angka yang dicari. Berikut merupakan contoh implementasi fungsi tersebut.
+Tentang fungsi `findMax()` sendiri, fungsi ini dibuat untuk mempermudah pencarian angka-angka yang nilainya di bawah atau sama dengan angka tertentu. Fungsi ini mengembalikan dua buah nilai balik:
+
+- Nilai balik pertama adalah jumlah angkanya.
+- Nilai balik kedua berupa closure yang mengembalikan angka-angka yang dicari.
+
+Berikut merupakan contoh implementasi fungsi tersebut:
```go
func main() {
From cc8e83f6c7effdc00b93569f869d6d452cf7064c Mon Sep 17 00:00:00 2001
From: novalagung
Date: Tue, 23 Apr 2024 18:54:29 +0700
Subject: [PATCH 6/9] feat: update A.19. Fungsi Multiple Return
---
content/A-fungsi-multiple-return.md | 26 ++++++++++++--------------
1 file changed, 12 insertions(+), 14 deletions(-)
diff --git a/content/A-fungsi-multiple-return.md b/content/A-fungsi-multiple-return.md
index 796983568..2ac5893f0 100644
--- a/content/A-fungsi-multiple-return.md
+++ b/content/A-fungsi-multiple-return.md
@@ -1,12 +1,10 @@
# A.19. Fungsi Multiple Return
-Umumnya fungsi hanya memiliki satu buah nilai balik saja. Jika ada kebutuhan di mana data yang dikembalikan harus banyak, biasanya digunakanlah tipe seperti `map`, slice, atau `struct` sebagai nilai balik.
+Di Go, suatu fungsi bisa saja mengembalikan nilai belik lebih dari 1 buah. Teknik ini bisa menjadi alternatif selain menggunakan tipe data kolektif seperti `map`, slice, atau `struct` sebagai nilai balik. Pada chapter ini kita akan belajar penerapannya.
-Go menyediakan kapabilitas bagi programmer untuk membuat fungsi memiliki banyak nilai balik. Pada chapter ini akan dibahas bagaimana penerapannya.
+## A.19.1. Penerapan Fungsi Multiple Return
-## A.19.1 Penerapan Fungsi Multiple Return
-
-Cara membuat fungsi yang memiliki banyak nilai balik tidaklah sulit. Tinggal tulis saja pada saat deklarasi fungsi semua tipe data nilai yang dikembalikan, dan pada keyword `return` tulis semua data yang ingin dikembalikan. Contoh bisa dilihat pada berikut.
+Cara membuat fungsi agar memiliki banyak nilai balik tidaklah sulit, caranya pada saat deklarasi fungsi, tulis semua tipe data nilai balik yang ingin dikembalikan. Kemudian dalam body fungsi, pada penggunaan keyword `return`, tulis semua data yang ingin dikembalikan. Contoh:
```go
package main
@@ -25,7 +23,7 @@ func calculate(d float64) (float64, float64) {
}
```
-Fungsi `calculate()` di atas menerima satu buah parameter (`diameter`) yang digunakan dalam proses perhitungan. Di dalam fungsi tersebut ada 2 hal yang dihitung, yaitu nilai **luas** dan **keliling**. Kedua nilai tersebut kemudian dijadikan sebagai return value fungsi.
+Fungsi `calculate()` di atas memiliki satu buah parameter yaitu `d` (diameter). Di dalam fungsi terdapat operasi perhitungan nilai **luas** dan **keliling** dari nilai `d`. Kedua hasilnya kemudian dijadikan sebagai return value.
Cara pendefinisian banyak nilai balik bisa dilihat pada kode di atas, langsung tulis tipe data semua nilai balik dipisah tanda koma, lalu ditambahkan kurung di antaranya.
@@ -39,7 +37,7 @@ Tak lupa di bagian penulisan keyword `return` harus dituliskan juga semua data y
return area, circumference
```
-Implementasi dari fungsi `calculate()` di atas, bisa dilihat pada kode berikut.
+Sekarang, coba panggil fungsi `calculate()` yang sudah dibuat untuk mencari nilai luas dan keliling dari suatu diameter.
```go
func main() {
@@ -55,13 +53,13 @@ Output program:
![Penerapan teknik multiple return](images/A_fungsi_multiple_return_1_multiple_return.png)
-Karena fungsi tersebut memiliki banyak nilai balik, maka pada pemanggilannya harus disiapkan juga banyak variabel untuk menampung nilai kembalian yang ada (sesuai jumlah nilai balik fungsi).
+Fungsi `calculate()` memiliki banyak nilai balik, maka dalam pemanggilannya harus disiapkan juga sejumlah variabel untuk menampung nilai balik fungsi (sesuai dengan jumlah nilai balik yang dideklarasikan).
```go
var area, circumference = calculate(diameter)
```
-## A.19.2 Fungsi Dengan Predefined Return Value
+## A.19.2. Fungsi Dengan Predefined Return Value
Keunikan lainnya yang jarang ditemui di bahasa lain adalah, di Go variabel yang digunakan sebagai nilai balik bisa didefinisikan di awal.
@@ -84,15 +82,15 @@ Fungsi dideklarasikan memiliki 2 buah tipe data, dan variabel yang nantinya dija
Karena variabel nilai balik sudah ditentukan di awal, untuk mengembalikan nilai cukup dengan memanggil `return` tanpa perlu diikuti variabel apapun. Nilai terakhir `area` dan `circumference` sebelum pemanggilan keyword `return` adalah hasil dari fungsi di atas.
----
+## A.19.3. Penjelasan tambahan
-Ada beberapa hal baru dari kode di atas yang perlu dibahas, seperti `math.Pow()` dan `math.Pi`. Berikut adalah penjelasannya.
+Ada beberapa hal baru dari kode di atas yang perlu dibahas, diantaranya `math.Pow()` dan `math.Pi`.
-#### • Penggunaan Fungsi `math.Pow()`
+#### ◉ Penggunaan Fungsi `math.Pow()`
-Fungsi `math.Pow()` digunakan untuk memangkat nilai. `math.Pow(2, 3)` berarti 2 pangkat 3, hasilnya 8. Fungsi ini berada dalam package `math`.
+Fungsi `math.Pow()` digunakan untuk operasi pangkat nilai. `math.Pow(2, 3)` berarti 2 pangkat 3, hasilnya 8. Fungsi ini berada dalam package `math`.
-#### • Penggunaan Konstanta `math.Pi`
+#### ◉ Penggunaan Konstanta `math.Pi`
`math.Pi` adalah konstanta bawaan `package math` yang merepresentasikan **Pi** atau **22/7**.
From 84e8eb6f41250177cc1f71fdf55c5a8e4912ca1b Mon Sep 17 00:00:00 2001
From: novalagung
Date: Tue, 23 Apr 2024 18:59:26 +0700
Subject: [PATCH 7/9] feat: A.22. Fungsi Sebagai parameter
---
content/A-fungsi-sebagai-parameter.md | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/content/A-fungsi-sebagai-parameter.md b/content/A-fungsi-sebagai-parameter.md
index 0010838e7..0e27fde4d 100644
--- a/content/A-fungsi-sebagai-parameter.md
+++ b/content/A-fungsi-sebagai-parameter.md
@@ -1,8 +1,8 @@
# A.22. Fungsi Sebagai parameter
-Setelah pada chapter sebelumnya kita belajar mengenai fungsi yang mengembalikan nilai balik berupa fungsi, kali ini topiknya tidak kalah unik, yaitu fungsi yang digunakan sebagai parameter.
+Pada chapter sebelumnya kita telah belajar tentang fungsi yang mengembalikan nilai balik berupa fungsi. Kali ini topiknya tidak kalah unik, yaitu tentang fungsi yang memiliki parameter sebuah fungsi.
-Di Go, fungsi bisa dijadikan sebagai tipe data variabel. Dari situ sangat memungkinkan untuk menjadikannya sebagai parameter juga.
+Di Go, fungsi bisa dijadikan sebagai tipe data variabel, maka sangat memungkinkan untuk menjadikannya sebagai parameter.
## A.22.1. Penerapan Fungsi Sebagai Parameter
@@ -50,7 +50,7 @@ func main() {
}
```
-Ada cukup banyak hal yang terjadi di dalam tiap pemanggilan fungsi `filter()` di atas. Berikut merupakan penjelasannya.
+Ada cukup banyak hal yang terjadi di dalam tiap pemanggilan fungsi `filter()` di atas. Berikut adalah penjelasannya:
1. Data array (yang didapat dari parameter pertama) akan di-looping.
2. Di tiap perulangannya, closure `callback` dipanggil, dengan disisipkan data tiap elemen perulangan sebagai parameter.
@@ -62,15 +62,17 @@ Ada cukup banyak hal yang terjadi di dalam tiap pemanggilan fungsi `filter()` di
Pada `dataContainsO`, parameter kedua fungsi `filter()` berisikan statement untuk deteksi apakah terdapat substring `"o"` di dalam nilai variabel `each` (yang merupakan data tiap elemen), jika iya, maka kondisi filter bernilai `true`, dan sebaliknya.
-pada contoh ke-2 (`dataLength5`), closure `callback` berisikan statement untuk deteksi jumlah karakter tiap elemen. Jika ada elemen yang jumlah karakternya adalah 5, berarti elemen tersebut lolos filter.
+Pada contoh ke-2 (`dataLength5`), closure `callback` berisikan statement untuk deteksi jumlah karakter tiap elemen. Jika ada elemen yang jumlah karakternya adalah 5, berarti elemen tersebut lolos filter.
-Memang butuh usaha ekstra untuk memahami pemanfaatan closure sebagai parameter fungsi. Tapi setelah paham, penerapan teknik ini pada kondisi yang tepat akan sangat membantu proses pembuatan aplikasi.
+Memang butuh usaha ekstra untuk memahami pemanfaatan closure sebagai parameter fungsi. Tapi setelah paham, penerapan teknik ini pada kondisi yang tepat akan sangat berguna.
## A.22.2. Alias Skema Closure
-Kita sudah mempelajari bahwa closure bisa dimanfaatkan sebagai tipe parameter, contohnya seperti pada fungsi `filter()`. Pada fungsi tersebut kebetulan skema tipe parameter closure-nya tidak terlalu panjang, hanya ada satu buah parameter dan satu buah nilai balik.
+Kita sudah mempelajari bahwa closure bisa dimanfaatkan sebagai tipe parameter, contohnya seperti pada fungsi `filter()`. Di fungsi tersebut kebetulan skema tipe parameter closure-nya tidak terlalu panjang, hanya ada satu buah parameter dan satu buah nilai balik.
-Pada fungsi yang skema-nya cukup panjang, akan lebih baik jika menggunakan alias, apalagi ketika ada parameter fungsi lain yang juga menggunakan skema yang sama. Membuat alias fungsi berarti menjadikan skema fungsi tersebut menjadi tipe data baru. Caranya dengan menggunakan keyword `type`. Contoh:
+Untuk fungsi yang skema-nya cukup panjang, akan lebih baik jika menggunakan alias dalam pendefinisiannya, apalagi ketika ada parameter fungsi lain yang juga menggunakan skema yang sama, maka kita tidak perlu menuliskan skema panjang fungsi tersebut berulang-ulang.
+
+Membuat alias fungsi berarti menjadikan skema fungsi tersebut menjadi tipe data baru. Caranya dengan menggunakan keyword `type`. Contoh:
```go
type FilterCallback func(string) bool
From 9d39bc73874287377846f07ff2cb95108ea67ab5 Mon Sep 17 00:00:00 2001
From: novalagung
Date: Tue, 23 Apr 2024 19:09:40 +0700
Subject: [PATCH 8/9] feat: writing improvement
---
content/A-defer-exit.md | 8 ++++----
content/A-error-panic-recover.md | 2 +-
content/A-json.md | 2 +-
content/A-method.md | 6 +++---
content/A-properti-public-dan-private.md | 2 +-
content/A-sql.md | 2 +-
content/B-template-functions.md | 6 +++---
content/C-golang-jwt.md | 4 ++--
8 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/content/A-defer-exit.md b/content/A-defer-exit.md
index 0f891f4e9..f2692f1c1 100644
--- a/content/A-defer-exit.md
+++ b/content/A-defer-exit.md
@@ -19,7 +19,7 @@ func main() {
}
```
-Output:
+Output program:
![Penerapan `defer`](images/A_defer_exit_1_defer.png)
@@ -45,7 +45,7 @@ func orderSomeFood(menu string) {
}
```
-Output:
+Output program:
![Penerapan `defer` dengan `return`](images/A_defer_exit_2_defer_return.png)
@@ -68,7 +68,7 @@ func main() {
}
```
-Output:
+Output program:
```
halo 1
@@ -95,7 +95,7 @@ func main() {
}
```
-Output:
+Output program:
```
halo 1
diff --git a/content/A-error-panic-recover.md b/content/A-error-panic-recover.md
index 6a2291e4a..6d98ac0e3 100644
--- a/content/A-error-panic-recover.md
+++ b/content/A-error-panic-recover.md
@@ -157,7 +157,7 @@ func main() {
}
```
-Output:
+Output program:
![Handle panic menggunakan recover](images/A_error_panic_recover_4_recover.png)
diff --git a/content/A-json.md b/content/A-json.md
index 72ce09b14..bcf51a652 100644
--- a/content/A-json.md
+++ b/content/A-json.md
@@ -125,7 +125,7 @@ var jsonString = string(jsonData)
fmt.Println(jsonString)
```
-Output:
+Output program:
![Encode data ke JSON](images/A_json_2_encode.png)
diff --git a/content/A-method.md b/content/A-method.md
index edc5e2d95..e449763ca 100644
--- a/content/A-method.md
+++ b/content/A-method.md
@@ -44,7 +44,7 @@ func main() {
}
```
-Output:
+Output program:
![Penggunaan method](images/A_method_1_method.png)
@@ -106,7 +106,7 @@ func main() {
}
```
-Output:
+Output program:
![Penggunaan method pointer](images/A_method_2_method_pointer.png)
@@ -124,7 +124,7 @@ var s2 = &student{"ethan hunt", 22}
s2.sayHello()
```
-## A.30.2. Penjelasan tambahan
+## A.25.3. Penjelasan tambahan
Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah dipraktekan:
diff --git a/content/A-properti-public-dan-private.md b/content/A-properti-public-dan-private.md
index 20c6a62f7..87d28e37c 100644
--- a/content/A-properti-public-dan-private.md
+++ b/content/A-properti-public-dan-private.md
@@ -176,7 +176,7 @@ fmt.Println("name ", s1.Name)
fmt.Println("grade", s1.grade)
```
-Output:
+Output program:
![Error lain muncul saat menjalankan program](images/A_properti_public_private_4_error.png)
diff --git a/content/A-sql.md b/content/A-sql.md
index dd3f0b1fd..a87811434 100644
--- a/content/A-sql.md
+++ b/content/A-sql.md
@@ -174,7 +174,7 @@ func main() {
}
```
-Output:
+Output program:
![Membaca data dari database server](images/A_sql_2_sql_query.png)
diff --git a/content/B-template-functions.md b/content/B-template-functions.md
index b80d8e9ec..86b59b70d 100644
--- a/content/B-template-functions.md
+++ b/content/B-template-functions.md
@@ -168,7 +168,7 @@ Cara penggunannya juga masih sama.
```
-Output:
+Output program:
![Fungsi String](images/B_template_functions_4_string_func.png)
@@ -205,7 +205,7 @@ Berikut merupakan contoh penerapan fungsi `len` dan `index`.
```
-Output:
+Output program:
![Fungsi `len` dan `index`](images/B_template_functions_5_len_index.png)
@@ -224,7 +224,7 @@ Selain fungsi operator perbandingan, terdapat juga operator logika `or`, `and`,
{{end}}
```
-Output:
+Output program:
![Fungsi `or`, `and`, dan `not`](images/B_template_functions_6_or_and_not.png)
diff --git a/content/C-golang-jwt.md b/content/C-golang-jwt.md
index 7d8b1489b..8e73621c4 100644
--- a/content/C-golang-jwt.md
+++ b/content/C-golang-jwt.md
@@ -377,7 +377,7 @@ Jalankan aplikasi, lalu test menggunakan curl.
curl -X POST --user noval:kaliparejaya123 http://localhost:8080/login
```
-Output:
+Output program:
![JWT Authentication](images/C_golang_jwt_2_jwt_authentication.png)
@@ -391,7 +391,7 @@ curl -X GET \
http://localhost:8080/index
```
-Output:
+Output program:
![JWT Authorization](images/C_golang_jwt_3_jwt_authorization.png)
From 359c3235e4f0b2bae534bd6b81b60c3b2463c879 Mon Sep 17 00:00:00 2001
From: novalagung
Date: Tue, 23 Apr 2024 19:10:02 +0700
Subject: [PATCH 9/9] feat: update A.20. Fungsi Variadic
---
content/A-fungsi-variadic.md | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/content/A-fungsi-variadic.md b/content/A-fungsi-variadic.md
index 81ee86407..7263e9926 100644
--- a/content/A-fungsi-variadic.md
+++ b/content/A-fungsi-variadic.md
@@ -1,16 +1,16 @@
# A.20. Fungsi Variadic
-Go mengadopsi konsep **variadic function** atau pembuatan fungsi dengan parameter sejenis yang tak terbatas. Maksud **tak terbatas** di sini adalah jumlah parameter yang disisipkan ketika pemanggilan fungsi bisa berapa saja.
+Go mengadopsi konsep **variadic function** atau pembuatan fungsi dengan parameter bisa menampung nilai sejenis yang tidak terbatas jumlahnya.
-Parameter variadic memiliki sifat yang mirip dengan slice. Nilai dari parameter-parameter yang disisipkan bertipe data sama, dan ditampung oleh sebuah variabel saja. Cara pengaksesan tiap datanya juga sama, dengan menggunakan index.
+Parameter variadic memiliki sifat yang mirip dengan slice, yaitu nilai dari parameter-parameter yang disisipkan bertipe data sama, dan kesemuanya cukup ditampung oleh satu variabel saja. Cara pengaksesan tiap nilai juga mirip, yaitu dengan menggunakan index.
Pada chapter ini kita akan belajar mengenai cara penerapan fungsi variadic.
## A.20.1. Penerapan Fungsi Variadic
-Deklarasi parameter variadic sama dengan cara deklarasi variabel biasa, pembedanya adalah pada parameter jenis ini ditambahkan tanda titik tiga kali (`...`) tepat setelah penulisan variabel (sebelum tipe data). Nantinya semua nilai yang disisipkan sebagai parameter akan ditampung oleh variabel tersebut.
+Deklarasi parameter variadic sama dengan cara deklarasi variabel biasa, pembedanya adalah pada parameter jenis ini ditambahkan tanda titik tiga kali (`...`) tepat setelah penulisan variabel, sebelum tipe data. Nantinya semua nilai yang disisipkan sebagai parameter akan ditampung oleh variabel tersebut.
-Berikut merupakan contoh penerepannya.
+Contoh program:
```go
package main
@@ -62,13 +62,15 @@ Berikut merupakan penjelasan tambahan untuk beberapa hal dari kode yang sudah di
#### ◉ Penggunaan Fungsi `fmt.Sprintf()`
-Fungsi `fmt.Sprintf()` pada dasarnya sama dengan `fmt.Printf()`, hanya saja fungsi ini tidak menampilkan nilai, melainkan mengembalikan nilainya dalam bentuk string. Pada kasus di atas, nilai kembalian `fmt.Sprintf()` ditampung oleh variabel `msg`.
+Fungsi `fmt.Sprintf()` pada dasarnya sama dengan `fmt.Printf()`, hanya saja fungsi ini tidak menampilkan nilai, melainkan mengembalikan nilainya dalam bentuk string. Pada case di atas, nilai kembalian `fmt.Sprintf()` ditampung oleh variabel `msg`.
Selain `fmt.Sprintf()`, ada juga `fmt.Sprint()` dan `fmt.Sprintln()`.
#### ◉ Penggunaan Fungsi `float64()`
-Sebelumnya sudah dibahas bahwa `float64` merupakan tipe data. Tipe data jika ditulis sebagai fungsi (penandanya ada tanda kurungnya) berguna untuk **casting**. Casting sendiri adalah teknik untuk konversi tipe sebuah data ke tipe lain. Sebagian besar tipe data dasar yang telah dipelajari pada chapter [A.9. Variabel](/A-variabel.html) bisa di-cast. Dan cara penerapannya juga sama, cukup panggil sebagai fungsi, lalu masukan data yang ingin dikonversi sebagai parameter.
+Sebelumnya sudah dibahas bahwa `float64` merupakan tipe data. Tipe data jika ditulis sebagai fungsi (penandanya ada tanda kurungnya) menandakan bahwa digunakan untuk keperluan **casting**. Casting sendiri adalah teknik untuk konversi tipe sebuah data ke tipe lain. Sebagian besar tipe data dasar yang telah dipelajari pada chapter [A.9. Variabel](/A-variabel.html) bisa di-casting.
+
+Cara penerapan casting: panggil saja tipe data yang diingunkan seperti pemanggilan fungsi, lalu masukan data yang ingin dikonversi sebagai argument pemanggilan fungsi tersebut.
Pada contoh di atas, variabel `total` yang tipenya adalah `int`, dikonversi menjadi `float64`, begitu juga `len(numbers)` yang menghasilkan `int` dikonversi ke `float64`.
@@ -76,11 +78,9 @@ Variabel `avg` perlu dijadikan `float64` karena penghitungan rata-rata lebih ser
Operasi bilangan (perkalian, pembagian, dan lainnya) di Go hanya bisa dilakukan jika tipe datanya sejenis. Maka dari itulah perlu adanya casting ke tipe `float64` pada tiap operand.
----
-
-## A.20.2. Pengisian Parameter Fungsi Variadic Menggunakan Data Slice
+## A.20.3. Pengisian Parameter Fungsi Variadic Menggunakan Data Slice
-Slice bisa digunakan sebagai parameter variadic. Caranya dengan menambahkan tanda titik tiga kali, tepat setelah nama variabel yang dijadikan parameter. Contohnya bisa dilihat pada kode berikut.
+Slice bisa digunakan sebagai argument pada fungsi variadic. Caranya penerapannya: tulis saja nama variabel tapi disertai dengan tanda titik tiga kali, dituliskan tepat setelah nama variabel yang dijadikan parameter. Contohnya bisa dilihat pada kode berikut:
```go
var numbers = []int{2, 4, 3, 5, 4, 3, 3, 5, 5, 3}
@@ -90,9 +90,9 @@ var msg = fmt.Sprintf("Rata-rata : %.2f", avg)
fmt.Println(msg)
```
-Pada kode di atas, variabel `numbers` yang merupakan slice int, disisipkan ke fungsi `calculate()` sebagai parameter variadic (bisa dilihat tanda 3 titik setelah penulisan variabel). Teknik ini sangat berguna ketika sebuah data slice ingin difungsikan sebagai parameter variadic.
+Pada kode di atas, variabel `numbers` bertipe data slice int, disisipkan pada pemanggilan fungsi `calculate()` sebagai argument parameter fungsi variadic (bisa dilihat tanda 3 titik setelah penulisan variabel). Teknik ini sangat berguna pada case dimana sebuah data slice perlu untuk digunakan sebagai argument parameter variadic.
-Perhatikan juga kode berikut ini. Intinya adalah sama, hanya caranya yang berbeda.
+Agar lebih jelas, perhatikan 2 kode berikut. Intinya sama, hanya cara penulisannya yang berbeda.
```go
var numbers = []int{2, 4, 3, 5, 4, 3, 3, 5, 5, 3}
@@ -105,7 +105,7 @@ var avg = calculate(2, 4, 3, 5, 4, 3, 3, 5, 5, 3)
Pada deklarasi parameter fungsi variadic, tanda 3 titik (`...`) dituliskan sebelum tipe data parameter. Sedangkan pada pemanggilan fungsi dengan menyisipkan parameter array, tanda tersebut dituliskan di belakang variabelnya.
-## A.20.3. Fungsi Dengan Parameter Biasa & Variadic
+## A.20.4. Fungsi Dengan Parameter Biasa & Variadic
Parameter variadic bisa dikombinasikan dengan parameter biasa, dengan syarat parameter variadic-nya harus diposisikan di akhir. Contohnya bisa dilihat pada kode berikut.
@@ -123,7 +123,7 @@ func yourHobbies(name string, hobbies ...string) {
Nilai parameter pertama fungsi `yourHobbies()` akan ditampung oleh `name`, sedangkan nilai parameter kedua dan seterusnya akan ditampung oleh `hobbies` sebagai slice.
-Cara pemanggilannya masih sama seperi pada fungsi biasa.
+Cara pemanggilannya masih sama seperi pada fungsi biasa, contoh:
```go
func main() {
@@ -131,7 +131,7 @@ func main() {
}
```
-Jika parameter kedua dan seterusnya ingin diisi dengan data dari slice, maka gunakan tanda titik tiga kali.
+Jika parameter kedua dan seterusnya ingin diisi dengan data dari slice, maka gunakan tanda titik tiga kali seperti ini:
```go
func main() {