author Ahmad Muhardian

Cara Replace String dalam Template Go


Sebuah masalah kecil yang saya temukan ketika mengedit skrip Blogimport. Saya ingin memperbesar ukuran gambar thumbnail dengan cara me-replace string yang ada di URL.

Misalnya, dari URL ini:

https://2.bp.blogspot.com/-DEeRanrBa6s/WGWGwA2qW5I/AAAAAAAADg4/feGUc-g9rXc9B7hXpKr0ecG9UOMXU3_VQCK4B/s72-c/pemrograman%2Bjavascript%2B-%2Bpetanikode.png

Menjadi seperti ini:

https://2.bp.blogspot.com/-DEeRanrBa6s/WGWGwA2qW5I/AAAAAAAADg4/feGUc-g9rXc9B7hXpKr0ecG9UOMXU3_VQCK4B/s1600/pemrograman%2Bjavascript%2B-%2Bpetanikode.png

Nah, di sana saya ingin merubah s72-c agar menjadi s1600. Pada template Hugo, saya bisa melakukannya seperti ini:

{{ replace .Media.ThumbnailUrl 's72-c' 's1600' }}

Namun, Go belum memiliki fungsi replace seperti Hugo. Setelah bolak-balik Google dan Stack Overflow, saya akhirnya membuat sebuah pertanyaan baru di Stack Overflow dan mendapatkan jawabannya.

Pertama, kita harus membuat fungsi replace dulu dengan Go.

func replace(input, from,to string) string {
    return strings.Replace(input,from,to, -1)
}

Kemudian melakukan mapping terhadap fungsi tadi agar nanti bisa dikenali dalam template.

funcMap = template.FuncMap{
     "replace":  replace,
}

Setelah itu, barulah kita masukan fungsinya ketika eksekusi template.

template := template.New("").Funcs(funcMap)

Barulah kita dapat menggunakan fungsi replace pada template go.

{{ replace .Media.ThumbnailUrl 's72-c' 's1600' }}

Contoh kode lengkapnya dapat juga di lihat di sini.

package main

import (
    "encoding/xml"
    "flag"
    "fmt"
    "log"
    "io/ioutil"
    "os"
    "path/filepath"
    "strings"
    "text/template"
    "time"
    "unicode"
)



type Date time.Time

func (d Date) String() string {
    return time.Time(d).Format("2006-01-02T15:04:05Z")
}

func (d *Date) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error {
    var v string
    dec.DecodeElement(&v, &start)
    t, err := time.Parse("2006-01-02T15:04:05.000-07:00", v)
    if err != nil {
        return err
    }
    *d = Date(t)
    return nil
}

type Draft bool

func (d *Draft) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error {
    var v string
    dec.DecodeElement(&v, &start)
    switch v {
    case "yes":
        *d = true
        return nil
    case "no":
        *d = false
        return nil
    }
    return fmt.Errorf("Unknown value for draft boolean: %s", v)
}

type AuthorImage struct{
    Src string `xml:"src,attr"`
}

type Author struct {
    Name string `xml:"name"`
    Uri string `xml:"uri"`
    Image AuthorImage `xml:"image"`
}

type Export struct {
    XMLName xml.Name `xml:"feed"`
    Entries []Entry `xml:"entry"`
}


type Media struct {
    ThumbnailUrl string `xml:"url,attr"`
}

type Entry struct {
    ID string `xml:"id"`
    Published Date `xml:"published"`
    Updated Date `xml:"updated"`
    Draft Draft `xml:"control>draft"`
    Title string `xml:"title"`
    Content string `xml:"content"`
    Tags Tags `xml:"category"`
    Author Author `xml:"author"`
    Media Media `xml:"thumbnail"`
    Extra string
}

type Tag struct {
    Name string `xml:"term,attr"`
    Scheme string `xml:"scheme,attr"`
}

type Tags []Tag

