diff --git a/content/A-array.md b/content/A-array.md index 2d4e75f32..cfb047bb9 100644 --- a/content/A-array.md +++ b/content/A-array.md @@ -199,7 +199,7 @@ Parameter pertama keyword `make` diisi dengan tipe data elemen array yang diingi --- diff --git a/content/A-buffered-channel.md b/content/A-buffered-channel.md index b5e16bf82..11f3f026c 100644 --- a/content/A-buffered-channel.md +++ b/content/A-buffered-channel.md @@ -68,7 +68,7 @@ Lebih detailnya mengenai fungsi `time.Sleep()` dan `time.Second` dibahas pada ch --- diff --git a/content/A-channel-range-close.md b/content/A-channel-range-close.md index 9ce0ee6a7..3bd29d9f0 100644 --- a/content/A-channel-range-close.md +++ b/content/A-channel-range-close.md @@ -73,7 +73,7 @@ Dan sebaliknya pada fungsi `printMessage(ch <-chan string)`, channel `ch` hanya --- diff --git a/content/A-channel-select.md b/content/A-channel-select.md index 07b9603bd..bd779f787 100644 --- a/content/A-channel-select.md +++ b/content/A-channel-select.md @@ -81,7 +81,7 @@ Cukup mudah bukan? --- diff --git a/content/A-channel-timeout.md b/content/A-channel-timeout.md index a76d38bae..59d151d3a 100644 --- a/content/A-channel-timeout.md +++ b/content/A-channel-timeout.md @@ -68,7 +68,7 @@ Muncul output setiap kali ada penerimaan data dengan delay waktu acak. Ketika da --- diff --git a/content/A-channel.md b/content/A-channel.md index 583ada27a..c7cedee60 100644 --- a/content/A-channel.md +++ b/content/A-channel.md @@ -164,7 +164,7 @@ fmt.Println(message) --- diff --git a/content/A-client-http-request-simple.md b/content/A-client-http-request-simple.md index 91b737d3f..89c6d64cf 100644 --- a/content/A-client-http-request-simple.md +++ b/content/A-client-http-request-simple.md @@ -173,7 +173,7 @@ Sampai sini kita telah belajar bagaimana cara membuat http request sederhana unt --- diff --git a/content/A-command-line-args-flag.md b/content/A-command-line-args-flag.md index d79470285..e93a3f24f 100644 --- a/content/A-command-line-args-flag.md +++ b/content/A-command-line-args-flag.md @@ -130,7 +130,7 @@ Kegunaan dari parameter terakhir method-method flag adalah untuk memunculkan hin --- diff --git a/content/A-concurrency-pipeline.md b/content/A-concurrency-pipeline.md index d842a7649..7d09766b1 100644 --- a/content/A-concurrency-pipeline.md +++ b/content/A-concurrency-pipeline.md @@ -8,7 +8,7 @@ Go memiliki beberapa API untuk keperluan konkurensi, dua diantaranya adalah *gor Definisi *pipeline* yang paling mudah versi penulis adalah **beberapa/banyak proses yang berjalan secara konkuren yang masing-masing proses merupakan bagian dari serangkaian tahapan proses yang berhubungan satu sama lain**. -Analoginya seperti ini: bayangkan sebuah flow proses untuk auto backup database secara rutin, yang di mana database server yang perlu di-backup ada banyak. Untuk backup-nya sendiri kita menggunakan program Go, bukan *shell script*. Mungkin secara garis besar serangkaian tahapan proses yang akan dijalankan adalah berikut: +Analoginya seperti ini: bayangkan sebuah flow proses untuk auto backup database secara rutin, yang mana database server yang perlu di-backup ada banyak. Untuk backup-nya sendiri kita menggunakan program Go, bukan *shell script*. Mungkin secara garis besar serangkaian tahapan proses yang akan dijalankan adalah berikut: 1. Kita perlu data *list* dari semua database yang harus di-backup, beserta alamat akses dan kredensial-nya. 2. Kita jalankan proses backup, bisa secara sekuensial (setelah `db1` selesai, lanjut `db2`, lanjut `db3`, dst), atau secara paralel (proses backup `db1`, `db2`, `db3`, dan lainnya dijalankan secara bersamaan). @@ -383,12 +383,12 @@ Fungsi `getSum()` menerima channel dan akan secara aktif memantau dan membaca da Nah, karena di sini kita punya 3 worker yang jelasnya menghasilkan 3 buah channel baru, kita perlu sebuah mekanisme untuk menggabung channel tersebut, agar nanti mudah untuk dikontrol ([SSoT](https://en.wikipedia.org/wiki/Single_source_of_truth)). Di sinilah peran fungsi `mergeChanFileInfo()`. -Fungsi `mergeChanFileInfo()` digunakan untuk *multiplexing* atau menggabung banyak channel ke satu channel saja, yang di mana channel ini juga akan **otomatis di-close ketika channel input (`chanFileContent`) adalah *closed***. Fungsi jenis seperti ini biasa disebut dengan **Fan-in function**. +Fungsi `mergeChanFileInfo()` digunakan untuk *multiplexing* atau menggabung banyak channel ke satu channel saja, yang mana channel ini juga akan **otomatis di-close ketika channel input (`chanFileContent`) adalah *closed***. Fungsi jenis seperti ini biasa disebut dengan **Fan-in function**. Jadi TL;DR nya: * Fungsi Fan-out digunakan untuk pembuatan worker, untuk distribusi job, yang proses distribusinya sendiri akan berhenti ketika channel inputan di-close. -* Fungsi Fan-in digunakan untuk *multiplexing* atau menggabung banyak worker ke satu channel saja, yang di mana channel baru ini juga otomatis di-close ketika channel input adalah closed. +* Fungsi Fan-in digunakan untuk *multiplexing* atau menggabung banyak worker ke satu channel saja, yang mana channel baru ini juga otomatis di-close ketika channel input adalah closed. Sekarang lanjut buat fungsi `getSum()`. @@ -408,7 +408,7 @@ func getSum(chanIn <-chan FileInfo) <-chan FileInfo { } ``` -Bisa dilihat, di situ channel inputan `chanIn` di-listen dan setiap ada penerimaan data (via channel tersebut) dilanjut ke proses kalkulasi md5 hash. Hasil hash-nya di tambahkan ke data `FileInfo` kemudian dikirim lagi ke channel `chanOut` yang di mana channel ini merupakan nilai balik fungsi `getSum()`. +Bisa dilihat, di situ channel inputan `chanIn` di-listen dan setiap ada penerimaan data (via channel tersebut) dilanjut ke proses kalkulasi md5 hash. Hasil hash-nya di tambahkan ke data `FileInfo` kemudian dikirim lagi ke channel `chanOut` yang mana channel ini merupakan nilai balik fungsi `getSum()`. Ketika `chanIn` closed, maka bisa diasumsikan semua data sudah dikirim. Jika memang iya dan data-data tersebut sudah di proses (pencarian md5hash-nya), maka channel `chanOut` juga di-close. @@ -539,7 +539,7 @@ Ok sekian untuk chapter panjang ini. --- diff --git a/content/A-data-type-conversion.md b/content/A-data-type-conversion.md index 135d4f3ba..d46d8af97 100644 --- a/content/A-data-type-conversion.md +++ b/content/A-data-type-conversion.md @@ -247,7 +247,7 @@ Kombinasi `switch` - `case` bisa dimanfaatkan untuk deteksi tipe konkret data ya --- diff --git a/content/A-defer-exit.md b/content/A-defer-exit.md index bb719c471..7361a2807 100644 --- a/content/A-defer-exit.md +++ b/content/A-defer-exit.md @@ -131,7 +131,7 @@ Meskipun `defer fmt.Println("halo")` ditempatkan sebelum `os.Exit()`, statement --- diff --git a/content/A-encoding-base64.md b/content/A-encoding-base64.md index 5d4bbfd1d..b0f1255ee 100644 --- a/content/A-encoding-base64.md +++ b/content/A-encoding-base64.md @@ -83,7 +83,7 @@ fmt.Println(decodedString) --- diff --git a/content/A-error-panic-recover.md b/content/A-error-panic-recover.md index dca1c4ff4..6f3ba2640 100644 --- a/content/A-error-panic-recover.md +++ b/content/A-error-panic-recover.md @@ -220,7 +220,7 @@ Bisa dilihat di dalam perulangan terdapat sebuah IIFE untuk recover panic dan ju --- diff --git a/content/A-exec.md b/content/A-exec.md index 7c05f887e..e6943b853 100644 --- a/content/A-exec.md +++ b/content/A-exec.md @@ -55,7 +55,7 @@ Selain `.Output()` ada sangat banyak sekali API untuk keperluan komunikasi denga --- diff --git a/content/A-file.md b/content/A-file.md index 58b386141..d68749919 100644 --- a/content/A-file.md +++ b/content/A-file.md @@ -154,7 +154,7 @@ func main() { --- diff --git a/content/A-fungsi-closure.md b/content/A-fungsi-closure.md index b8e6ab54b..70801b73b 100644 --- a/content/A-fungsi-closure.md +++ b/content/A-fungsi-closure.md @@ -176,7 +176,7 @@ Output program: --- diff --git a/content/A-fungsi-multiple-return.md b/content/A-fungsi-multiple-return.md index 2ac5893f0..e30d1a649 100644 --- a/content/A-fungsi-multiple-return.md +++ b/content/A-fungsi-multiple-return.md @@ -97,7 +97,7 @@ Fungsi `math.Pow()` digunakan untuk operasi pangkat nilai. `math.Pow(2, 3)` bera --- diff --git a/content/A-fungsi-sebagai-parameter.md b/content/A-fungsi-sebagai-parameter.md index 0e27fde4d..b714c0a39 100644 --- a/content/A-fungsi-sebagai-parameter.md +++ b/content/A-fungsi-sebagai-parameter.md @@ -102,7 +102,7 @@ Variabel `result` bernilai `true` karena string `"ang"` merupakan bagian dari st --- diff --git a/content/A-fungsi-variadic.md b/content/A-fungsi-variadic.md index 7263e9926..056ad76f7 100644 --- a/content/A-fungsi-variadic.md +++ b/content/A-fungsi-variadic.md @@ -147,7 +147,7 @@ Output program: --- diff --git a/content/A-fungsi.md b/content/A-fungsi.md index dc748eec4..81a66aa39 100644 --- a/content/A-fungsi.md +++ b/content/A-fungsi.md @@ -177,7 +177,7 @@ Di dalamnya terdapat proses validasi nilai variabel pembagi, jika nilainya adala --- diff --git a/content/A-go-vendoring.md b/content/A-go-vendoring.md index c2deefdb0..0489772c1 100644 --- a/content/A-go-vendoring.md +++ b/content/A-go-vendoring.md @@ -62,7 +62,7 @@ Konsekuensi penerapan vendoring adalah size project menjadi cukup besar. Untuk p --- diff --git a/content/A-golang-generics.md b/content/A-golang-generics.md index e79828b1a..07e85c9e3 100644 --- a/content/A-golang-generics.md +++ b/content/A-golang-generics.md @@ -63,7 +63,7 @@ Penulisan notasi fungsi dengan Generic kurang lebih sebagai berikut: func FuncName[dataType ](params) ``` -Pada kode di atas, tipe data `[]int` kita ganti menjadi tipe data `[]V`, yang di mana tipe `V` dideklarasikan dengan notasi `[V int]`. Tipe data `V` di situ artinya kompatibel atau *comparable* dengan tipe `int`. Bisa diambil kesimpulan kedua fungsi yang telah kita tulis adalah ekuivalen. +Pada kode di atas, tipe data `[]int` kita ganti menjadi tipe data `[]V`, yang mana tipe `V` dideklarasikan dengan notasi `[V int]`. Tipe data `V` di situ artinya kompatibel atau *comparable* dengan tipe `int`. Bisa diambil kesimpulan kedua fungsi yang telah kita tulis adalah ekuivalen. ```go func Sum(numbers []int) int { @@ -276,7 +276,7 @@ Sampai artikel ini ditulis, generic tidak bisa diterapkan pada method (meski bis --- diff --git a/content/A-goroutine.md b/content/A-goroutine.md index 758b71cc0..59d598bdb 100644 --- a/content/A-goroutine.md +++ b/content/A-goroutine.md @@ -91,7 +91,7 @@ Bisa dilihat pada kode di atas, untuk menampung inputan text `trafalgar d law`, --- diff --git a/content/A-hash-sha1.md b/content/A-hash-sha1.md index 213fdce2d..bbea5df35 100644 --- a/content/A-hash-sha1.md +++ b/content/A-hash-sha1.md @@ -97,7 +97,7 @@ Metode ini sering dipakai untuk enkripsi password user. Salt dan data hasil hash --- diff --git a/content/A-hello-world.md b/content/A-hello-world.md index df9f388b5..f1e954de0 100644 --- a/content/A-hello-world.md +++ b/content/A-hello-world.md @@ -125,7 +125,7 @@ Contoh statement di atas akan menghasilkan output: **Hello world! how are you**. --- diff --git a/content/A-interface-kosong.md b/content/A-interface-kosong.md index ca9e8edbb..af7fb4058 100644 --- a/content/A-interface-kosong.md +++ b/content/A-interface-kosong.md @@ -150,7 +150,7 @@ for _, each := range fruits { --- diff --git a/content/A-interface.md b/content/A-interface.md index 136d76ed0..cd484ed4a 100644 --- a/content/A-interface.md +++ b/content/A-interface.md @@ -179,7 +179,7 @@ Pada chapter [A.23. Pointer](/A-pointer.html) dijelaskan bahwa method pointer bi --- diff --git a/content/A-json.md b/content/A-json.md index d20c97510..67e505925 100644 --- a/content/A-json.md +++ b/content/A-json.md @@ -132,7 +132,7 @@ Output program: --- diff --git a/content/A-komentar.md b/content/A-komentar.md index 0790e783b..ea364f2ce 100644 --- a/content/A-komentar.md +++ b/content/A-komentar.md @@ -47,7 +47,7 @@ Sifat komentar ini sama seperti komentar inline, yaitu sama-sama diabaikan oleh --- diff --git a/content/A-konstanta.md b/content/A-konstanta.md index e3c130222..468de16e4 100644 --- a/content/A-konstanta.md +++ b/content/A-konstanta.md @@ -100,7 +100,7 @@ const three, four string = "tiga", "empat" --- diff --git a/content/A-map.md b/content/A-map.md index d8fa8aff8..9a9ab776f 100644 --- a/content/A-map.md +++ b/content/A-map.md @@ -173,7 +173,7 @@ var data = []map[string]string{ --- diff --git a/content/A-method.md b/content/A-method.md index ba0d3f47f..cdd2afb53 100644 --- a/content/A-method.md +++ b/content/A-method.md @@ -157,7 +157,7 @@ Lebih detailnya dibahas pada chapter selanjutnya. --- diff --git a/content/A-mongodb.md b/content/A-mongodb.md index d3786986c..8d277336c 100644 --- a/content/A-mongodb.md +++ b/content/A-mongodb.md @@ -327,7 +327,7 @@ if len(result) > 0 { --- diff --git a/content/A-mutex.md b/content/A-mutex.md index 66ee5c926..e4c263342 100644 --- a/content/A-mutex.md +++ b/content/A-mutex.md @@ -172,7 +172,7 @@ func main() { --- diff --git a/content/A-operator.md b/content/A-operator.md index 1c2135657..eb3402faa 100644 --- a/content/A-operator.md +++ b/content/A-operator.md @@ -93,7 +93,7 @@ Template `\t` digunakan untuk menambahkan indent tabulasi. Biasa dimanfaatkan un --- diff --git a/content/A-perulangan.md b/content/A-perulangan.md index bc0d43608..635b42628 100644 --- a/content/A-perulangan.md +++ b/content/A-perulangan.md @@ -162,7 +162,7 @@ Pada `for` bagian dalam, terdapat seleksi kondisi untuk pengecekan nilai `i`. Ke --- diff --git a/content/A-pipeline-context-cancellation.md b/content/A-pipeline-context-cancellation.md index e904ca11e..a20895b7a 100644 --- a/content/A-pipeline-context-cancellation.md +++ b/content/A-pipeline-context-cancellation.md @@ -241,7 +241,7 @@ Cara pembuatan object context sendiri sebenarnya ada 3: 3. Menggunakan fungsi `context.With...`. Fungsi ini sebenarnya bukan digunakan untuk inisialisasi objek konteks baru, tapi digunakan untuk menambahkan informasi tertentu pada *copied* context yang disisipkan di parameter pertama pemanggilan fungsi. Ada 3 buah fungsi `context.With...` yang bisa digunakan, yaitu: - Fungsi `context.WithCancel(ctx) (ctx, cancel)`. Fungsi ini digunakan untuk menambahkan fasilitas *cancellable* pada context yang disisipkan sebagai parameter pertama pemanggilan fungsi. Lewat nilai balik kedua, yaitu `cancel` yang tipenya `context.CancelFunc`, kita bisa secara paksa meng-*cancel* context ini. - - Fungsi `context.WithDeadline(ctx, time.Time) (ctx, cancel)`. Fungsi ini juga menambahkan fitur *cancellable* pada context, tapi selain itu juga menambahkan informasi deadline yang di mana jika waktu sekarang sudah melebihi deadline yang sudah ditentukan maka context otomatis di-cancel secara paksa. + - Fungsi `context.WithDeadline(ctx, time.Time) (ctx, cancel)`. Fungsi ini juga menambahkan fitur *cancellable* pada context, tapi selain itu juga menambahkan informasi deadline yang mana jika waktu sekarang sudah melebihi deadline yang sudah ditentukan maka context otomatis di-cancel secara paksa. - Fungsi `context.WithTimeout(ctx, time.Duration) (ctx, cancel)`. Fungsi ini sama seperti `context.WithDeadline()`, bedanya pada parameter kedua argument bertipe durasi (bukan objek `time.Time`). Kesamaan dari ketiga fungsi `context.With...` adalah sama-sama menambahkan fasilitas *cancellable* yang bisa dieksekusi lewat nilai balik kedua fungsi tersebut (yang tipenya `context.CancelFunc`). @@ -448,7 +448,7 @@ Perbedannya ada pada penerapan *cancellation*-nya. Pada contoh ini kita tidak me --- diff --git a/content/A-pointer.md b/content/A-pointer.md index 53117e3b4..1d782695a 100644 --- a/content/A-pointer.md +++ b/content/A-pointer.md @@ -101,7 +101,7 @@ Nilai variabel `number` berubah menjadi `10` karena perubahan yang terjadi di da --- diff --git a/content/A-properti-public-dan-private.md b/content/A-properti-public-dan-private.md index 9382caad9..3de0f328e 100644 --- a/content/A-properti-public-dan-private.md +++ b/content/A-properti-public-dan-private.md @@ -337,7 +337,7 @@ Di Go, setiap package masing-masing boleh memiliki fungsi `init()`. Fungsi terse --- diff --git a/content/A-random.md b/content/A-random.md index 474293c6a..3f6621e79 100644 --- a/content/A-random.md +++ b/content/A-random.md @@ -117,7 +117,7 @@ Dengan fungsi di atas kita bisa dengan mudah meng-generate string random dengan --- diff --git a/content/A-reflect.md b/content/A-reflect.md index dc6bb128c..1d626f9f5 100644 --- a/content/A-reflect.md +++ b/content/A-reflect.md @@ -168,7 +168,7 @@ Jika eksekusi method diikuti pengisian parameter, maka parameternya harus dituli --- diff --git a/content/A-regex.md b/content/A-regex.md index 1503460c8..8b9db8d91 100644 --- a/content/A-regex.md +++ b/content/A-regex.md @@ -165,7 +165,7 @@ Pada contoh di atas, ekspresi regexp `[a-b]+` digunakan sebagai kriteria split. --- diff --git a/content/A-seleksi-kondisi.md b/content/A-seleksi-kondisi.md index e5906b979..b13792992 100644 --- a/content/A-seleksi-kondisi.md +++ b/content/A-seleksi-kondisi.md @@ -202,7 +202,7 @@ if point > 7 { --- diff --git a/content/A-simplified-fan-in-fan-out-pipeline.md b/content/A-simplified-fan-in-fan-out-pipeline.md index e3ecc0229..a8278f685 100644 --- a/content/A-simplified-fan-in-fan-out-pipeline.md +++ b/content/A-simplified-fan-in-fan-out-pipeline.md @@ -1,6 +1,6 @@ # A.63. Concurrency Pattern: Simplified Fan-out Fan-in Pipeline -Pada chapter sebelumnya, yaitu chapter [A.62. Concurrency Pattern: Pipeline](/A-concurrency-pipeline.html), kita telah mempelajari tentang pipeline pattern, yang di mana pattern tersebut merupakan rekomendasi dari tim Go dalam meng-*handle* jenis kasus sekarangkain proses yang berjalan secara konkuren. +Pada chapter sebelumnya, yaitu chapter [A.62. Concurrency Pattern: Pipeline](/A-concurrency-pipeline.html), kita telah mempelajari tentang pipeline pattern, yang mana pattern tersebut merupakan rekomendasi dari tim Go dalam meng-*handle* jenis kasus sekarangkain proses yang berjalan secara konkuren. > Penulis sangat anjurkan untuk mencoba mempelajari praktek chapter sebelumnya terlebih dahulu jika belum. Karena chapter kali ini ada hubungannya dengan chapter tersebut. @@ -334,7 +334,7 @@ Nah dari sini semoga cukup jelas ya bedanya kalau dari sisi performa. Inilah pen --- diff --git a/content/A-slice.md b/content/A-slice.md index d459aa7df..93ffee30d 100644 --- a/content/A-slice.md +++ b/content/A-slice.md @@ -151,7 +151,7 @@ fruits[x:y] **Slicing** yang dimulai dari indeks **0** hingga **y** akan mengembalikan elemen-elemen mulai indeks **0** hingga sebelum indeks **y**, dengan lebar kapasitas adalah sama dengan slice aslinya. -Sedangkan slicing yang dimulai dari indeks **x**, yang di mana nilai **x** adalah lebih dari **0**, membuat elemen ke-**x** slice yang diambil menjadi elemen ke-0 slice baru. Hal inilah yang membuat kapasitas slice berubah. +Sedangkan slicing yang dimulai dari indeks **x**, yang mana nilai **x** adalah lebih dari **0**, membuat elemen ke-**x** slice yang diambil menjadi elemen ke-0 slice baru. Hal inilah yang membuat kapasitas slice berubah. ## A.16.6. Fungsi `append()` @@ -258,7 +258,7 @@ fmt.Println(cap(bFruits)) // cap: 2 --- diff --git a/content/A-sql.md b/content/A-sql.md index d2464aadb..2f4ceb2a5 100644 --- a/content/A-sql.md +++ b/content/A-sql.md @@ -342,7 +342,7 @@ Selengkapya mengenai driver yang tersedia di Go silakan lihat di [https://go.dev --- diff --git a/content/A-string-format.md b/content/A-string-format.md index 5e0cea832..3c66f8a09 100644 --- a/content/A-string-format.md +++ b/content/A-string-format.md @@ -270,7 +270,7 @@ fmt.Printf("%%\n") --- diff --git a/content/A-strings.md b/content/A-strings.md index 7419ae2d0..90145ea99 100644 --- a/content/A-strings.md +++ b/content/A-strings.md @@ -158,7 +158,7 @@ fmt.Println(str) // "EAT!" --- diff --git a/content/A-struct.md b/content/A-struct.md index f4f757dc7..a3fa021c7 100644 --- a/content/A-struct.md +++ b/content/A-struct.md @@ -408,7 +408,7 @@ var num Number = 12 --- diff --git a/content/A-time-duration.md b/content/A-time-duration.md index 354be56c9..2e7521d4a 100644 --- a/content/A-time-duration.md +++ b/content/A-time-duration.md @@ -121,7 +121,7 @@ duration := time.Duration(n) * time.Second --- diff --git a/content/A-time-parsing-format.md b/content/A-time-parsing-format.md index 8eb4070e1..e463af1f4 100644 --- a/content/A-time-parsing-format.md +++ b/content/A-time-parsing-format.md @@ -214,7 +214,7 @@ Kode di atas menghasilkan error karena format tidak sesuai dengan skema data yan --- diff --git a/content/A-timer-ticker-scheduler.md b/content/A-timer-ticker-scheduler.md index ddcce0eb7..92d4c4853 100644 --- a/content/A-timer-ticker-scheduler.md +++ b/content/A-timer-ticker-scheduler.md @@ -126,7 +126,7 @@ func main() { Pada contoh di atas bisa dilihat, selain ticker disiapkan juga variabel channel `done`. Variabel ini kita gunakan untuk mengontrol kapan ticker harus di stop. -Cara kerja program di atas: teknik `for` - `select` pada channel digunakan untuk mengecek penerimaan data dari channel `done` dan `ticker.C`. By default, channel `ticker.C` akan menerima kiriman data setiap `X` duration yang di mana pada kode di atas adalah 1 detik (lihat argumen inisialisasi objek ticker). +Cara kerja program di atas: teknik `for` - `select` pada channel digunakan untuk mengecek penerimaan data dari channel `done` dan `ticker.C`. By default, channel `ticker.C` akan menerima kiriman data setiap `X` duration yang mana pada kode di atas adalah 1 detik (lihat argumen inisialisasi objek ticker). Data yang dikirimkan via channel `ticker.C` adalah data date-time kapan event itu terjadi. Pada kode di atas, setiap ada kiriman data via channel tersebut kita tampilkan. @@ -197,7 +197,7 @@ Ketika user tidak menginputkan apa-apa dalam kurun waktu 5 detik, pesan timeout --- diff --git a/content/A-tipe-data.md b/content/A-tipe-data.md index 9e72bfaaa..b8c8462f0 100644 --- a/content/A-tipe-data.md +++ b/content/A-tipe-data.md @@ -122,7 +122,7 @@ Nantinya kita akan sering bertemu dengan nilai `nil` setelah masuk pada pembahas --- diff --git a/content/A-unit-test.md b/content/A-unit-test.md index 8e427229a..03beb1ed1 100644 --- a/content/A-unit-test.md +++ b/content/A-unit-test.md @@ -187,7 +187,7 @@ Fungsi `assert.Equal()` digunakan untuk uji perbandingan. Parameter ke-2 dibandi --- diff --git a/content/A-url-parsing.md b/content/A-url-parsing.md index a12e46917..0bdc2ede6 100644 --- a/content/A-url-parsing.md +++ b/content/A-url-parsing.md @@ -39,7 +39,7 @@ Selain itu, query yang ada pada url akan otomatis diparsing juga, menjadi bentuk --- diff --git a/content/A-variabel.md b/content/A-variabel.md index 77d2fd87d..bf421eb4c 100644 --- a/content/A-variabel.md +++ b/content/A-variabel.md @@ -183,7 +183,7 @@ Nantinya kita akan bahas lebih detail ketika sudah masuk ke pembahasan masing-ma --- diff --git a/content/A-waitgroup.md b/content/A-waitgroup.md index 608acc80a..6075a62ae 100644 --- a/content/A-waitgroup.md +++ b/content/A-waitgroup.md @@ -72,7 +72,7 @@ Kombinasi yang tepat antara `sync.WaitGroup` dan channel sangat penting, keduany --- diff --git a/content/A-web-server.md b/content/A-web-server.md index 0eb5bf7cf..3a35e279a 100644 --- a/content/A-web-server.md +++ b/content/A-web-server.md @@ -125,7 +125,7 @@ Sampai chapter ini yang kita pelajari adalah yang sifatnya fundamental atau dasa --- diff --git a/content/A-web-service-api.md b/content/A-web-service-api.md index e4df9a3c1..c84a5a2fc 100644 --- a/content/A-web-service-api.md +++ b/content/A-web-service-api.md @@ -143,7 +143,7 @@ Data user ID pada endpoint `/user` ditulis dalam format query parameters, yaitu --- diff --git a/content/B-ajax-json-payload.md b/content/B-ajax-json-payload.md index 25d46d77f..4beadd093 100644 --- a/content/B-ajax-json-payload.md +++ b/content/B-ajax-json-payload.md @@ -1,22 +1,24 @@ # B.14. AJAX JSON Payload -Sebelumnya kita telah belajar bagaimana cara submit data dari front-end ke back-end menggunakan teknik **Form Data**. Kali ini kita akan belajar tentang cara request menggunakan teknik **Request Payload** dengan tipe payload adalah **JSON**. +Sebelumnya kita telah mempelajari cara submit data dari front-end ke back-end dengan menggunakan payload **Form Data**. Kali ini kita akan belajar tentang cara request menggunakan payload **JSON**. -Teknik request **Form Data** digunakan salah satu nya pada request submit lewat `
`. Pada chapter ini, kita tidak akan menggunakan cara submit lewat form, melainkan menggunakan teknik AJAX (Asynchronous JavaScript And XML), dengan payload ber-tipe JSON. +> **Form Data** merupakan tipe payload default HTTP request via tag `` -[Perbedaan](http://stackoverflow.com/a/23152367/1467988) antara kedua jenis request tersebut adalah pada isi header `Content-Type`, dan bentuk informasi dikirimkan. Secara default, request lewat ``, content type-nya adalah `application/x-www-form-urlencoded`. Data dikirimkan dalam bentuk query string (key-value) seperti `id=n001&nama=bruce`. +Pada chapter ini, kita tidak akan menggunakan tag `` untuk submit data, melainkan dengan memanfaatkan teknik AJAX (Asynchronous JavaScript And XML) dengan payload JSON. -> Ketika di form ditambahkan atribut `enctype="multipart/form-data"`, maka content type berubah menjadi `multipart/form-data`. +Sebenarnya [perbedaan](http://stackoverflow.com/a/23152367/1467988) antara kedua jenis request tersebut ada di dua hal, yaitu isi header `Content-Type` dan struktur informasi dikirimkan. Request lewat `` secara default memiliki content type `application/x-www-form-urlencoded`, efeknya data dikirimkan dalam bentuk query string (key-value) seperti `id=n001&nama=bruce`. -Request Payload JSON sedikit berbeda, `Content-Type` berisikan `application/json`, dan data disisipkan dalam `Body` dalam bentuk **JSON** string. +> Pengiriman data via tag `` sebenarnya bisa menggunakan content-type selain `application/x-www-form-urlencoded`, yaitu `multipart/form-data`. + +Untuk payload JSON, `Content-Type` yang digunakan adalah `application/json`. Dengannya, data disisipkan di dalam `Body` request dalam bentuk **JSON** string. ## B.14.1. Struktur Folder Proyek -OK, langsung saja, pertama siapkan proyek dengan struktur seperti pada gambar di bawah ini. +OK, mari praktek. Pertama siapkan proyek dengan struktur seperti gambar berikut. ![Struktur proyek](images/B_ajax_json_payload_1_structure.png) -> Silakan unduh file js jQuery dari situs official-nya. +> Silakan unduh file JS jQuery dari situs official-nya. ## B.14.2. Front End - HTML @@ -43,7 +45,7 @@ Layout dari view perlu disiapkan terlebih dahulu, tulis kode berikut pada file ` ``` -Selanjutnya, pada tag `` tambahkan tabel sederhana berisikan inputan-inputan yang diperlukan. Ada tiga buah inputan yang harus dipersiapkan, yaitu: *Name*, *Age*, dan *Gender*; dan juga sebuah button untuk submit form. +Selanjutnya, pada tag `` tambahkan tabel sederhana dengan isi didalamnya adalah inputan form. Ada tiga buah inputan yang perlu dibuat yaitu: *Name*, *Age*, dan *Gender*. Selain itu, sebuah button untuk keperluan submit form juga perlu disiapkan. ```html @@ -79,7 +81,7 @@ Selanjutnya, pada tag `` tambahkan tabel sederhana berisikan inputan-inp ## B.14.3. Front End - HTML -Sekarang kita masuk ke bagian paling menyenangkan/menyebalkan (tergantung taste), yaitu javascript. Siapkan sebuah event `submit` pada `#user-form`. Dalam event tersebut default handler event submit milik `` di-override, diganti dengan AJAX request. +Sekarang kita masuk ke bagian paling menyenangkan/menyebalkan (tergantung taste), yaitu javascript. Siapkan sebuah event `submit` pada `#user-form`. Default handler untuk event submit milik `` di-override, diganti dengan AJAX request. ```js $("#user-form").on("submit", function (e) { @@ -105,9 +107,9 @@ $("#user-form").on("submit", function (e) { }); ``` -Value semua inputan diambil lalu dimasukkan dalam sebuah objek lalu di stringify (agar menjadi JSON string), untuk kemudian di jadikan sebagai payload request. Bisa dilihat pada kode AJAX di atas, `contentType` nilainya adalah `application/json`. +Value semua inputan dalam form diambil, kemudian dimasukkan ke sebuah objek lalu di stringify, agar berubah menjadi JSON string untuk kemudian di jadikan sebagai payload request. Bisa dilihat pada kode AJAX di atas, `contentType` nilainya adalah `application/json`. -Respon dari ajax di atas akan dimunculkan pada `

`. +Respon dari AJAX di atas nantinya dimunculkan pada `

`. ## B.14.4. Back End @@ -145,7 +147,7 @@ func handleIndex(w http.ResponseWriter, r *http.Request) { } ``` -Sedangkan `handleSave` akan memproses request yang di-submit dari bagian depan. +Sedangkan `handleSave` akan memproses request yang di-submit dari front-end. ```go func handleSave(w http.ResponseWriter, r *http.Request) { @@ -175,24 +177,25 @@ func handleSave(w http.ResponseWriter, r *http.Request) { } ``` -Isi payload didapatkan dengan cara men-decode body request (`r.Body`). Proses decoding tidak dilakukan menggunakan `json.Unmarshal()` melainkan lewat json decoder, karena akan [lebih efisien](http://stackoverflow.com/a/21198571/1467988) untuk jenis kasus seperti ini. +Isi payload didapatkan dengan cara men-decode body request (`r.Body`). Proses decoding tidak dilakukan menggunakan `json.Unmarshal()` melainkan lewat JSON decoder dengan alasan [efisiensinya lebih baik](http://stackoverflow.com/a/21198571/1467988). -> Gunakan `json.Decoder` jika data adalah stream `io.Reader`. Gunakan `json.Unmarshal()` untuk decode data sumbernya sudah ada di memory. +- `json.Decoder` cocok digunakan untuk decode data JSON yang sumber datanya adalah stream `io.Reader`, contohnya seperti `r.Body`. +- `json.Unmarshal()` cocok untuk proses decoding yang sumber datanya sudah tersimpan di variabel (bukan stream). -## B.14.5. Test +## B.14.5. Testing -Jalankan program, test hasilnya di browser. +Jalankan program yang telah dibuat, test hasilnya di browser. ![Hasil tes](images/B_ajax_json_payload_2_test.png) -Gunakan fasilitas Developer Tools pada Chrome untuk melihat detail dari request. +Gunakan fasilitas Developer Tools pada Chrome untuk menginspeksi aktifitas AJAX-nya. ![Request](images/B_ajax_json_payload_3_inspect.png) --- diff --git a/content/B-ajax-json-response.md b/content/B-ajax-json-response.md index 127c6d6ef..23bad5207 100644 --- a/content/B-ajax-json-response.md +++ b/content/B-ajax-json-response.md @@ -1,6 +1,6 @@ # B.15. AJAX JSON Response -Pada chapter sebelumnya, kita belajar cara untuk memproses request dengan payload bertipe JSON. Pada chapter ini kita akan belajar untuk membuat satu endpoint yang mengembalikan data JSON string. +Kita telah belajar cara untuk memproses request dengan payload bertipe JSON di chapter sebelumnya. Pembelajaran kali ini masih tentang tipe data JSON tapi lebih fokus ke bagian back-end-nya, yaitu membuat sebuah Web Service API sederhana yang mengembalikan response berbentuk JSON. ## B.15.1. Praktek @@ -21,7 +21,7 @@ func main() { } ``` -Selanjutnya buat handler untuk rute `/`. Di dalam fungsi ini, data dummy ber-tipe slice object disiapkan. Data ini akan dikonversi ke JSON lalu dijadikan nilai balik endpoint `/`. +Selanjutnya buat handler untuk rute `/`. Di dalam fungsi tersenit, disiapkan data dummy ber-tipe slice object. Data ini kemudian dikonversi ke JSON lalu dijadikan nilai balik endpoint `/`. ```go func ActionIndex(w http.ResponseWriter, r *http.Request) { @@ -46,29 +46,27 @@ func ActionIndex(w http.ResponseWriter, r *http.Request) { } ``` -Cara mengkonversi data ke bentuk json cukup mudah, bisa menggunakan `json.Marshal()`. Fungsi ini mengembalikan dua nilai balik, data json (dalam bentuk `[]byte`) dan error jika ada. +Cara mengkonversi data ke bentuk json cukup mudah, bisa menggunakan `json.Marshal()`. Fungsi ini mengembalikan dua nilai balik, data JSON dalam bentuk `[]byte`, dan error jika ada. > Untuk mengambil bentuk string dari hasil konversi JSON, cukup lakukan casting pada data slice bytes tersebut. Contoh: `string(jsonInBytes)` -Karena nilai balik konversi sudah dalam bentuk bytes, maka langsung saja panggil method `Write()` milik `http.ResponseWriter` dan sisipkan data json sebagai argument pemanggilan method. +Karena nilai balik konversi sudah dalam bentuk bytes, maka langsung saja panggil method `Write()` milik `http.ResponseWriter` untuk menjadikannya sebagai API response. Panggil method tersebut, kemudian sisipkan data JSON sebagai argument pemanggilan method. Jangan lupa juga untuk menambahkan response header `Content-Type: application/json`. ## B.15.2. Testing -OK, semua sudah selesai, lakukan testing. +OK, semua sudah selesai, jalankan program lalu test API-nya. ![Testing web server](images/B_ajax_json_response_1_test.png) ## B.15.3. JSON Response menggunakan JSON.Encoder -Pada chapter sebelumnya sudah disinggung, bahwa lebih baik menggunakan `json.Decoder` jika ingin men-decode data yang sumbernya ada di stream `io.Reader` +Pada chapter sebelumnya telah disinggung bahwa lebih baik menggunakan `json.Decoder` jika ingin men-decode data yang sumbernya ada di stream `io.Reader` -Package json juga memiliki fungsi lain-nya yaitu `json.Encoder`, yang sangat cocok digunakan untuk meng-encode data menjadi JSON dengan tujuan objek langsung ke stream `io.Reader`. +Selain `json.Decoder`, ada juga `json.Encoder` yang penggunaannya adalah untuk meng-encode data menjadi JSON dengan output langsung disimpan ke stream `io.Reader`. -Karena tipe `http.ResponseWriter` adalah meng-embed `io.Reader`, maka jelasnya bisa kita terapkan penggunaan encoder di sini. - -Contohnya penerapannya sebagai berikut. +Tipe `http.ResponseWriter` adalah meng-embed `io.Reader`, maka tipe tersebut bisa kita gunakan pada proses encoding menggunakan `json.Encoder`. Contoh penerapannya bisa dilihat berikut ini. ```go w.Header().Set("Content-Type", "application/json") @@ -80,12 +78,12 @@ if err != nil { } ``` -Kode di atas hasilnya ekuivalen dengan penggunaan `json.Marshal`. +Kode di atas hasilnya ekuivalen dengan encoding data object ke JSON string menggunakan `json.Marshal()`. --- diff --git a/content/B-ajax-multi-upload.md b/content/B-ajax-multi-upload.md index d6a5fcc2f..2fb47ca14 100644 --- a/content/B-ajax-multi-upload.md +++ b/content/B-ajax-multi-upload.md @@ -1,20 +1,20 @@ # B.16. AJAX Multiple File Upload -Pada chapter ini, kita akan belajar 3 hal dalam satu waktu, yaitu: +Pada chapter ini, kita akan belajar 3 hal sekaligus yang mencakup poin-poin berikut: -1. Bagaiamana cara untuk upload file via AJAX. -2. Cara untuk handle upload banyak file sekaligus. -3. Cara handle upload file yang lebih hemat memori. +1. Cara untuk upload file via AJAX +2. Cara untuk handle upload banyak file sekaligus +3. Cara handle upload file yang lebih hemat memori -Sebelumnya pada chapter [B.13. Form Upload File](/B-form-upload-file.html), pemrosesan file upload dilakukan lewat **ParseMultipartForm**, sedangkan pada chapter ini metode yang dipakai berbeda, yaitu menggunakan **MultipartReader**. +Sebelumnya, pada chapter [B.13. Form Upload File](/B-form-upload-file.html), pemrosesan file upload dilakukan lewat **ParseMultipartForm**, sedangkan pada chapter ini metode yang dipakai berbeda, yaitu **MultipartReader**. -Kelebihan dari `MultipartReader` adalah, file yang di upload **tidak** di simpan sebagai file temporary di lokal terlebih dahulu (tidak seperti `ParseMultipartForm`), melainkan langsung diambil dari stream `io.Reader`. +Kelebihan dari `MultipartReader` adalah, file yang di upload **tidak** di simpan pada file temporary di lokal terlebih dahulu (tidak seperti `ParseMultipartForm`), melainkan data file bisa diambil langsung dari stream `io.Reader`. -Di bagian front end, upload file secara asynchronous bisa dilakukan menggunakan objek [FormData](https://developer.mozilla.org/en/docs/Web/API/FormData). Semua file dimasukkan dalam objek `FormData`, setelah itu objek tersebut dijadikan sebagai payload AJAX request. +Cara penerapan `MultipartReader` ini membutuhkan front-end untuk melakukan upload file secara asynchronous menggunakan objek [FormData](https://developer.mozilla.org/en/docs/Web/API/FormData). Semua file yang akan di-upload diambil konten dan metadatanya menggunakan javascript untuk dimasukkan ke objek `FormData`. Setelahnya, object tersebut dijadikan sebagai payload AJAX request. ## B.16.1. Struktur Folder Proyek -Mari langsung kita praktekkan, pertama siapkan proyek dengan struktur seperti gambar di bawah ini. +Mari praktekkan, pertama siapkan proyek dengan struktur seperti gambar di bawah ini. ![Folder Structure](images/B_ajax_multi_upload_1_structure.png) @@ -22,9 +22,9 @@ Mari langsung kita praktekkan, pertama siapkan proyek dengan struktur seperti ga ## B.16.2. Front End -Buka `view.html`, siapkan template dasar view. Dalam file ini terdapat satu buah inputan upload file yang mendukung multi upload, dan satu buah tombol submit. +Buka `view.html`, siapkan template dasar view. Dalam file ini terdapat satu buah inputan upload file yang mendukung multi-upload, dan satu buah tombol submit. -Untuk meng-enable kapabilitas multi upload, cukup tambahkan atribut `multiple` pada input file. +Untuk mengaktifkan kapabilitas multi upload, cukup tambahkan atribut `multiple` pada input file. ```html @@ -77,18 +77,18 @@ $("#user-form").on("submit", function (e) { }); ``` -Objek inputan files (yang didapat dari `$("#upload-file")[0].files`) memiliki property `.files` yang isinya merupakan array dari semua file yang dipilih oleh user ketika upload. File-file tersebut di-loop, dimasukkan ke dalam objek `FormData` yang telah dibuat. +Objek inputan files (yang didapat dari `$("#upload-file")[0]`) memiliki property `.files` yang isinya merupakan array dari semua file yang dipilih oleh user ketika upload. File-file tersebut diiterasi, setiap datanya dimasukkan ke dalam objek `FormData` yang telah dibuat. -AJAX dilakukan lewat `jQuery.ajax`. Berikut adalah penjelasan mengenai konfigurasi `processData` dan `contentType` dalam AJAX yang sudah dibuat. +Operasi AJAX request dilakukan lewat `jQuery.ajax`. Berikut adalah penjelasan mengenai konfigurasi `processData` dan `contentType` dalam AJAX yang sudah dibuat. - Konfigurasi `contentType` perlu di set ke `false` agar header Content-Type yang dikirim bisa menyesuaikan data yang disisipkan. - Konfigurasi `processData` juga perlu di set ke `false`, agar data yang akan di kirim tidak otomatis dikonversi ke query string atau json string (tergantung `contentType`). Pada konteks ini kita memerlukan payload tetap dalam tipe `FormData`. -## B.16.3. Back End +## B.16.3. Back-End -Ada 2 route handler yang harus dipersiapkan di back end. Pertama adalah rute `/` yang menampilkan form upload, dan rute `/upload` untuk pemrosesan upload sendiri. +Ada 2 route handler yang harus dipersiapkan di back-end. Pertama adalah rute `/` untuk keperluan memunculkan form upload, dan rute `/upload` untuk pemrosesan upload dari AJAX request. -Buka file `main.go`, isi dengan package yang dibutuhkan, lalu lakukan registrasi dua rute yang dimaksud di atas, beserta satu buah rute untuk static assets. +Buka file `main.go`, import package yang diperlukan, lalu deklarasikan dua rute yang telah disebut di atas, beserta satu buah rute baru untuk *serving* static assets. ```go package main @@ -123,11 +123,11 @@ func handleIndex(w http.ResponseWriter, r *http.Request) { } ``` -Sebelumnya, pada chapter [B.13. Form Upload File](/B-form-upload-file.html), metode yang digunakan untuk handle file upload adalah menggunakan `ParseMultipartForm`, file di proses dalam memori dengan alokasi tertentu, dan jika melebihi alokasi maka akan disimpan pada temporary file. +Sebelumnya, pada chapter [B.13. Form Upload File](/B-form-upload-file.html), metode yang digunakan untuk handle file upload adalah `ParseMultipartForm`. Cara kerjanya, file di proses dalam memori dengan alokasi tertentu, dan jika melebihi alokasi maka akan disimpan pada temporary file. Metode tersebut kurang tepat guna jika digunakan untuk memproses file yang ukurannya besar (file size melebihi `maxMemory`) atau jumlah file-nya sangat banyak (memakan waktu, karena isi dari masing-masing file akan ditampung pada file *temporary* sebelum benar-benar di-copy ke file tujuan). -Solusinya dari dua masalah di atas adalah menggunakan `MultipartReader` untuk handling file upload. Dengan metode ini, file destinasi isinya akan di-copy lagsung dari stream `io.Reader`, tanpa butuh file temporary untuk perantara. +Solusi dari dua masalah yang telah disebutkan adalah menggunakan `MultipartReader` untuk handling file upload. Lewat metode ini, file destinasi isidi-copy lagsung dari stream `io.Reader` tanpa butuh file temporary untuk perantara. Kembali ke bagian perkodingan, siapkan fungsi `handleUpload`, isinya kode berikut. @@ -149,9 +149,9 @@ func handleUpload(w http.ResponseWriter, r *http.Request) { } ``` -Bisa dilihat, method `.MultipartReader()` dipanggil dari objek request milik handler. Mengembalikan dua objek, pertama `*multipart.Reader` dan `error` (jika ada). +Bisa dilihat, method `.MultipartReader()` dipanggil dari objek request milik handler. Operasi tersebut menghasilkan dua nilai balik, `*multipart.Reader` dan `error` (jika ada). -Selanjutnya lakukan perulangan terhadap objek `reader`. Setiap file yang di-upload di proses di masing-masing perulangan. Setelah looping berakhir. idealnya semua file sudah terproses dengan benar. +Selanjutnya, lakukan perulangan terhadap objek `reader`. Setiap file yang di-upload di proses di masing-masing perulangan. Setelah looping berakhir, idealnya semua file sudah terproses dengan benar. ```go for { @@ -181,11 +181,11 @@ w.Write([]byte(`all files uploaded`)) Method `.NextPart()` mengembalikan 2 informasi, yaitu objek stream `io.Reader` (dari file yg di upload), dan `error`. -File destinasi dipersiapkan, kemudian diisi dengan data dari stream file, menggunakan `io.Copy()`. +File destinasi disiapkan kemudian diisi dengan data dari stream file, menggunakan `io.Copy()`. -Jika `reader.NextPart()` mengembalikan error `io.EOF`, menandakan bahwa semua file sudah di proses, maka hentikan perulangan. +Jika `reader.NextPart()` mengembalikan error `io.EOF`, maka bisa disimpulkan semua file telah di proses, kemudian perulangan dihentikan. -OK, semua persiapan sudah cukup. +OK, semua persiapan sudah cukup, selanjutnya masuk fase testing. ## B.16.4. Testing @@ -200,7 +200,7 @@ Cek apakah file sudah terupload. --- diff --git a/content/B-cookie.md b/content/B-cookie.md index 7b8fa6eb5..e718ab1bc 100644 --- a/content/B-cookie.md +++ b/content/B-cookie.md @@ -1,10 +1,10 @@ # B.21. HTTP Cookie -Cookie adalah data dalam bentuk teks yang disimpan pada komputer (oleh web browser) ketika pengunjung sedang surfing ke sebuah situs. Cookie dapat dibuat dari sisi front end (javascript) maupun back end (dalam konteks ini Go). +Cookie adalah data berbentuk teks yang disimpan pada komputer (oleh web browser) yang biasanya didapat ketika pengunjung sedang surfing di internet, mengakses situs website. Pembuatan cookie dilakukan di sisi aplikasi website, pembuatnya bisa si front end (javascript) bisa juga back end (dalam konteks ini Go). -Cookie merupakan salah satu aspek penting dalam pengembangan aplikasi web. Sangat sering kita membutuhkan sebuah data bisa disimpan dan diakses untuk keperluan aplikasi web kita, seperti pengecekan preferensi pengunjung, pengecekan status login tidak nya user. +Cookie merupakan salah satu aspek penting dalam pengembangan aplikasi web. Sangat sering kita memerlukan data di web yang perlu untuk disimpan dan mudah diakses oleh aplikasi web yang dikembangkan, diantaranya untuk keperluan seperti pengecekan preferensi pengunjung, pengecekan status login tidaknya user, dan lainnya. -Pada chapter ini kita akan belajar bagaimana cara membuat dan mengakses cookie di Go. +Pada chapter ini kita akan belajar bagaimana cara membuat dan mengakses cookie lewat Go. ## B.21.1. Praktek @@ -35,10 +35,10 @@ func main() { Variabel `cookieName` berisikan string, nantinya digunakan sebagai nama cookie. - - Rute `/` bertugas untuk membuat cookie baru (jika belum ada atau cookie sudah ada namun expired). + - Rute `/` bertugas untuk membuat cookie baru (jika belum ada atau cookie sudah ada namun expired) - Rute `/delete` mempunyai tugas untuk menghapus cookie, lalu redirect ke `/` sehingga cookie baru akan dibuat -OK, sekarang buat fungsi handler `ActionIndex()`. Di dalam fungsi ini, data berupa random string disimpan dalam cookie. +OK, sekarang buat fungsi handler `ActionIndex()`. Di dalam handler tersebut terdapat pembuatan cookie yang value-nya diisi data random string. ```go func ActionIndex(w http.ResponseWriter, r *http.Request) { @@ -62,24 +62,21 @@ func ActionIndex(w http.ResponseWriter, r *http.Request) { } ``` -Cookie bisa dikases lewat method `.Cookie()` milik objek `*http.Request`. Method ini mengembalikan 2 informasi: - - - Objek cookie - - Error, jika ada - -Pada kode di atas, ketika `storedCookie` nilainya bukanlah `nil` (berarti cookie dengan nama `cookieName` sudah dibuat), maka objek cookie tersebut disimpan dalam `c`. +Cookie bisa dikases lewat method `.Cookie()` milik objek `*http.Request`. Method ini mengembalikan 2 informasi yaitu objek cookie dan error, jika ada. Pembuatan cookie cukup mudah, tinggal cetak saja objek baru dari struct `http.Cookie`. -Jika `c.Value` adalah kosong, kita asumsikan bahwa cookie belum pernah dibuat (atau expired), maka kita buat cookie baru dengan data adalah random string. +Pada kode di atas, ketika `storedCookie` nilainya bukan `nil` (berarti cookie dengan nama `cookieName` sudah dibuat), maka objek `storedCookie` nilainya disimpan ke variabel `c`. + +Jika `c.Value` bernilai kosong, diasumsikan cookie belum pernah dibuat (atau expired), maka dibuatkanlah cookie baru dengan data adalah random string. > Untuk mempermudah generate random string, kita gunakan library bernama [Gubrak v2](https://github.com/novalagung/gubrak). Fungsi `gubrak.RandomString(32)` akan menghasilkan string 32 karakter. -Cookie bisa expired. Lama cookie aktif ditentukan lewat property `Expires`. Pada kode di atas expiration duration kita set selama 5 menit. +Cookie bisa expired. Lama aktifnya cookie ditentukan lewat property `Expires`. Di contoh durasi 5 menit digunakan sebagai expiration duration cookie. Gunakan `http.SetCookie()` untuk menyimpan cookie yang baru dibuat. -OK, selanjutnya buat handler `ActionDelete()`, seperti yang sudah disinggung di atas. Handler ini difungsikan untuk menghapus cookie dengan nama `cookieName`, lalu redirect ke `/` agar cookie baru diciptakan. +Selanjutnya buat handler `ActionDelete()`. Handler ini difungsikan untuk menghapus cookie dengan nama `cookieName`, lalu me-redirect request ke endpoint `/` agar cookie baru diciptakan. ```go func ActionDelete(w http.ResponseWriter, r *http.Request) { @@ -93,11 +90,11 @@ func ActionDelete(w http.ResponseWriter, r *http.Request) { } ``` -Cara menghapus cookie adalah dengan menge-set ulang cookie dengan nama yang sama, dengan isi property `Expires = time.Unix(0, 0)` dan `MaxAge = -1`. Tujuannya agar cookie expired. +Cara menghapus cookie adalah dengan menge-set ulang cookie dengan nama yang sama namun dengan isi property `Expires = time.Unix(0, 0)` dan `MaxAge = -1`. ## B.21.2. Testing -Jalankan aplikasi, lalu akses `/`. Sebuah random string akan muncul di layar, dan jika kita cek pada bagian response header, informasi cookie nya juga tampil. +Jalankan aplikasi, lalu akses endpoint `/`. Di halaman website akan muncul sebuah random string, dan jika kita cek pada bagian response header, informasi cookie-nya juga tampil. ![Cookie](images/B_cookie_1_cookie.png) @@ -126,7 +123,7 @@ Objek cookie memiliki beberapa property, beberapa di antaranya: --- diff --git a/content/B-custom-mux-multiplexer.md b/content/B-custom-mux-multiplexer.md index 1482d015e..88c9f6b04 100644 --- a/content/B-custom-mux-multiplexer.md +++ b/content/B-custom-mux-multiplexer.md @@ -1,16 +1,16 @@ # B.20. Custom Multiplexer -Pada chapter ini, kita akan belajar membuat custom multiplexer sendiri, lalu memanfaatkannya untuk mempermudah manajemen middleware. +Pada chapter ini, kita akan belajar cara membuat custom multiplexer sendiri, lalu memanfaatkannya untuk keperluan manajemen middleware. -Silakan salin project sebelumnya, chapter [B.19. Middleware http.Handler](/B-middleware-using-http-handler.html), ke folder baru untuk keperluan pembelajaran. +Silakan salin project sebelumnya (chapter [B.19. Middleware http.Handler](/B-middleware-using-http-handler.html)) ke folder baru untuk keperluan pembelajaran. ## B.20.1. Pembuatan Custom Mux -Pada chapter sebelumnya, default mux milik Go digunakan untuk routing dan implementasi middleware. Kali ini default mux tersebut tidak digunakan, kita akan buat mux baru. +Pada chapter sebelumnya, default mux milik Go digunakan untuk routing dan implementasi middleware. Kali ini default mux tersebut tidak digunakan karena mux baru akan dibuat. -Namun pembuatan mux baru tidaklah cukup, karena *naturally* mux baru tersebut tidak akan ada beda dengan default mux. Oleh karena itu agar lebih berguna, kita akan buat tipe mux baru, meng-embed `http.ServeMux` ke dalamnya, lalu membuat beberapa hal dalam struct tersebut. +Sebenarnya, pembuatan mux baru tidaklah cukup, karena mux baru tidak memiliki perbedaan signifikan dibanding default mux. Agar mux baru menjadi lebih berguna, mux baru tersebut perlu meng-embed `http.ServeMux` dan kita juga perlu mempersiapkan beberapa method. -OK, langsung saja kita praktekan. Ubah isi fungsi main menjadi seperti berikut. +OK, mari kita praktekan. Ubah isi fungsi `main()` menjadi seperti berikut. ```go mux := new(CustomMux) @@ -28,11 +28,11 @@ fmt.Println("server started at localhost:9000") server.ListenAndServe() ``` -Objek `mux` dicetak dari struct `CustomMux` yang jelasnya akan di buat. Struct ini di dalamnya meng-embed `http.ServeMux`. +Objek `mux` dicetak dari struct `CustomMux` yang mana nantinya struct ini dibuat dengan meng-embed `http.ServeMux`. -Registrasi middleware juga diubah, sekarang menggunakan method `.RegisterMiddleware()` milik mux. +Registrasi middleware juga perlu diubah, sekarang menggunakan method `.RegisterMiddleware()` milik `CustomMux`. -Pada file `middleware.go`, siapkan struct `CustomMux`. Selain meng-embed objek mux milik Go, siapkan juga satu variabel bertipe slice-dari-tipe-fungsi-middleware. +Selanjutnya, di file `middleware.go` siapkan struct `CustomMux`. Selain meng-embed objek mux milik Go, siapkan juga satu variabel bertipe `[]func(next http.Handler) http.Handler`. ```go type CustomMux struct { @@ -49,7 +49,7 @@ func (c *CustomMux) RegisterMiddleware(next func(next http.Handler) http.Handler } ``` -Lalu buat method `ServeHTTP`. Method ini diperlukan dalam custom mux agar memenuhi kriteria interface `http.Handler`. +Lalu buat method `ServeHTTP()`. Method ini diperlukan agar custom mux memenuhi kriteria interface `http.Handler`. ```go func (c *CustomMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -63,9 +63,11 @@ func (c *CustomMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { } ``` -Method `ServeHTTP()` milik mux adalah method yang pasti dipanggil pada web server, di setiap request yang masuk. +Method `ServeHTTP()` milik mux dipanggil setiap kali ada HTTP request. Dengan perubahan di atas, maka setiap kali ada request masuk pasti akan melewati middleware-middleware terlebih dahulu secara berurutan. -Dengan perubahan di atas, setiap kali ada request masuk pasti akan melewati middleware-middleware terlebih dahulu secara berurutan. Jika lolos middleware ke-1, lanjut ke-2; jika lolos middleware ke-2, lanjut ke-3; dan seterusnya. +- Jika lolos middleware ke-1, lanjut ke-2 +- Jika lolos middleware ke-2, lanjut ke-3 +- ... dan seterusnya ## B.20.2. Testing @@ -73,16 +75,18 @@ Jalankan aplikasi. ![Run the server](images/B_http_basic_auth_2_run_server.png) -Lalu test menggunakan `curl`, hasilnya adalah sama dengan pada chapter sebelumnya. +Lalu test menggunakan `curl`, hasilnya pasti sama dengan pada chapter sebelumnya. ![Consume API](images/B_http_basic_auth_3_test_api.png) -Jika ada keperluan untuk menambahkan middleware baru lainnya, cukup registrasikan lewat `.RegisterMiddleware()`. Source code menjadi lebih rapi dan nyaman untuk dilihat. +Jika ada keperluan untuk menambahkan middleware baru lainnya, cukup registrasikan lewat `.RegisterMiddleware()`. Pengaplikasian teknik custom mux ini membuat manajemen middleware menjadi lebih mudah. + +> Fun fact: semua *3rd party* router di Go (seperti Gin, Chi, Gorilla Mux, dan lainnya) menerapkan teknik custom multiplexer --- diff --git a/content/B-download-file.md b/content/B-download-file.md index 5cca97083..ed350ad12 100644 --- a/content/B-download-file.md +++ b/content/B-download-file.md @@ -1,10 +1,10 @@ # B.17. Download File -Setelah sebelumnya belajar cara untuk handle upload file, kali ini kita akan belajar bagaimana cara membuat handler yang hasilnya adalah download file. +Sebelumnya kita telah belajar cara untuk handle upload file, kali ini kita akan belajar bagaimana cara membuat HTTP handler yang menghasilkan response berbentuk download file. -Sebenarnya download file bisa dengan mudah di-implementasikan menggunakan teknik routing static file, langsung akses url dari public assets di browser. Namun outcome dari teknik ini sangat tergantung pada browser. Tiap browser memiliki behaviour berbeda, ada yang file tidak di-download melainkan dibuka di tab, ada yang ter-download. +Sebenarnya download file bisa dengan mudah di-implementasikan menggunakan teknik routing static file, lalu langsung mengakses url assets di browser. Namun outcome dari teknik ini sangat tergantung default konfigurasi browser. Tiap browser memiliki *behaviour* berbeda, ada yang akan merespon dengan membuka file di tab, ada juga yang merespon dengan men-download file tersebut. -Dengan menggunakan teknik berikut, file pasti akan ter-download. +Dengan penerapan teknik yang dibahas pada chapter ini, file bisa dipastikan di-download oleh browser sewaktu diakses. ## B.17.1. Struktur Folder Proyek @@ -12,7 +12,7 @@ OK, pertama siapkan terlebih dahulu proyek dengan struktur seperti gambar beriku ![Project structure](images/B_download_file_1_structure.png) -File yang berada di folder `files` adalah dummy, jadi anda bisa gunakan file apapun dengan jumlah berapapun untuk keperluan belajar. +File yang berada di folder `files` adalah dummy file. Silakan gunakan file apapun dengan jumlah berapapun untuk keperluan praktek ini. ## B.17.2. Front End @@ -35,9 +35,9 @@ Pertama siapkan dahulu template nya, isi file `view.html` dengan kode berikut. ``` -Tag `
` pada `view.html`. @@ -135,7 +135,7 @@ Actions `range` digunakan untuk melakukan perulangan pada template view. Keyword ``` -Penulisannya cukup unik, keyword `range` dituliskan terlebih dahulu, diikuti variabel penampung index dan elemen. Jika yang dibutuhkan hanya elemen saja, bisa cukup gunakan `{{range $elem := .Hobbies}}`. Semua kode setelah baris deklarasi hingga penutup `{{end}}`, akan diulang sesuai jumlah elemen/item-nya. +Penulisannya cukup unik, keyword `range` dituliskan terlebih dahulu, diikuti variabel penampung index dan elemen. Jika yang dibutuhkan hanya elemen saja, maka gunakan `{{range $elem := .Hobbies}}`. Semua kode setelah baris deklarasi hingga penutup `{{end}}`, akan diulang sesuai jumlah elemen/item-nya. ![Perulangan](images/B_template_actions_variables_3_loop.png) @@ -175,7 +175,7 @@ Lalu bagaimana cara pengaksesan method yang membutuhkan parameter, jika tanda ku ## B.6.6. Penggunaan Keyword `with` Untuk Mengganti Scope Variabel Pada Suatu Blok -Secara default **current scope** di template view adalah data yang dilempar back end. Scope current objek bisa diganti dengan menggunakan keyword `with`, sehingga nantinya untuk mengakses sub-property variabel objek (seperti `.Info.Affiliation`), bisa tidak dilakukan dari objek terluar. +Default-nya, **current scope** di template view adalah data yang dilempar back end. Scope current objek bisa diganti dengan menggunakan keyword `with`, sehingga nantinya untuk mengakses sub-property variabel objek (seperti `.Info.Affiliation`), bisa tidak dilakukan dari objek terluar. > Current scope yg dimaksud di sini adalah seperti object `this` ibarat bahasa pemrograman lain. @@ -247,7 +247,7 @@ Untuk seleksi kondisi yang kondisinya adalah bersumber dari variabel bertipe `bo --- diff --git a/content/B-template-custom-functions.md b/content/B-template-custom-functions.md index 415c82267..cf7281ef4 100644 --- a/content/B-template-custom-functions.md +++ b/content/B-template-custom-functions.md @@ -1,6 +1,6 @@ # B.8. Template: Custom Functions -Pada chapter sebelumnya kita telah mengenal beberapa *predefined* function yang disediakan oleh Go. Kali ini kita akan belajar tentang fungsi custom, bagaimana cara membuat dan menggunakannya dalam template. +Pada chapter sebelumnya kita telah berkenalan dengan beberapa *predefined* function yang disediakan oleh Go. Kali ini kita akan belajar tentang fungsi custom, bagaimana cara pembuatan dan penggunaannya dalam template. ## B.8.1. Front End @@ -20,14 +20,14 @@ Pertama, siapkan project baru. Buat file template `view.html`, lalu isi dengan k ``` -Ada 2 hal yang perlu diperhatikan dari kode di atas. Pertama, terdapat dua buah fungsi yang dipanggil beberapa kali. +Ada 2 hal yang perlu diperhatikan dari kode di atas. Terdapat dua buah fungsi yang dipanggil beberapa kali. 1. Fungsi `unescape()`, digunakan untuk menampilkan string tanpa di-escape 2. Fungsi `avg()`, digunakan untuk mencari rata-rata dari angka-angka yang disisipkan sebagai parameter Kedua fungsi tersebut adalah fungsi kustom yang akan kita buat. -Hal ke-2, terdapat 1 baris statement yang penulisannya agak unik, yaitu `{{"" | unescape}}`. Statement tersebut maknanya adalah string `""` digunakan sebagai parameter dalam pemanggilan fungsi `unescape`. Tanda pipe atau `|` adalah penanda bahwa parameter dituliskan terlebih dahulu sebelum nama fungsi nya. +Di contoh terdapat 1 baris statement yang penulisannya agak unik, yaitu `{{"" | unescape}}`. Statement tersebut maknanya adalah string `""` digunakan sebagai parameter dalam pemanggilan fungsi `unescape`. Tanda pipe atau `|` adalah penanda bahwa parameter dituliskan terlebih dahulu sebelum nama fungsi-nya. ## B.8.2. Back End @@ -62,7 +62,7 @@ var funcMap = template.FuncMap{ Dalam `funcMap` di atas, dua buah fungsi disiapkan, `unescape()` dan `avg()`. Nantinya fungsi ini kita gunakan di view. -Setelah itu, siapkan fungsi `main()` dengan isi route handler untuk `/`. Di dalam handler ini, `view.html` diparsing, kemudian disisipkan fungsi yang telah dibuat di atas ke dalamnya. +Setelah itu, siapkan fungsi `main()` dengan isi route handler untuk `/`. Di dalam handler ini, `view.html` diparsing, kemudian fungsi yang telah dibuat di atas disisipkan ke dalam view. ```go func main() { @@ -86,7 +86,7 @@ Berikut merupakan penjelasan step-by-step mengenai kode panjang untuk parsing da 2. Fungsi custom yang telah kita buat, diregistrasikan agar dikenali oleh template tersebut. Bisa dilihat pada pemanggilan method `Funcs()`. 3. Setelah itu, lewat method `ParseFiles()`, view `view.html` di-parsing. Akan dicari dalam file tersebut apakah ada template yang didefinisikan dengan nama `view.html`. Karena di dalam template view tidak ada deklarasi template sama sekali (\{\{template "namatemplate"\}\}), maka akan dicari view yang namanya adalah `view.html`. Keseluruhan isi `view.html` akan dianggap sebagai sebuah template dengan nama template adalah nama file itu sendiri. -## B.8.3. Test +## B.8.3. Testing Tes hasilnya lewat browser. @@ -96,9 +96,9 @@ Tes hasilnya lewat browser. Pada kode di atas, pemanggilan `template.New()` menghasilkan objek bertipe `*template.Template`. -Pada chapter [B.5. Template: Render Partial HTML Template](/B-template-render-partial-html.html) kita telah belajar mengenai fungsi `template.ParseFiles()`, yang fungsi tersebut juga mengembalikan objek bertipe `*template.Template`. +Pada chapter [B.5. Template: Render Partial HTML Template](/B-template-render-partial-html.html) kita telah belajar mengenai fungsi `template.ParseFiles()` yang fungsi tersebut juga mengembalikan objek bertipe `*template.Template`. -Pada kode di atas, method `ParseFiles()` yang dipanggil bukanlah fungsi `template.ParseFiles()` yang kita telah pelajari sebelumnya. Meskipun namanya sama, kedua fungsi/method ini berbeda. +Di contoh di chapter ini, method `ParseFiles()` yang dipanggil bukanlah fungsi `template.ParseFiles()` yang kita telah pelajari sebelumnya. Meskipun namanya sama, kedua fungsi/method ini berbeda. - Fungsi `template.ParseFiles()`, adalah milik package `template`. Fungsi ini digunakan untuk mem-parsing semua view yang disisipkan sebagai parameter. - Method `ParseFiles()`, milik `*template.Template`, digunakan untuk memparsing semua view yang disisipkan sebagai parameter, lalu diambil hanya bagian yang nama template-nya adalah sama dengan nama template yang sudah di-alokasikan menggunakan `template.New()`. Jika template yang dicari tidak ada, maka akan mencari yang nama file-nya sama dengan nama template yang sudah ter-alokasi. @@ -108,7 +108,7 @@ Chapter selanjutnya akan membahas lebih detail mengenai penggunaan method `Parse --- diff --git a/content/B-template-functions.md b/content/B-template-functions.md index 86b59b70d..342a7b721 100644 --- a/content/B-template-functions.md +++ b/content/B-template-functions.md @@ -1,6 +1,8 @@ # B.7. Template: Functions -Go menyediakan beberapa *predefiend* function yang bisa digunakan dalam file template. Pada chapter ini kita akan membahas beberapa di antaranya beserta cara penggunaannya. Cara pemanggilan fungsi atau method sebuah objek pada file template sedikit berbeda dibanding seperti pada chapter sebelumnya. +Go menyediakan beberapa *predefiend* function yang bisa digunakan langsung dalam file template. Pada chapter ini kita akan membahas beberapa di antaranya beserta cara penggunaannya. + +Cara pemanggilan fungsi atau method sebuah objek pada file template sedikit berbeda dibanding dengan yang telah dicontohkan pada chapter sebelumnya. ## B.7.1. Persiapan @@ -160,7 +162,7 @@ Go juga menyediakan beberapa fungsi string yang bisa dimanfaatkan, yaitu: - `printf` (merupakan alias dari `fmt.Sprintf`) - `println` (merupakan alias dari `fmt.Sprintln`) -Cara penggunannya juga masih sama. +Cara penggunannya juga masih sama, contoh: ```html

@@ -231,7 +233,7 @@ Output program: ---

diff --git a/content/B-template-render-html.md b/content/B-template-render-html.md index ad1b757a2..31f3ec9d4 100644 --- a/content/B-template-render-html.md +++ b/content/B-template-render-html.md @@ -1,8 +1,8 @@ # B.4. Template: Render HTML Template -Pada bagian ini kita akan belajar bagaimana cara render file **template** ber-tipe **HTML**, untuk ditampilkan pada browser. +Pada bagian ini kita akan belajar bagaimana cara render file **template** yang berisi **HTML** untuk ditampilkan ke layar browser. -Terdapat banyak jenis template pada Go, yang akan kita pakai adalah template HTML. Package `html/template` berisi banyak sekali fungsi untuk kebutuhan rendering dan parsing file template jenis ini. +Terdapat banyak jenis template pada Go, di sini yang akan kita pakai adalah template HTML. Package `html/template` berisi banyak sekali fungsi untuk operasi rendering dan parsing file template HTML. ## B.4.1. Struktur Aplikasi @@ -57,15 +57,15 @@ if err != nil { Package `path` berisikan banyak fungsi yang berhubungan dengan lokasi folder atau path, yang salah satu di antaranya adalah fungsi `path.Join()`. Fungsi ini digunakan untuk menggabungkan folder atau file atau keduanya menjadi sebuah path, dengan separator relatif terhadap OS yang digunakan. -> Separator yang digunakan oleh `path.Join()` adalah `\` untuk wind\*ws dan `/` untuk un\*x. +> Separator yang digunakan oleh `path.Join()` adalah `\` untuk windows dan `/` untuk linux/unix/macos. Contoh penerapan `path.Join()` bisa dilihat di kode di atas, `views` di-join dengan `index.html`, menghasilkan `views/index.html`. Sedangkan `template.ParseFiles()`, digunakan untuk parsing file template, dalam contoh ini file `view/index.html`. Fungsi ini mengembalikan 2 data, yaitu hasil dari proses parsing yang bertipe `*template.Template`, dan informasi `error` jika ada. -Fungsi `http.Error()` digunakan untuk menandai response (`http.ResponseWriter`) bahwa terjadi error, dengan kode error dan pesan error bisa ditentukan. Pada contoh di atas yang digunakan adalah **500 - internal server error** yang direpresentasikan oleh variabel `http.StatusInternalServerError`. +Fungsi `http.Error()` digunakan untuk menandai HTTP request dengan response berupa error dengan kode serta pesan error bisa kita tentukan sendiri. Pada contoh di atas yang digunakan adalah **500 - internal server error**, direpresentasikan oleh variabel `http.StatusInternalServerError`. -Method `Execute()` milik `*template.Template`, digunakan untuk menyisipkan data pada template, untuk kemudian ditampilkan ke browser. Data bisa disipkan dalam bentuk `struct`, `map`, atau `interface{}`. +Method `Execute()` milik `*template.Template`, digunakan untuk menyisipkan data pada template, kemudian menampilkannya ke browser. Data bisa disipkan ke view dalam bentuk `struct`, `map`, atau `interface{}`. - Jika dituliskan dalam bentuk `map`, maka **key** akan menjadi nama variabel dan **value** menjadi nilainya - Jika dituliskan dalam bentuk variabel objek cetakan `struct`, nama **property** akan menjadi nama variabel @@ -94,13 +94,13 @@ Tanda titik "\." pada \{\{\.namaVariabel\}\} menerangkan bahwa variabel tersebut ## B.4.4. Testing -Semua sudah siap, maka jalankan program lalu lakukan testing via browser. +Semua sudah siap, sekarang jalankan program, lalu lakukan testing di browser. ![Output HTML](images/B_template_render_html_2_output.png) ## B.4.5. Static File CSS -Kita akan coba tambahkan sebuah stylesheet di sini. Langsung saja, buat file statis `assets/site.css`, isi dengan kode berikut. +Coba tambahkan sebuah stylesheet di sini. Buat file `assets/site.css`, isi dengan kode berikut. ```css body { @@ -139,7 +139,7 @@ Jalankan aplikasi untuk test hasil. --- diff --git a/content/B-template-render-partial-html.md b/content/B-template-render-partial-html.md index c86a4de30..160292781 100644 --- a/content/B-template-render-partial-html.md +++ b/content/B-template-render-partial-html.md @@ -1,15 +1,15 @@ # B.5. Template: Render Partial HTML Template -Satu buah halaman yang berisikan html, bisa terbentuk dari banyak template html (parsial). Pada chapter ini kita akan belajar bagaimana membuat, mem-parsing, dan me-render semua file tersebut. +Satu buah halaman yang berisikan html bisa saja terbentuk dari lebih dari satu proses parsing template html (parsial). Pada chapter ini kita akan belajar bagaimana membuat, mem-parsing, dan me-render semua template file. -Ada beberapa metode yang bisa digunakan, dari ke semuanya akan kita bahas 2 di antaranya, yaitu: +Ada beberapa metode yang bisa digunakan, 2 di antaranya: - Menggunakan fungsi `template.ParseGlob()`. - Menggunakan fungsi `template.ParseFiles()`. ## B.5.1. Struktur Aplikasi -Mari langsung kita praktekan. Buat project baru, siapkan file dan folder dengan susunan seperti dengan gambar berikut. +Mari belajar sambil praktek seperti biasa. Buat project baru, siapkan file dan folder dengan susunan seperti dengan gambar berikut. ![Structure](images/B_template_render_partial_html_1_structure.png) @@ -39,11 +39,11 @@ func main() { Tipe `M` merupakan alias dari `map[string]interface{}`, disiapkan untuk mempersingkat penulisan tipe map tersebut. Pada pembahasan-pembahasan selanjutnya kita akan banyak menggunakan tipe ini. -Pada kode di atas, di dalam fungsi `main()`, fungsi `template.ParseGlob()` dipanggil, dengan parameter adalah pattern path `"views/*"`. Fungsi ini digunakan untuk memparsing semua file yang match dengan pattern yang ditentukan, dan fungsi ini mengembalikan 2 objek: `*template.Template` & `error`. +Pada kode di atas, di dalam fungsi `main()`, fungsi `template.ParseGlob()` dipanggil, dengan parameter adalah pattern path `"views/*"`. Fungsi ini digunakan untuk memparsing semua file yang match dengan *pattern*/pola yang ditentukan. Fungsi ini mengembalikan 2 objek yaitu `*template.Template` & `error`. > Pattern path pada fungsi `template.ParseGlob()` nantinya akan di proses oleh `filepath.Glob()` -Proses parsing semua file html dalam folder `views` dilakukan di awal, agar ketika mengakses rute-tertentu-yang-menampilkan-html, tidak terjadi proses parsing lagi. +Proses parsing semua file html dalam folder `views` dilakukan di awal, agar ketika suatu endpoint diakses nantinya tidak terjadi proses parsing melainkan hanya proses rendering saja. > Parsing semua file menggunakan `template.ParseGlob()` yang dilakukan di luar handler, tidak direkomendasikan dalam fase development. Karena akan mempersulit testing html. Lebih detailnya akan dibahas di bagian bawah. @@ -164,7 +164,7 @@ Bisa dilihat pada gambar di atas, ketika rute `/index` dan `/about` di akses, ko ## B.5.6. Parsing Banyak File HTML Menggunakan `template.ParseFiles()` -Metode parsing menggunakan `template.ParseGlob()` memiliki kekurangan yaitu sangat tergantung terhadap pattern path yang digunakan. Jika dalam suatu proyek terdapat sangat banyak file html dan folder, sedangkan hanya beberapa yang digunakan, pemilihan pattern path yang kurang tepat akan menjadikan file lain ikut ter-parsing dengan sia-sia. +Metode parsing menggunakan `template.ParseGlob()` memiliki kekurangan yaitu sangat tergantung terhadap pattern path yang digunakan. Jika dalam suatu proyek terdapat sangat banyak file html dan folder, sedangkan hanya beberapa yang digunakan, pemilihan pattern path yang kurang tepat akan menjadikan file lain ikut ter-parsing sia-sia. Dan juga, karena statement `template.ParseGlob()` dieksekusi diluar handler, maka ketika ada perubahan pada salah satu view, lalu halaman di refresh, output yang dihasilkan akan tetap sama. Solusi dari masalah ini adalah dengan memanggil `template.ParseGlob()` di tiap handler rute-rute yang diregistrasikan. @@ -210,7 +210,7 @@ Mari kita praktekan. Ubah handler rute `/index` dan `/about`. Gunakan `template. }) ``` - - **Hapus** statement `template.ParseGlob()`. + - Tak lupa **hapus** statement `template.ParseGlob()`. ```go var tmpl, err = template.ParseGlob("views/*") @@ -229,7 +229,7 @@ Jalankan aplikasi untuk test hasilnya. --- diff --git a/content/C-advanced-configuration-viper.md b/content/C-advanced-configuration-viper.md index 8ce1fbd12..2d949af8f 100644 --- a/content/C-advanced-configuration-viper.md +++ b/content/C-advanced-configuration-viper.md @@ -150,7 +150,7 @@ Penggunaan fasilitas watcher memerlukan tambahan 3rd party library [fsnotify](ht --- diff --git a/content/C-advanced-middleware-and-logging.md b/content/C-advanced-middleware-and-logging.md index 529642b1f..3ec99b970 100644 --- a/content/C-advanced-middleware-and-logging.md +++ b/content/C-advanced-middleware-and-logging.md @@ -271,7 +271,7 @@ Satu kata, *cantik*. --- diff --git a/content/C-best-practice-configuration-env-var.md b/content/C-best-practice-configuration-env-var.md index 97aa1dd91..03f9a5188 100644 --- a/content/C-best-practice-configuration-env-var.md +++ b/content/C-best-practice-configuration-env-var.md @@ -157,7 +157,7 @@ Mungkin dari sini pembaca bisa lanjut ke chapter [C.35. Dockerize Aplikasi Golan --- diff --git a/content/C-client-http-request-advanced.md b/content/C-client-http-request-advanced.md index 78119776c..044f106d4 100644 --- a/content/C-client-http-request-advanced.md +++ b/content/C-client-http-request-advanced.md @@ -189,7 +189,7 @@ Jalankan aplikasi server, buka prompt terminal/CMD baru, lalu jalankan aplikasi --- diff --git a/content/C-convert-html-to-pdf.md b/content/C-convert-html-to-pdf.md index 590c3528e..ec4dce079 100644 --- a/content/C-convert-html-to-pdf.md +++ b/content/C-convert-html-to-pdf.md @@ -172,7 +172,7 @@ Cara ini cocok digunakan untuk konversi data HTML yang isinya muncul pada saat p --- diff --git a/content/C-cors-preflight-request.md b/content/C-cors-preflight-request.md index b1680b359..72415362c 100644 --- a/content/C-cors-preflight-request.md +++ b/content/C-cors-preflight-request.md @@ -292,7 +292,7 @@ Berikut adalah list konfigurasi yang bisa dimanfaatkan dari library ini. --- diff --git a/content/C-csrf.md b/content/C-csrf.md index 7938ef433..5bdd82fb0 100644 --- a/content/C-csrf.md +++ b/content/C-csrf.md @@ -220,7 +220,7 @@ Lewat teknik pencegahan ini, bukan berarti serangan CSRF tidak bisa dilakukan, s --- diff --git a/content/C-dockerize-golang.md b/content/C-dockerize-golang.md index b6b41990e..f75db6512 100644 --- a/content/C-dockerize-golang.md +++ b/content/C-dockerize-golang.md @@ -302,11 +302,11 @@ docker container run --name my-container-hello-world --rm -it -e PORT=8080 -e IN #### ◉ 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 pembaca tidak pakai `--name` sangat dianjurkan paka `--rm` dalam penerapan `docker run`. +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 mana nama container harus unik. Jadi kalau ada duplikasi pasti langsung error. Nah dari sini berarti kalau pembaca tidak pakai `--name` sangat dianjurkan paka `--rm` dalam penerapan `docker run`. #### ◉ 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. +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 mana jika kita terminate atau kill command menggunakan `CTRL + C` atau `CMD + C` (untuk mac), maka otomatis container akan di stop. Nah dengan menggabungkan flag `--rm` dan flag `-it` kita bisa dengan mudah stop kemudian hapus container. @@ -320,7 +320,7 @@ docker container stop my-container-hello-world --- diff --git a/content/C-echo-routing.md b/content/C-echo-routing.md index 9ea7e7a1d..8c7d3217f 100644 --- a/content/C-echo-routing.md +++ b/content/C-echo-routing.md @@ -279,7 +279,7 @@ Jalankan aplikasi, lalu coba akses `http://localhost:9000/static/layout.js`. --- diff --git a/content/C-echo-template-rendering.md b/content/C-echo-template-rendering.md index 5868aec4f..2603b1815 100644 --- a/content/C-echo-template-rendering.md +++ b/content/C-echo-template-rendering.md @@ -4,7 +4,7 @@ Pada chapter ini kita akan belajar cara render template html pada aplikasi yang Pada dasarnya proses parsing dan rendering template tidak di-handle oleh echo sendiri, melainkan oleh API dari package `html/template`. Jadi bisa dibilang cara render template di echo adalah sama seperti pada aplikasi yang murni menggunakan golang biasa, seperti yang sudah dibahas pada chapter [Template: Render HTML Template](/B-template-render-html.html), [Template: Render Partial HTML Template](/B-template-render-partial-html.html), [Template: Render Specific HTML Template](/B-render-specific-html.html), dan [Template: Render HTML String](/B-render-html-string.html). -Echo menyediakan satu fasilitas yang bisa kita manfaatkan untuk standarisasi rendering template. Cara penggunaannya, dengan meng-override default `.Renderer` property milik echo menggunakan objek cetakan struct, yang di mana pada struct tersebut harus ada method bernama `.Render()` dengan skema sesuai dengan kebutuhan echo. Nah, di dalam method `.Render()` inilah kode untuk parsing dan rendering template ditulis. +Echo menyediakan satu fasilitas yang bisa kita manfaatkan untuk standarisasi rendering template. Cara penggunaannya, dengan meng-override default `.Renderer` property milik echo menggunakan objek cetakan struct, yang mana pada struct tersebut harus ada method bernama `.Render()` dengan skema sesuai dengan kebutuhan echo. Nah, di dalam method `.Render()` inilah kode untuk parsing dan rendering template ditulis. ## C.7.1. Praktek @@ -137,7 +137,7 @@ Proses parsing dan rendering tidak di-handle oleh echo, melainkan menggunakan AP --- diff --git a/content/C-flag-parser.md b/content/C-flag-parser.md index bf3ef968e..b547daec0 100644 --- a/content/C-flag-parser.md +++ b/content/C-flag-parser.md @@ -336,7 +336,7 @@ Cobra merupakan library yang dirancang khusus untuk development aplikasi berbasi --- diff --git a/content/C-golang-aws-s3.md b/content/C-golang-aws-s3.md index b731b568c..00da51527 100644 --- a/content/C-golang-aws-s3.md +++ b/content/C-golang-aws-s3.md @@ -437,7 +437,7 @@ Hasilnya: --- diff --git a/content/C-golang-ftp.md b/content/C-golang-ftp.md index db0a484eb..0f0fc05a4 100644 --- a/content/C-golang-ftp.md +++ b/content/C-golang-ftp.md @@ -293,7 +293,7 @@ Jalankan aplikasi, cek hasilnya. Untuk memvalidasi bahwa file di client dan di s --- diff --git a/content/C-golang-grpc-protobuf.md b/content/C-golang-grpc-protobuf.md index f202307db..6b2e0336c 100644 --- a/content/C-golang-grpc-protobuf.md +++ b/content/C-golang-grpc-protobuf.md @@ -541,7 +541,7 @@ OK, jika anda membaca sampai baris ini, berarti anda telah berhasil sabar dalam --- diff --git a/content/C-golang-jwt.md b/content/C-golang-jwt.md index 8e73621c4..8bc111aff 100644 --- a/content/C-golang-jwt.md +++ b/content/C-golang-jwt.md @@ -407,7 +407,7 @@ Semua berjalan sesuai harapan. Agar lebih meyakinkan, coba lakukan beberapa test --- diff --git a/content/C-golang-ldap-authentication.md b/content/C-golang-ldap-authentication.md index 17fdb17a3..2b454fe3e 100644 --- a/content/C-golang-ldap-authentication.md +++ b/content/C-golang-ldap-authentication.md @@ -317,7 +317,7 @@ if err != nil { --- diff --git a/content/C-golang-protobuf-implementation.md b/content/C-golang-protobuf-implementation.md index 9f363cd4f..c76cb12a8 100644 --- a/content/C-golang-protobuf-implementation.md +++ b/content/C-golang-protobuf-implementation.md @@ -390,7 +390,7 @@ Pada chapter selanjutnya kita akan belajar tentang penerapan gRPC dan protobuf. --- diff --git a/content/C-golang-redis.md b/content/C-golang-redis.md index 1d0e01715..76d0a2a7e 100644 --- a/content/C-golang-redis.md +++ b/content/C-golang-redis.md @@ -163,7 +163,7 @@ Error, ini karena data yang disimpan hanya di retain sesuai `ttl` yaitu 3 detik, --- diff --git a/content/C-golang-ssh-sftp.md b/content/C-golang-ssh-sftp.md index a816f87fc..bdab2d295 100644 --- a/content/C-golang-ssh-sftp.md +++ b/content/C-golang-ssh-sftp.md @@ -264,7 +264,7 @@ Jalankan aplikasi untuk melihat hasilnya. --- diff --git a/content/C-golang-sso-saml-sp.md b/content/C-golang-sso-saml-sp.md index 45db97f02..583afbd6b 100644 --- a/content/C-golang-sso-saml-sp.md +++ b/content/C-golang-sso-saml-sp.md @@ -272,7 +272,7 @@ http.ListenAndServe(portString, nil) go run *.go ``` -Oops, muncul error pada saat mengakses `http://localhost:9000/index`. Meski url ini merupakan protected url, yang di mana hanya bisa diakses ketika sudah login, harusnya user akan di-redirect ke halaman login, bukan malah memunculkan error. +Oops, muncul error pada saat mengakses `http://localhost:9000/index`. Meski url ini merupakan protected url, yang mana hanya bisa diakses ketika sudah login, harusnya user akan di-redirect ke halaman login, bukan malah memunculkan error. ![Metadata share](images/C_golang_sso_saml_sp_1_saml_metadata_missing.png) @@ -311,7 +311,7 @@ Salah satu benefit metode inisialisasi ini: ketika ada banyak aplikasi SP (misal --- diff --git a/content/C-http-error-handling.md b/content/C-http-error-handling.md index c45a2cba6..4fed93576 100644 --- a/content/C-http-error-handling.md +++ b/content/C-http-error-handling.md @@ -105,7 +105,7 @@ Silakan ubah kode `fmt.Sprintf("%d.html", report.Code)` sesuai format nama halam --- diff --git a/content/C-http-gzip-compression.md b/content/C-http-gzip-compression.md index 70cb27dab..bfacf39fb 100644 --- a/content/C-http-gzip-compression.md +++ b/content/C-http-gzip-compression.md @@ -141,7 +141,7 @@ e.Logger.Fatal(e.Start(":9000")) --- diff --git a/content/C-http-request-payload-validation.md b/content/C-http-request-payload-validation.md index e05577959..52405d187 100644 --- a/content/C-http-request-payload-validation.md +++ b/content/C-http-request-payload-validation.md @@ -109,7 +109,7 @@ Nah, pada chapter selanjutnya kita akan belajar cara membuat custom error handle --- diff --git a/content/C-http2-server-push.md b/content/C-http2-server-push.md index 042489085..723845978 100644 --- a/content/C-http2-server-push.md +++ b/content/C-http2-server-push.md @@ -162,7 +162,7 @@ Selain dari kolom protocol, penanda server push bisa dilihat juga lewat grafik * --- diff --git a/content/C-https-tls.md b/content/C-https-tls.md index 70e63931f..e8726619a 100644 --- a/content/C-https-tls.md +++ b/content/C-https-tls.md @@ -126,12 +126,12 @@ Coba test juga menggunakan browser, jika menggunakan chrome maka akan muncul war ![browser example](images/C_https_tls_5_browser_example.png) -Warning `NET::ERR_CERT_AUTHORITY_INVALID` muncul ketika mengakses sebuah website menggunakan protokol `https` yang di mana website ini mengaplikasikan **self-signed certificate**, bukan menggunakan certificate yang sudah diverifikasi oleh CA. +Warning `NET::ERR_CERT_AUTHORITY_INVALID` muncul ketika mengakses sebuah website menggunakan protokol `https` yang mana website ini mengaplikasikan **self-signed certificate**, bukan menggunakan certificate yang sudah diverifikasi oleh CA. --- diff --git a/content/C-parsing-http-request-payload-echo.md b/content/C-parsing-http-request-payload-echo.md index c55917c5f..896068830 100644 --- a/content/C-parsing-http-request-payload-echo.md +++ b/content/C-parsing-http-request-payload-echo.md @@ -115,7 +115,7 @@ curl -X GET http://localhost:9000/user?name=Joe&email=nope@novalagung.com --- diff --git a/content/C-read-write-excel-xlsx-file.md b/content/C-read-write-excel-xlsx-file.md index 7bd3311a0..5e729154e 100644 --- a/content/C-read-write-excel-xlsx-file.md +++ b/content/C-read-write-excel-xlsx-file.md @@ -192,7 +192,7 @@ Jalankan aplikasi untuk mengecek hasilnya. --- diff --git a/content/C-scraping-parsing-html.md b/content/C-scraping-parsing-html.md index ec7def3b3..822764d79 100644 --- a/content/C-scraping-parsing-html.md +++ b/content/C-scraping-parsing-html.md @@ -190,7 +190,7 @@ Jalankan aplikasi, lihat hasilnya. --- diff --git a/content/C-secure-insecure-client-http-request.md b/content/C-secure-insecure-client-http-request.md index 8eb6646d8..f180245b1 100644 --- a/content/C-secure-insecure-client-http-request.md +++ b/content/C-secure-insecure-client-http-request.md @@ -185,7 +185,7 @@ Tujuan mengapa penulis tambahkan sub chapter **Konfigurasi SSL/TLS Lanjutan** in --- diff --git a/content/C-secure-middleware.md b/content/C-secure-middleware.md index df4469d40..60ee73267 100644 --- a/content/C-secure-middleware.md +++ b/content/C-secure-middleware.md @@ -149,7 +149,7 @@ Lebih mendetailnya silakan langsung cek halaman official library secure di https --- diff --git a/content/C-securecookie.md b/content/C-securecookie.md index ea3fe2779..319e504c2 100644 --- a/content/C-securecookie.md +++ b/content/C-securecookie.md @@ -153,7 +153,7 @@ http.SetCookie(c.Response(), cookie) --- diff --git a/content/C-send-email.md b/content/C-send-email.md index 14025d460..5d8fe368f 100644 --- a/content/C-send-email.md +++ b/content/C-send-email.md @@ -194,7 +194,7 @@ Daaaaannnnn ... cukup itu saja penyesuaiannya agar bisa kirim email via konfigur --- diff --git a/content/C-session.md b/content/C-session.md index d3963c8f3..069fbc1de 100644 --- a/content/C-session.md +++ b/content/C-session.md @@ -310,7 +310,7 @@ Tujuan dari kode yang kita tulis kurang lebih sebagai berikut. --- diff --git a/content/C-singleflight.md b/content/C-singleflight.md index 33a7c549b..67e41441e 100644 --- a/content/C-singleflight.md +++ b/content/C-singleflight.md @@ -235,7 +235,7 @@ Cukup berguna bukan? Dengan adahnya singleflight API ini, beban backend akan sed --- diff --git a/content/C-write-pdf-file.md b/content/C-write-pdf-file.md index ce7d0529f..043ba38e2 100644 --- a/content/C-write-pdf-file.md +++ b/content/C-write-pdf-file.md @@ -87,7 +87,7 @@ Coba jalankan aplikasi untuk melihat hasilnya. Buka generated file `file.pdf`, i --- diff --git a/content/C-xml-parser.md b/content/C-xml-parser.md index d8f7a2120..ad948e2f7 100644 --- a/content/C-xml-parser.md +++ b/content/C-xml-parser.md @@ -274,7 +274,7 @@ Jalankan aplikasi, lihat hasilnya. --- diff --git a/content/D-golang-web-socket-chatting-app.md b/content/D-golang-web-socket-chatting-app.md index 50286d74d..3b543c6ce 100644 --- a/content/D-golang-web-socket-chatting-app.md +++ b/content/D-golang-web-socket-chatting-app.md @@ -414,7 +414,7 @@ Ketika salah satu user leave, pesan **User XXX: disconnected** akan di-broadcast --- diff --git a/content/D-google-api-search.md b/content/D-google-api-search.md index 30d62ed6c..f519f45ff 100644 --- a/content/D-google-api-search.md +++ b/content/D-google-api-search.md @@ -325,7 +325,7 @@ Error di atas muncul karena, host `localhost` belum didaftarkan pada API console --- 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 b2c46a01c..cb51c12e2 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 @@ -16,7 +16,7 @@ Dengan metode worker pool ini, maka penggunaan memory dan performansi program ak *Connection pool* adalah metode untuk manajemen sejumlah koneksi database, agar bisa digunakan secara optimal. -Connection pool sangat penting dalam kasus operasi data yang berhubungan dengan database yang di mana concurrent programming diterapkan. +Connection pool sangat penting dalam kasus operasi data yang berhubungan dengan database yang mana concurrent programming diterapkan. 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. @@ -342,7 +342,7 @@ Praktek pada chapter ini sifatnya adalah POC, jadi sangat mungkin diperlukan pen --- diff --git a/content/README.md b/content/README.md index 3d385ec2a..e0f28b35a 100644 --- a/content/README.md +++ b/content/README.md @@ -35,7 +35,7 @@ Ebook ini bisa di-download dalam bentuk file via link berikut: Untuk mendapatkan konten buku yang paling update, silakan baca langsung versi web secara online atau download ulang e-book versi terbaru. -## Source Code Praktek +## Source Code Praktik Source code contoh-contoh program bisa diunduh di [github.com/novalagung/dasarpemrogramangolang-example](https://github.com/novalagung/dasarpemrogramangolang-example). Dianjurkan untuk tidak copy-paste dari source code dalam proses belajar, usahakan untuk menulis sendiri kode program agar cepat terbiasa dengan bahasa Go.