func (t Tags) TomlString() string {
    names := []string{}
    for _, t := range t {
        if t.Scheme == "http://www.blogger.com/atom/ns#" {
            names = append(names, fmt.Sprintf("%q", t.Name))
        }
    }
    return strings.Join(names, ", ")
}

var templ = `+++
title = "{{ .Title }}"
date = {{ .Published }}
updated = {{ .Updated }}{{ with .Tags.TomlString }}
tags = [{{ . }}]{{ end }}{{ if .Draft }}
draft = true{{ end }}
blogimport = true {{ with .Extra }}
{{.}}{{ end }}
[author]
    name = "{{ .Author.Name }}"
    uri = "{{ .Author.Uri }}"
    image = "{{ .Author.Image.Src }}"
[image]
    src = "{{ resizeImage .Media.ThumbnailUrl }}"
    link = ""
    thumblink = "{{ .Media.ThumbnailUrl }}"
    alt = ""
    title = ""
    author = ""
    license = ""
    licenseLink = ""
+++
{{ .Content }}
`

var t = template.Must(template.New("").Funcs(funcMap).Parse(templ))

// maps the the function into template
var funcMap = template.FuncMap{
        "resizeImage": resizeImage,
}

// Resize image of thumbnail to larger size (scale to 1600)
func resizeImage(url string) string {
    return strings.Replace(url, "s72-c", "s1600", -1)
}

func main() {
    log.SetFlags(0)

    extra := flag.String("extra", "", "additional metadata to set in frontmatter")
    flag.Parse()

    args := flag.Args()

    if len(args) != 2 {
        log.Printf("Usage: %s [options] <xmlfile> <targetdir>", os.Args[0])
        log.Println("options:")
        flag.PrintDefaults()
        os.Exit(1)
    }

    dir := args[1]

    info, err := os.Stat(dir)
    if os.IsNotExist(err) {
        err = os.MkdirAll(dir, 0755)
    }
    if err != nil {
        log.Fatal(err)
    }

    if !info.IsDir(){
        log.Fatal("Second argument is not a directory.")
     }


    b, err := ioutil.ReadFile(args[0])
    if err != nil {
        log.Fatal(err)
    }

    exp := Export{}

    err = xml.Unmarshal(b, &exp)
    if err != nil {
        log.Fatal(err)
    }

    if len(exp.Entries) < 1 {
        log.Fatal("No blog entries found!")
    }

    count := 0
    drafts := 0
    for _, entry := range exp.Entries {
        isPost := false
        for _, tag := range entry.Tags {
            if tag.Name == "http://schemas.google.com/blogger/2008/kind#post" &&
                tag.Scheme == "http://schemas.google.com/g/2005#kind" {
                isPost = true
                break
            }
        }
        if !isPost {
            continue
        }
        if extra != nil {
            entry.Extra = *extra
        }
        if err := writeEntry(entry, dir); err != nil {
            log.Fatalf("Failed writing post %q to disk:\n%s", entry.Title, err)
        }
        if entry.Draft {
            drafts++
        } else {
            count++
        }
    }
    log.Printf("Wrote %d published posts to disk.", count)
    log.Printf("Wrote %d drafts to disk.", drafts)
}

var delim = []byte("+++\n")

func writeEntry(e Entry, dir string) error {
    filename := filepath.Join(dir, makePath(e.Title)+".md")
    f, err := os.OpenFile(filename, os.O_CREATE | os.O_TRUNC | os.O_WRONLY, 0644)
    if err != nil {
        return err
    }
    defer f.Close()

    return t.Execute(f, e)
}


// Take a string with any characters and replace it so the string could be used in a path.
// E.g. Social Media -> social-media
func makePath(s string) string {
    return unicodeSanitize(strings.ToLower(strings.Replace(strings.TrimSpace(s), " ", "-", -1)))
}

func unicodeSanitize(s string) string {
    source := []rune(s)
    target := make([]rune, 0, len(source))

    for _, r := range source {
        if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '.' || r == '_' || r == '-' {
            target = append(target, r)
        }
    }

    return string(target)
}

Baca Juga ini

Saya tidak Bisa Bahasa Pemrograman Go, Terpaksa ini yang Saya Lakukan

Saya tidak Bisa Bahasa Pemrograman Go, Terpaksa ini yang Saya Lakukan

Ada cerita menarik yang saya alami ketika migrasi Petani Kode dari Blogger ke Hugo. Waktu itu, ketika saya ingin mengimpor konten dari Blogger ke Hugo, ada sesuatu yang kurang dari skrip Go yang disediakan. Kekuarangnnya tidak mampu membuat atau mengambil gambar (thumbnail) untuk setiap artikel. Saya kemudian berpikir, mungkin nanti saya bisa edit setiap gambar untuk artikel. Namun, rasanya akan sangat melelahkan melakukannya karena jumlah artikelnya sampai ratusan. Tidak ingin melakukan hal yang berulang-ulang seperti kata orang, Don’t Repeat Yourself di singkat DRY.

Cara Menyimpan Graf pada MySQL

Cara Menyimpan Graf pada MySQL

Ini artikel lama, belum di-update. Isinya mungkin tidak relevan dengan kondisi sekarang Awalnya saya tidak begitu paham dengan kegunaan graf pada komputer. Namun, setelah belajar beberapa teori seperti kecerdasan buatan, analisa algoritma, struktur data, matematika diskrit, kalkulus, dan sebagainya. Saya mendapatkan sedikit pencerahan. Graf dapat digunakan untuk menyelesaikan berbagai permasalahan seperti pencarian jalur terpendek, relasi hubungan sesuatu, representasi pengetahuan pada AI, dsb. Saya kemudian tertarik untuk mengetahui cara melakukan komputasi graf.

Tutorial PyGTK #4: Mengenal 5 Macam Container untuk Membuat Layout

Tutorial PyGTK #4: Mengenal 5 Macam Container untuk Membuat Layout

Pada tutorial ini, kamu akan belajar macam-macam layout dan container di PyGTK. Ini penting untuk membuat tampilan aplikasi yang lebih rapi...

7 (Bahasa) Pemrograman yang Dapat Dilakukan Langsung di Ubuntu

7 (Bahasa) Pemrograman yang Dapat Dilakukan Langsung di Ubuntu

Apa saja bahasa pemrograman yang bisa kamu langsung pakai di Ubuntu?

Bagaimana Cara Orang Buta Belajar Pemrograman?

Bagaimana Cara Orang Buta Belajar Pemrograman?

Kali ini saya akan mencoba menerjemahkan sebuah artikel hasil wawancara dari FossBoss yang mungkin menginspirasi. Judul aslinya “Mengenal Ali Abdulghani, Seorang Programmer Tunanetra yang Bekerja di Bidang Open Source” (Meet Ali Abdulghani, a Blind Programmer Working in the field of Open Source). Hallo Ali, ceritakan kami tentang dirimu, pendidikanmu, dan kehidupanmuNama saya Ali Abdulghani, orang-orang memanggil saya Alimiracle, panggilan itu datang sejak saya mulai menggunakan Solaris.

Sejarah dan Asal Usul 'Hello World' yang Jarang diketahui Orang

Sejarah dan Asal Usul 'Hello World' yang Jarang diketahui Orang

Siapa yang tidak kenal dengan program Hello World? Sebuah program yang paling sederhana di dunia. Fungsi utama dari program ini adalah menampilkan pesan ‘hello world’ ke layar. Hampir di setiap bahasa pemrograman menggunakan program Hello World untuk perkenalan pertama… Namun, tidak banyak yang tahu, kapan program ini pertama kali dibuat? Siapa yang membuatnya dan Kenapa harus Hello World? Karena itu, mari kita bahas sejarah dan asal usulnya. Tulisan ini terinspirasi dari komentar teman-teman di instagram, terima kasih sebelumnya sudah menyarankan saya untuk membahas asal-usul program Hello World 😄.